;;; my-org-jira.el -- Extensions for org-jira -*- lexical-binding: t -*- ;; Copyright (C) 2023 Free Software Foundation. ;; Author: Yuchen Pei ;; Package-Requires: ((emacs "28.2")) ;; This file is part of dotted. ;; dotted is free software: you can redistribute it and/or modify it under ;; the terms of the GNU Affero General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; dotted 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 Affero General ;; Public License for more details. ;; You should have received a copy of the GNU Affero General Public ;; License along with dotted. If not, see . ;;; Commentary: ;; Extensions for org-jira. ;;; Code: (require 'org-jira) ;; Override `org-jira--render-issue' ;; include issue-id in the headline (defun my-org-jira--render-issue (Issue) "Render single ISSUE." ;; (org-jira-log "Rendering issue from issue list") ;; (org-jira-log (org-jira-sdk-dump Issue)) (with-slots (filename proj-key issue-id summary status priority headline id) Issue (let (p) (with-current-buffer (org-jira--get-project-buffer Issue) (org-jira-freeze-ui (org-jira-maybe-activate-mode) (org-jira--maybe-render-top-heading proj-key) (setq p (org-find-entry-with-id issue-id)) (save-restriction (if (and p (>= p (point-min)) (<= p (point-max))) (progn (goto-char p) (forward-thing 'whitespace) (org-jira-kill-line)) (goto-char (point-max)) (unless (looking-at "^") (insert "\n")) (insert "** ")) (org-jira-insert (concat (org-jira-get-org-keyword-from-status status) " " (org-jira-get-org-priority-cookie-from-issue priority) issue-id " " headline)) (save-excursion (unless (search-forward "\n" (point-max) 1) (insert "\n"))) (org-narrow-to-subtree) (save-excursion (org-back-to-heading t) (org-set-tags-to (replace-regexp-in-string "-" "_" issue-id))) (mapc (lambda (entry) (let ((val (slot-value Issue entry))) (when (or (and val (not (string= val ""))) (eq entry 'assignee)) ;; Always show assignee (org-jira-entry-put (point) (symbol-name entry) val)))) '(assignee filename reporter type type-id priority labels resolution status components created updated sprint)) (org-jira-entry-put (point) "ID" issue-id) (org-jira-entry-put (point) "CUSTOM_ID" issue-id) ;; Insert the duedate as a deadline if it exists (when org-jira-deadline-duedate-sync-p (let ((duedate (oref Issue duedate))) (when (> (length duedate) 0) (org-deadline nil duedate)))) (mapc (lambda (heading-entry) (ensure-on-issue-id-with-filename issue-id filename (let* ((entry-heading (concat (symbol-name heading-entry) (format ": [[%s][%s]]" (concat jiralib-url "/browse/" issue-id) issue-id)))) (setq p (org-find-exact-headline-in-buffer entry-heading)) (if (and p (>= p (point-min)) (<= p (point-max))) (progn (goto-char p) (org-narrow-to-subtree) (goto-char (point-min)) (forward-line 1) (delete-region (point) (point-max))) (if (org-goto-first-child) (org-insert-heading) (goto-char (point-max)) (org-insert-subheading t)) (org-jira-insert entry-heading "\n")) ;; Insert 2 spaces of indentation so Jira markup won't cause org-markup (org-jira-insert (replace-regexp-in-string "^" " " (format "%s" (slot-value Issue heading-entry))))))) '(description)) (when org-jira-download-comments (org-jira-update-comments-for-issue Issue) ;; FIXME: Re-enable when attachments are not erroring. ;;(org-jira-update-attachments-for-current-issue) ) ;; only sync worklog clocks when the user sets it to be so. (when org-jira-worklog-sync-p (org-jira-update-worklogs-for-issue issue-id filename)))))))) ;; Overload `org-jira-update-worklogs-from-org-clocks'. (defun my-org-jira-update-worklogs-from-org-clocks () "Update or add a worklog based on the org clocks." (interactive) (let* ((issue-id (org-jira-get-from-org 'issue 'key)) (filename (org-jira-filename)) ;; Fetch all workflogs for this issue (jira-worklogs-ht (org-jira-worklog-to-hashtable issue-id))) (org-jira-log (format "About to sync worklog for issue: %s in file: %s" issue-id filename)) (ensure-on-issue-id-with-filename issue-id filename (search-forward (format ":%s:" (or (org-clock-drawer-name) "LOGBOOK")) nil 1 1) (org-beginning-of-line) ;; (org-cycle 1) (while (search-forward "CLOCK: " nil 1 1) (let ((org-time (buffer-substring-no-properties (point) (point-at-eol)))) (forward-line) ;; See where the stuff ends (what point) (let (next-clock-point) (save-excursion (search-forward-regexp "\\(CLOCK\\|:END\\):" nil 1 1) (setq next-clock-point (point))) (let ((clock-content (buffer-substring-no-properties (point) next-clock-point))) ;; Update via jiralib call (let* ((worklog (org-jira-org-clock-to-jira-worklog org-time clock-content)) (comment-text (cdr (assoc 'comment worklog))) (comment-text (if (string= (org-trim comment-text) "") nil comment-text))) (unless (cdr (assoc 'worklog-id worklog)) (jiralib-add-worklog issue-id (cdr (assoc 'started worklog)) (cdr (assoc 'time-spent-seconds worklog)) comment-text nil) ; no callback - synchronous ) ))))) (org-jira-log (format "Updating worklog from org-jira-update-worklogs-from-org-clocks call")) (org-jira-update-worklogs-for-issue issue-id filename) ))) (defun my-org-jira-comment-url (issue-id comment-id) (format "%s/browse/%s?focusedCommentId=%s&page=com.atlassian.jira.plugin.system.issuetabpanels%%3Acomment-tabpanel#comment-%s" jiralib-url issue-id comment-id comment-id)) (defun my-org-jira-comment-url-at-point () (my-org-jira-comment-url (org-entry-get (save-excursion (outline-up-heading 1) (point)) "ID") (org-entry-get (point) "ID"))) (defun my-org-jira-kill-comment-url-at-point () (interactive) (kill-new (my-org-jira-comment-url-at-point))) (provide 'my-org-jira) ;;; my-org-jira.el ends here