Skip to content

Instantly share code, notes, and snippets.

@jstewart
Created August 8, 2010 22:21
Show Gist options
  • Save jstewart/514629 to your computer and use it in GitHub Desktop.
Save jstewart/514629 to your computer and use it in GitHub Desktop.
;;; jira.el --- Connect to JIRA issue tracking software
;; Copyright (C) 2009 Brian Zwahr
;; original Copyright (C) 2007 Dave Benjamin
;; Authors:
;; Brian Zwahr <[email protected]>
;; Dave Benjamin <[email protected]>
;; Version: 0.3.3
;; Last modified: October 12, 2009
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; **********
;;; Commentary
;;; **********
;; This file provides jira-mode, an emacs major mode for connecting to and
;; using a Jira server. (http://www.atlassian.com/software/jira/). This
;; jira-mode is far from complete (more below) but is mostly usable as is
;; for the features that are present.
;; Note that some functions/processes can be a bit slow. I believe this has
;; something to do with XMLRPC.
;; Also, XMLRPC access to jira is incomplete. Certain Jira features cannot be
;; used via XMLRPC such as (but not limited to):
;; - Changing ticket status
;; - Closing/resolving tickets
;; - Watching a ticket
;; All of the XML-RPC API is wrapped, though not all of the API is exposed
;; via interactive functions. For API details, see:
;; http://confluence.atlassian.com/pages/viewpage.action?pageId=1035
;; http://www.atlassian.com/software/jira/docs/api/rpc-jira-plugin/latest/com/atlassian/jira/rpc/xmlrpc/XmlRpcService.html
;;; *************
;;; Configuration
;;; *************
;; 1.) Load the file jira.el, either manuall or place (require 'jira) in your .emacs with jira.el in the load path.
;; 2.) Customize the variable jira-url to point to the XML-RPC url of the Jira
;; installation to be accessed.
;; 3.) The faces can be customized for different look/feel/highlighting.
;;; *****
;;; Usage
;;; *****
;; M-x jira-mode will load the major mode into a new buffer named *Jira*.
;; You will be asked to login; use the username/password for the Jira server.
;; A few internal lists should be populated automatically containing a list
;; of projects, issue types, etc.
;; The following commands/keyboard shorcuts can be used:
;; li - jira-list-issues
;; lp - jira-list-projects
;; lf - jira-list-filters
;; si - jira-search-issues
;; sp - jira-search-project-issues
;; i - jira-show-issue
;; c - jira-create-ticket
;; o - jira-comment-ticket
;; r - jira-refresh-ticket
;; a - jira-assign-ticket
;; n - jira-next-comment
;; p - jira-previous-comment
;; jl - jira-login
;; jL - jira-logout
;; Q - jira-mode-quit
;; When viewing an issues, pressing o, r, etc. acts upon that issue.
;; For instance, while viewing an issue, pressing o will ask for a comment.
;; That comment will be posted to the issue currently being viewed.
;; Some prompts have tab completions in the minibuffer/echo area. Try it out to
;; see which prompts do and which do not.
;;; Code:
(require 'cl)
(require 'xml-rpc)
;; **************************
;; Jira Mode - by Brian Zwahr
;; **************************
(defgroup jira nil
"Jira customization group."
:group 'applications)
(defgroup jira-faces nil
"Faces for displaying Jira information."
:group 'jira)
(defcustom jira-url ""
"User customizable URL to Jira server."
:group 'jira
:type 'string
:initialize 'custom-initialize-set)
(defface jira-issue-info-face
'((t (:foreground "black" :background "yellow4")))
"Base face for issue information."
:group 'jira-faces)
(defface jira-issue-info-header-face
'((t (:bold t :inherit 'jira-issue-info-face)))
"Base face for issue headers."
:group 'jira-faces)
(defface jira-issue-summary-face
'((t (:bold t)))
"Base face for issue summary."
:group 'jira-faces)
(defface jira-comment-face
'((t (:background "gray23")))
"Base face for comments."
:group 'jira-faces)
(defface jira-comment-header-face
'((t (:bold t)))
"Base face for comment headers."
:group 'jira-faces)
(defface jira-link-issue-face
'((t (:underline t)))
"Face for linked issues."
:group 'jira-faces)
(defface jira-link-project-face
'((t (:underline t)))
"Face for linked projects"
:group 'jira-faces)
(defface jira-link-filter-face
'((t (:underline t)))
"Face for linked filters"
:group 'jira-faces)
(defvar jira-mode-hook nil)
(defvar jira-mode-map nil)
(if jira-mode-map
nil
(progn
(setq jira-mode-map (make-sparse-keymap))
(define-key jira-mode-map "li" 'jira-list-issues)
(define-key jira-mode-map "lp" 'jira-list-projects)
(define-key jira-mode-map "lf" 'jira-list-filters)
(define-key jira-mode-map "si" 'jira-search-issues)
(define-key jira-mode-map "sp" 'jira-search-project-issues)
(define-key jira-mode-map "i" 'jira-show-issue)
(define-key jira-mode-map "c" 'jira-create-ticket)
(define-key jira-mode-map "o" 'jira-comment-ticket)
(define-key jira-mode-map "r" 'jira-refresh-ticket)
(define-key jira-mode-map "a" 'jira-assign-ticket)
(define-key jira-mode-map "n" 'jira-next-comment)
(define-key jira-mode-map "p" 'jira-previous-comment)
(define-key jira-mode-map "jl" 'jira-login)
(define-key jira-mode-map "jL" 'jira-logout)
(define-key jira-mode-map "Q" 'jira-mode-quit)
(define-key jira-mode-map [return] 'jira-return)))
(defun jira-mode ()
"A mode for working with the Jira ticketing system. XMLRPC is used via xmlrpc.el. Things run a bit slow, though sometimes they seems to run faster when doing multiple things at once to the same ticket: i.e. retrieve a ticket, its slow, comment the tickets, its faster, refresh the ticket its faster, wait a while then refresh and its slow again.
\\{jira-mode-map}"
(interactive)
(if (or (equal jira-url nil)
(equal jira-url ""))
(message "jira-url not set! Please use 'M-x customize-variable RET jira-url RET'!")
(progn
(switch-to-buffer "*Jira*")
(kill-all-local-variables)
(setq major-mode 'jira-mode)
(setq mode-name "Jira")
(use-local-map jira-mode-map)
(run-hooks 'jira-mode-hook)
(jira-store-projects)
(jira-store-priorities)
(jira-store-statuses)
(jira-store-types)
(insert "Welcome to jira-mode!")
(message "jira mode loaded!"))))
(defvar jira-current-issue nil
"This holds the currently selected issue.")
(defvar jira-projects-list nil
"This holds a list of projects and their details.")
(defvar jira-types nil
"This holds a list of issues types.")
(defvar jira-statuses nil
"This holds a list of statuses.")
(defvar jira-priorities nil
"This holds a list of priorities.")
(defvar jira-user-fullnames nil
"This holds a list of user fullnames.")
(defun jira-mode-quit ()
(interactive)
(jira-logout)
(kill-buffer "*Jira*"))
(defun jira-create-ticket (project type summary description)
(interactive (list (read-string "Project: ")
(read-string "Type: ")
(read-string "Summary: ")
(read-string "Description: ")))
(if (or (equal project "")
(equal type "")
(equal summary "")
(equal description ""))
(message "Must provide all information!")
(progn
(setq ticket-alist (list (cons "project" project)
(cons "type" type)
(cons "summary" summary)
(cons "description" description)))
(jira-create-issue ticket-alist))))
(defun jira-refresh-ticket ()
(interactive)
(jira-show-issue jira-current-issue))
(defun jira-comment-ticket (comment)
(interactive (list (read-string "Comment: ")))
(if (equal comment "")
(message "Must provide comment!")
(progn
(jira-add-comment jira-current-issue comment)
(jira-refresh-ticket))))
(defun jira-assign-ticket (assignee)
(interactive (list (read-string "Assignee: ")))
(if (equal assignee "")
(message "Must provide assignee!")
(progn
(setq ticket-alist (list (cons "assignee" (vector assignee))))
(jira-update-issue jira-current-issue ticket-alist)
(jira-refresh-ticket))))
(defun jira-update-ticket-summary (summary)
(interactive (list (read-string "Summary: ")))
(if (equal summary "")
(message "Must provide summary!")
(progn
(setq ticket-alist (list (cons "summary" (vector summary))))
(jira-update-issue jira-current-issue ticket-alist)
(jira-refresh-ticket))))
(defun jira-start-ticket ()
(interactive)
(setq ticket-alist (list (cons "status" (vector "3"))))
(jira-update-issue jira-current-issue ticket-alist))
(defun jira-store-projects ()
(setf jira-projects-list (jira-get-projects)))
(defun jira-store-types ()
(setf jira-types (jira-get-issue-types)))
(defun jira-store-statuses ()
(setf jira-statuses (jira-get-statuses)))
(defun jira-store-priorities ()
(setf jira-priorities (jira-get-priorities)))
(defun jira-get-project-name (key)
(let ((projects jira-projects-list)
(name nil))
(dolist (project projects)
(if (equal (cdr (assoc "key" project)) key)
(setf name (cdr (assoc "name" project)))))
name))
(defun jira-get-type-name (id)
(let ((types jira-types)
(name nil))
(dolist (type types)
(if (equal (cdr (assoc "id" type)) id)
(setf name (cdr (assoc "name" type)))))
name))
(defun jira-get-status-name (id)
(let ((statuses jira-statuses)
(name nil))
(dolist (status statuses)
(if (equal (cdr (assoc "id" status)) id)
(setf name (cdr (assoc "name" status)))))
name))
(defun jira-get-priority-name (id)
(let ((priorities jira-priorities)
(name nil))
(dolist (priority priorities)
(if (equal (cdr (assoc "id" priority)) id)
(setf name (cdr (assoc "name" priority)))))
(message name)))
(defun jira-get-user-fullname (username)
(if (assoc username jira-user-fullnames)
(cdr (assoc username jira-user-fullnames))
(progn
(let ((user (jira-get-user username)))
(setf jira-user-fullnames (append jira-user-fullnames (list (cons username (cdr (assoc "fullname" user))))))
(cdr (assoc "fullname" user))))))
(defun jira-next-comment ()
(interactive)
(let ((p (point)))
(if (search-forward "Comment #" nil t)
(progn
(if (equal p (- (point) 9))
(search-forward "Comment #" nil t))
(recenter 0)
(beginning-of-line)))))
(defun jira-previous-comment ()
(interactive)
(if (search-backward "Comment #" nil t)
(progn
(recenter 0)
(beginning-of-line))
(goto-char 0)))
(defun jira-return ()
(interactive)
(if (equal (face-at-point) 'jira-link-issue-face)
(jira-show-issue (thing-at-point 'sexp)))
(if (equal (face-at-point) 'jira-link-project-face)
(jira-search-project-issues (thing-at-point 'sexp) "" 20))
(if (equal (face-at-point) 'jira-link-filter-face)
(jira-list-issues (thing-at-point 'sexp))))
(defun point-on-issue-p ()
(save-excursion
(search-backward " ")))
(defun delete-eob-whitespace ()
(end-of-buffer)
(delete-horizontal-space)
(delete-char -1)
(beginning-of-buffer))
;; ***********************************
;; original functions by Dave Benjamin
;; modifications by Brian Zwahr noted
;; ***********************************
(defvar jira-token nil
"JIRA token used for authentication")
(defun jira-login (username password)
"Logs the user into JIRA."
(interactive (list (read-string "Username: ")
(read-passwd "Password: ")))
(setq jira-token (jira-call-noauth 'jira1.login username password)))
(defun jira-logout ()
"Logs the user out of JIRA"
(interactive)
(jira-call 'jira1.logout)
(setq jira-token nil))
(defun jira-list-projects ()
"Displays a list of all available JIRA projects"
(interactive)
(let ((projects (jira-get-projects)))
(jira-with-jira-buffer
(insert (number-to-string (length projects)) " JIRA projects found:\n\n")
(dolist (project projects)
(insert (format "%-12s" " "))
(beginning-of-line)
(add-text-properties
(point)
(save-excursion
(insert
(cdr (assoc "key" project)))
(point))
'(face jira-link-project-face))
(beginning-of-line)
(forward-char 12)
(insert (format "%s\n"
(cdr (assoc "name" project)))))))
(delete-eob-whitespace))
(defun jira-list-filters ()
"Displays a list of all saved JIRA filters"
(interactive)
(let ((filters (jira-get-saved-filters)))
(jira-with-jira-buffer
(insert (number-to-string (length filters)) " JIRA filters found:\n\n")
(dolist (filter filters)
(insert (format "%-8s" " "))
(beginning-of-line)
(add-text-properties
(point)
(save-excursion
(insert (cdr (assoc "id" filter)))
(point))
'(face jira-link-filter-face))
(beginning-of-line)
(forward-char 8)
(insert (format " %s\n"
(cdr (assoc "name" filter)))))))
(delete-eob-whitespace))
(defun jira-list-issues (filter-id)
"Displays a list of issues matching a filter"
(interactive
(list (let ((filter-alist (jira-get-filter-alist)))
(cdr (assoc (completing-read "Filter: " filter-alist nil t)
filter-alist)))))
(when filter-id
(let ((filter (jira-get-filter filter-id))
(issues (jira-get-issues-from-filter filter-id)))
(jira-with-jira-buffer
(insert "Filter:\n" (cdr (assoc "name" filter))
" (" (cdr (assoc "id" filter)) ")\n\n")
(when (cdr (assoc "description" filter))
(insert "Description:\n")
(let ((start (point)))
(insert (cdr (assoc "description" filter)) "\n\n")
(fill-region start (point))))
(jira-display-issues issues)))))
(defun jira-search-issues (text)
"Displays a list of issues maching a fulltext search"
(interactive "sSearch: ")
(let ((issues (jira-get-issues-from-text-search text)))
(jira-with-jira-buffer
(insert "Search: " text "\n\n")
(jira-display-issues issues))))
(defun jira-search-project-issues (project text max-results)
"Displays a list of issues within a project matching a fulltext search"
(interactive
(let ((project-keys
(mapcar (lambda (project)
(cdr (assoc "key" project)))
(jira-get-projects))))
(list
(completing-read "Project Key: " project-keys nil t)
(read-string "Search: ")
(read-number "Max Results: " 20))))
(let ((issues (jira-get-issues-from-text-search-with-project
(list project) (if (equal text "") " " text) max-results)))
(jira-with-jira-buffer
(insert "Project Key: " project "\n"
"Search: " text "\n"
"Max Results: " (number-to-string max-results) "\n\n")
(jira-display-issues issues))))
; Modified by Brian Zwahr to store issue key and improve layout/readability.
(defun jira-show-issue (issue-key)
"Displays details about a particular issue."
(interactive "sIssue Key: ")
(let ((issue (jira-get-issue issue-key))
(comments (jira-get-comments issue-key)))
(setf jira-current-issue issue-key)
(jira-with-jira-buffer
(setq truncate-lines nil)
(let ((s "Project: "))
(put-text-property 0 (length s) 'face 'jira-issue-info-header-face s)
(insert s))
(let ((s (jira-get-project-name (cdr (assoc "project" issue)))))
(put-text-property 0 (length s) 'face 'jira-issue-info-face s)
(insert s "\n"))
(let ((s "Key: "))
(put-text-property 0 (length s) 'face 'jira-issue-info-header-face s)
(insert s))
(let ((s (cdr (assoc "key" issue))))
(put-text-property 0 (length s) 'face 'jira-issue-info-face s)
(insert s "\n"))
(let ((s "Type: "))
(put-text-property 0 (length s) 'face 'jira-issue-info-header-face s)
(insert s))
(let ((s (jira-get-type-name (cdr (assoc "type" issue)))))
(put-text-property 0 (length s) 'face 'jira-issue-info-face s)
(insert s "\n"))
(let ((s "Status: "))
(put-text-property 0 (length s) 'face 'jira-issue-info-header-face s)
(insert s))
(let ((s (jira-get-status-name (cdr (assoc "status" issue)))))
(put-text-property 0 (length s) 'face 'jira-issue-info-face s)
(insert s "\n"))
(let ((s "Priority: "))
(put-text-property 0 (length s) 'face 'jira-issue-info-header-face s)
(insert s))
(let ((s (jira-get-priority-name (cdr (assoc "priority" issue)))))
(put-text-property 0 (length s) 'face 'jira-issue-info-face s)
(insert s "\n"))
(let ((s "Assignee: "))
(put-text-property 0 (length s) 'face 'jira-issue-info-header-face s)
(insert s))
(let ((s (jira-get-user-fullname (cdr (assoc "assignee" issue)))))
(put-text-property 0 (length s) 'face 'jira-issue-info-face s)
(insert s "\n"))
(let ((s "Reporter: "))
(put-text-property 0 (length s) 'face 'jira-issue-info-header-face s)
(insert s))
(let ((s (jira-get-user-fullname (cdr (assoc "reporter" issue)))))
(put-text-property 0 (length s) 'face 'jira-issue-info-face s)
(insert s "\n"))
(let ((s "Created: "))
(put-text-property 0 (length s) 'face 'jira-issue-info-header-face s)
(insert s))
(let ((s (cdr (assoc "created" issue))))
(put-text-property 0 (length s) 'face 'jira-issue-info-face s)
(insert s "\n"))
(let ((s "Updated: "))
(put-text-property 0 (length s) 'face 'jira-issue-info-header-face s)
(insert s))
(let ((s (cdr (assoc "updated" issue))))
(put-text-property 0 (length s) 'face 'jira-issue-info-face s)
(insert s "\n"))
(let ((s "Watchers: "))
(put-text-property 0 (length s) 'face 'jira-issue-info-header-face s)
(insert s))
(let ((s "N/A"))
(put-text-property 0 (length s) 'face 'jira-issue-info-face s)
(insert s "\n\n"))
(let ((s "Component(s): "))
(put-text-property 0 (length s) 'face 'jira-issue-info-header-face s)
(insert s))
(let ((s (if (cdr (assoc "components" issue)) (cdr (assoc "components" issue)) "None")))
(put-text-property 0 (length s) 'face 'jira-issue-info-face s)
(insert s "\n\n"))
(let ((s "Fix Version(s): "))
(put-text-property 0 (length s) 'face 'jira-issue-info-header-face s)
(insert s))
(let ((s (if (cdr (assoc "fixVersions" issue)) (cdr (assoc "fixVersions" issue)) "None")))
(put-text-property 0 (length s) 'face 'jira-issue-info-face s)
(insert s "\n\n"))
(let ((s (cdr (assoc "summary" issue))))
(put-text-property 0 (length s) 'face 'jira-issue-summary-face s)
(insert s "\n\n"))
(insert (concatenate 'string (cdr (assoc "description" issue)) "\n\n"))
(when comments
(let ((count 1))
(dolist (comment comments)
(insert "Comment #" (int-to-string count) "\n")
(let ((s (concatenate 'string (jira-get-user-fullname (cdr (assoc "author" comment))) " - " (cdr (assoc "created" comment)))))
(put-text-property 0 (length s) 'face 'jira-comment-header-face s)
(insert s "\n"))
(let ((c (jira-strip-cr (cdr (assoc "body" comment)))))
(put-text-property 0 (length c) 'face 'jira-comment-face c)
(insert c "\n\n"))
(setf count (1+ count))))))))
(defun jira-send-region-as-comment (start end issue-key)
"Send the currently selected region as an issue comment"
(interactive "r\nsIssue Key: ")
(jira-add-comment issue-key (buffer-substring start end)))
(defun jira-get-filter (filter-id)
"Returns a filter given its filter ID."
(flet ((id-match (filter)
(equal filter-id (cdr (assoc "id" filter)))))
(find-if 'id-match (jira-get-saved-filters))))
(defun jira-get-filter-alist ()
"Returns an association list mapping filter names to IDs"
(mapcar (lambda (filter)
(cons (cdr (assoc "name" filter))
(cdr (assoc "id" filter))))
(jira-get-saved-filters)))
(defun jira-get-status-abbrevs ()
"Returns an association list of status IDs to abreviated names"
(flet ((pair (status)
(cons (cdr (assoc "id" status))
(let ((status-name (cdr (assoc "name" status))))
(substring (replace-regexp-in-string
" *" "" status-name)
0 (min 3 (length status-name)))))))
(mapcar 'pair (jira-get-statuses))))
(defun jira-display-issues (issues)
"Inserts a list of issues into the current buffer"
(let ((status-abbrevs (jira-get-status-abbrevs))
(last-status))
(insert (number-to-string (length issues))
" JIRA issues found:\n")
(dolist (issue issues)
(let ((status (cdr (assoc "status" issue)))
(priority (cdr (assoc "priority" issue))))
(when (not (equal last-status status))
(setq last-status status)
(insert "\n"))
(insert (format "%-16s" " "))
(beginning-of-line)
(add-text-properties
(point)
(save-excursion
(insert
(cdr (assoc "key" issue)))
(point))
'(face jira-link-issue-face))
(beginning-of-line)
(forward-char 16)
(insert (format "%-10s %s %5s %s\n"
(cdr (assoc "assignee" issue))
(cdr (assoc status status-abbrevs))
(if priority
(make-string (- 6 (string-to-number priority))
?*)
"")
(cdr (assoc "summary" issue)))))))
(delete-eob-whitespace))
(defun jira-add-comment (issue-key comment)
"Adds a comment to an issue"
(jira-call 'jira1.addComment issue-key comment))
(defun jira-create-issue (r-issue-struct)
"Creates an issue in JIRA from a Hashtable object."
(jira-call 'jira1.createIssue r-issue-struct))
(defun jira-get-comments (issue-key)
"Returns all comments associated with the issue"
(jira-call 'jira1.getComments issue-key))
(defun jira-get-components (project-key)
"Returns all components available in the specified project"
(jira-call 'jira1.getComponents project-key))
(defun jira-get-issue (issue-key)
"Gets an issue from a given issue key."
(jira-call 'jira1.getIssue issue-key))
(defun jira-get-issues-from-filter (filter-id)
"Executes a saved filter"
(jira-call 'jira1.getIssuesFromFilter filter-id))
(defun jira-get-issues-from-text-search (search-terms)
"Find issues using a free text search"
(jira-call 'jira1.getIssuesFromTextSearch search-terms))
(defun jira-get-issues-from-text-search-with-project
(project-keys search-terms max-num-results)
"Find issues using a free text search, limited to certain projects"
(jira-call 'jira1.getIssuesFromTextSearchWithProject
project-keys search-terms max-num-results))
(defun jira-get-issue-types ()
"Returns all visible issue types in the system"
(jira-call 'jira1.getIssueTypes))
(defun jira-get-priorities ()
"Returns all priorities in the system"
(jira-call 'jira1.getPriorities))
;; Modified by Brian Zwahr to use getProjectsNoSchemes instead of getProjects
(defun jira-get-projects ()
"Returns a list of projects available to the user"
(jira-call 'jira1.getProjectsNoSchemes))
(defun jira-get-resolutions ()
"Returns all resolutions in the system"
(jira-call 'jira1.getResolutions))
(defun jira-get-saved-filters ()
"Gets all saved filters available for the currently logged in user"
(jira-call 'jira1.getSavedFilters))
(defun jira-get-server-info ()
"Returns the Server information such as baseUrl, version, edition, buildDate, buildNumber."
(jira-call 'jira1.getServerInfo))
(defun jira-get-statuses ()
"Returns all statuses in the system"
(jira-call 'jira1.getStatuses))
(defun jira-get-sub-task-issue-types ()
"Returns all visible subtask issue types in the system"
(jira-call 'jira1.getSubTaskIssueTypes))
(defun jira-get-user (username)
"Returns a user's information given a username"
(jira-call 'jira1.getUser username))
(defun jira-get-versions (project-key)
"Returns all versions available in the specified project"
(jira-call 'jira1.getVersions project-key))
(defun jira-update-issue (issue-key field-values)
"Updates an issue in JIRA from a Hashtable object."
(jira-call 'jira1.updateIssue issue-key field-values))
(defun jira-ensure-token ()
"Makes sure that a JIRA token has been set, logging in if necessary."
(unless jira-token
(jira-login (read-string "Username: ")
(read-passwd "Password: "))))
(defun jira-call (method &rest params)
"Calls an XML-RPC method on the JIRA server (low-level)"
(jira-ensure-token)
(apply 'jira-call-noauth method jira-token params))
(defun jira-call-noauth (method &rest params)
"Calls an XML-RPC method on the JIRA server without authentication (low-level)"
(let ((url-version "Exp") ; hack due to status bug in xml-rpc.el
(server-url jira-url))
(apply 'xml-rpc-method-call server-url method params)))
(defun jira-strip-cr (string)
"Removes carriage returns from a string"
(when string (replace-regexp-in-string "\r" "" string)))
;; Modified by Brian Zwahr to a specific *Jira* buffer, not a temp buffer
(defmacro jira-with-jira-buffer (&rest body)
"Sends all output and buffer modifications to *Jira* buffer."
`(with-current-buffer "*Jira*"
(delete-region (point-min) (point-max))
(setq truncate-lines t)
,@body
(beginning-of-buffer)))
(provide 'jira)
;;; jira.el ends here
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment