From 577d9c8198c42595c8254ec0e4881bf7a1f8ade2 Mon Sep 17 00:00:00 2001 From: Raimon Grau Date: Sun, 22 Jul 2018 14:40:40 +0100 Subject: [PATCH 01/15] use oauth2 lib and remove deferred dependency Use oauth2 library instead of manual managing of tokens. http calls are done through it so we don't use deferred.el anymore. --- org-gcal.el | 440 +++++++++++++--------------------------------------- 1 file changed, 110 insertions(+), 330 deletions(-) diff --git a/org-gcal.el b/org-gcal.el index 67fb827..563fa2a 100644 --- a/org-gcal.el +++ b/org-gcal.el @@ -5,7 +5,7 @@ ;; Version: 0.3 ;; Maintainer: Raimon Grau ;; Copyright (C) :2014 myuhe all rights reserved. -;; Package-Requires: ((request-deferred "0.2.0") (alert "1.1") (emacs "24") (cl-lib "0.5") (org "8.2.4")) +;; Package-Requires: ((alert "1.1") (emacs "24") (cl-lib "0.5") (org "8.2.4")) ;; Keywords: convenience, ;; This program is free software; you can redistribute it and/or modify @@ -35,10 +35,10 @@ (require 'alert) (require 'json) -(require 'request-deferred) (require 'org-element) (require 'org-archive) (require 'cl-lib) +(require 'oauth2) ;; Customization ;;; Code: @@ -106,9 +106,6 @@ Predicate functions take an event, and if they return nil the :group 'org-gcal :type 'boolean) -(defvar org-gcal-token-plist nil - "Token plist.") - (defvar org-gcal-header-alist ()) (defconst org-gcal-auth-url "https://accounts.google.com/o/oauth2/auth" @@ -122,125 +119,86 @@ Predicate functions take an event, and if they return nil the (defconst org-gcal-events-url "https://www.googleapis.com/calendar/v3/calendars/%s/events") +(defun org-gcal-auth () + "Authorize." + (oauth2-auth-and-store org-gcal-auth-url + org-gcal-token-url + org-gcal-resource-url + org-gcal-client-id + org-gcal-client-secret + "urn:ietf:wg:oauth:2.0:oob")) + +(defun rgc-cb (b cal skip-export) + (interactive) + (with-current-buffer b + ;; (search-forward "\n\n") + (let ((json-object-type 'plist)) + (org-gcal--sync cal (json-read) skip-export)))) + ;;;###autoload (defun org-gcal-sync (&optional a-token skip-export silent) "Import events from calendars. Using A-TOKEN and export the ones to the calendar if unless SKIP-EXPORT. Set SILENT to non-nil to inhibit notifications." (interactive) - (org-gcal--ensure-token) + ;; (org-gcal--ensure-token) (when org-gcal-auto-archive (dolist (i org-gcal-file-alist) (with-current-buffer (find-file-noselect (cdr i)) (org-gcal--archive-old-event)))) - (cl-loop for x in org-gcal-file-alist - do - (let ((x x) - (a-token (if a-token - a-token - (org-gcal--get-access-token))) - - (skip-export skip-export) - (silent silent)) - (deferred:$ - (request-deferred - (format org-gcal-events-url (car x)) - :type "GET" - :params `(("access_token" . ,a-token) - ("key" . ,org-gcal-client-secret) - ("singleEvents" . "True") - ("orderBy" . "startTime") - ("timeMin" . ,(org-gcal--subtract-time)) - ("timeMax" . ,(org-gcal--add-time)) - ("grant_type" . "authorization_code")) - :parser 'org-gcal--json-read - :error - (cl-function (lambda (&key error-thrown &allow-other-keys) - (message "Got error: %S" error-thrown)))) - (deferred:nextc it - (lambda (response) - (let - ((temp (request-response-data response)) - (status (request-response-status-code response)) - (error-msg (request-response-error-thrown response))) - (cond - ;; If there is no network connectivity, the response will - ;; not include a status code. - ((eq status nil) - (org-gcal--notify - "Got Error" - "Could not contact remote service. Please check your network connectivity.")) - ;; Receiving a 403 response could mean that the calendar - ;; API has not been enabled. When the user goes and - ;; enables it, a new token will need to be generated. This - ;; takes care of that step. - ((eq 401 (or (plist-get (plist-get (request-response-data response) :error) :code) - status)) - (org-gcal--notify - "Received HTTP 401" - "OAuth token expired. Now trying to refresh-token") - (deferred:next - (lambda() - (org-gcal-refresh-token 'org-gcal-sync skip-export)))) - ((eq 403 status) - (org-gcal--notify "Received HTTP 403" - "Ensure you enabled the Calendar API through the Developers Console, then try again.") - (deferred:nextc it - (lambda() - (org-gcal-refresh-token 'org-gcal-sync skip-export)))) - ;; We got some 2xx response, but for some reason no - ;; message body. - ((and (> 299 status) (eq temp nil)) - (org-gcal--notify - (concat "Received HTTP" (number-to-string status)) - "Error occured, but no message body.")) - ((not (eq error-msg nil)) - ;; Generic error-handler meant to provide useful - ;; information about failure cases not otherwise - ;; explicitly specified. - (org-gcal--notify - (concat "Status code: " (number-to-string status)) - (pp-to-string error-msg))) - ;; Fetch was successful. - (t - (with-current-buffer (find-file-noselect (cdr x)) - (unless skip-export - (save-excursion - (cl-loop with buf = (find-file-noselect org-gcal-token-file) - for local-event in (org-gcal--parse-id (cdr x)) - for pos in (org-gcal--headline-list (cdr x)) - when (or - (eq (car local-event) nil) - (not (string= (cdr local-event) - (cdr (assoc (caar local-event) - (with-current-buffer buf - (plist-get (read (buffer-string)) (intern (concat ":" (car x)))))))))) - do - (goto-char pos) - (org-gcal-post-at-point t) - finally - (kill-buffer buf)) - (sit-for 2) - (org-gcal-sync nil t t))) - (erase-buffer) - (let ((items (org-gcal--filter (plist-get (request-response-data response) :items )))) - (if (assoc (car x) org-gcal-header-alist) - (insert (cdr (assoc (car x) org-gcal-header-alist)))) - (insert - (mapconcat 'identity - (mapcar (lambda (lst) - (org-gcal--cons-list lst)) - items) "")) - (let ((plst (with-temp-buffer (insert-file-contents org-gcal-token-file) - (read (buffer-string))))) - (with-temp-file org-gcal-token-file (pp (plist-put plst (intern (concat ":" (car x))) (mapcar (lambda (lst) - (cons (plist-get lst :id) (org-gcal--cons-list lst))) - items)) (current-buffer))))) - (org-set-startup-visibility) - (save-buffer)) - (unless silent - (org-gcal--notify "Completed event fetching ." (concat "Fetched data overwrote\n" (cdr x))))))))))))) + (mapc (lambda (cal) + (let ((b (oauth2-url-retrieve-synchronously + (org-gcal-auth) + (format "%s?%s" + (format org-gcal-events-url (first cal)) + (format "singleEvents=True&orderBy=startTime&timeMin=%s&timeMax=%s" + (org-gcal--subtract-time) + (org-gcal--add-time)))))) + (rgc-cb b cal skip-export))) + org-gcal-file-alist)) + +(defun org-gcal--sync (x data &optional skip-export) + "An X. Also data." + (interactive) + (with-current-buffer (find-file-noselect (cdr x)) + (unless skip-export + (save-excursion + (cl-loop with buf = (find-file-noselect org-gcal-token-file) + for local-event in (org-gcal--parse-id (cdr x)) + for pos in (org-gcal--headline-list (cdr x)) + when (or + (eq (car local-event) nil) + (not (string= (cdr local-event) + (cdr (assoc (caar local-event) + (with-current-buffer buf + (plist-get (read (buffer-string)) (intern (concat ":" (car x)))))))))) + do + (goto-char pos) + (org-gcal-post-at-point t) + finally + (kill-buffer buf)) + (sit-for 2) + (org-gcal-sync nil t t))) + (erase-buffer) + (let ((items (org-gcal--filter (plist-get data :items )))) + (if (assoc (car x) org-gcal-header-alist) + (insert (cdr (assoc (car x) org-gcal-header-alist)))) + (insert + (mapconcat 'identity + (mapcar (lambda (lst) + (org-gcal--cons-list lst)) + items) "")) + (let ((plst (with-temp-buffer (insert-file-contents org-gcal-token-file) + (read (buffer-string))))) + (with-temp-file org-gcal-token-file + (pp (plist-put plst + (intern (concat ":" (car x))) + (mapcar (lambda (lst) + (cons (plist-get lst :id) (org-gcal--cons-list lst))) + items)) (current-buffer))))) + (org-set-startup-visibility) + (save-buffer))) ;;;###autoload (defun org-gcal-fetch () @@ -291,7 +249,6 @@ filter returns NIL, discard the item." If SKIP-IMPORT is not nil, do not import events from the current calendar." (interactive) - (org-gcal--ensure-token) (save-excursion (end-of-line) (org-back-to-heading) @@ -346,70 +303,6 @@ current calendar." (y-or-n-p (format "Do you really want to delete event?\n\n%s\n\n" smry))) (org-gcal--delete-event id))))) -(defun org-gcal-request-authorization () - "Request OAuth authorization at AUTH-URL by launching `browse-url'. -CLIENT-ID is the client id provided by the provider. -It returns the code provided by the service." - (browse-url (concat org-gcal-auth-url - "?client_id=" (url-hexify-string org-gcal-client-id) - "&response_type=code" - "&redirect_uri=" (url-hexify-string "urn:ietf:wg:oauth:2.0:oob") - "&scope=" (url-hexify-string org-gcal-resource-url))) - (read-string "Enter the code your browser displayed: ")) - -(defun org-gcal-request-token () - "Refresh OAuth access at TOKEN-URL." - (request - org-gcal-token-url - :type "POST" - :data `(("client_id" . ,org-gcal-client-id) - ("client_secret" . ,org-gcal-client-secret) - ("code" . ,(org-gcal-request-authorization)) - ("redirect_uri" . "urn:ietf:wg:oauth:2.0:oob") - ("grant_type" . "authorization_code")) - :parser 'org-gcal--json-read - :success (cl-function - (lambda (&key data &allow-other-keys) - (when data - (setq org-gcal-token-plist data) - (org-gcal--save-sexp data org-gcal-token-file)))) - :error - (cl-function (lambda (&key error-thrown &allow-other-keys) - (message "Got error: %S" error-thrown))))) - -(defun org-gcal-refresh-token (&optional fun skip-export start end smry loc desc id) - "Refresh OAuth access and call FUN after that. -Pass SKIP-EXPORT, START, END, SMRY, LOC, DESC. and ID to FUN if -needed." - (deferred:$ - (request-deferred - org-gcal-token-url - :type "POST" - :data `(("client_id" . ,org-gcal-client-id) - ("client_secret" . ,org-gcal-client-secret) - ("refresh_token" . ,(org-gcal--get-refresh-token)) - ("grant_type" . "refresh_token")) - :parser 'org-gcal--json-read - :error - (cl-function (lambda (&key error-thrown &allow-other-keys) - (message "Got error: %S" error-thrown)))) - (deferred:nextc it - (lambda (response) - (let ((temp (request-response-data response))) - (plist-put org-gcal-token-plist - :access_token - (plist-get temp :access_token)) - (org-gcal--save-sexp org-gcal-token-plist org-gcal-token-file) - org-gcal-token-plist))) - (deferred:nextc it - (lambda (token) - (cond ((eq fun 'org-gcal-sync) - (org-gcal-sync (plist-get token :access_token) skip-export)) - ((eq fun 'org-gcal--post-event) - (org-gcal--post-event start end smry loc desc id (plist-get token :access_token))) - ((eq fun 'org-gcal--delete-event) - (org-gcal--delete-event id (plist-get token :access_token)))))))) - ;; Internal (defun org-gcal--archive-old-event () (save-excursion @@ -436,56 +329,6 @@ needed." (org-archive-subtree))))) (save-buffer))) -(defun org-gcal--save-sexp (data file) - (if (file-directory-p org-gcal-dir) - (if (file-exists-p file) - (if (plist-get (read (buffer-string)) :token ) - (with-temp-file file - (pp (plist-put (read (buffer-string)) :token data) (current-buffer))) - (with-temp-file file - (pp `(:token ,data :elem nil) (current-buffer)))) - (progn - (find-file-noselect file) - (with-temp-file file - (pp `(:token ,data :elem nil) (current-buffer))))) - (progn - (make-directory org-gcal-dir) - (org-gcal--save-sexp data file)))) - -(defun org-gcal--json-read () - (let ((json-object-type 'plist)) - (goto-char (point-min)) - (re-search-forward "^{" nil t) - (delete-region (point-min) (1- (point))) - (goto-char (point-min)) - (json-read-from-string - (decode-coding-string - (buffer-substring-no-properties (point-min) (point-max)) 'utf-8)))) - -(defun org-gcal--get-refresh-token () - (if org-gcal-token-plist - (plist-get org-gcal-token-plist :refresh_token) - (progn - (if (file-exists-p org-gcal-token-file) - (progn - (with-temp-buffer (insert-file-contents org-gcal-token-file) - (plist-get (plist-get (read (buffer-string)) :token) :refresh_token))) - (org-gcal--notify - (concat org-gcal-token-file " is not exists" ) - (concat "Make" org-gcal-token-file)))))) - -(defun org-gcal--get-access-token () - (if org-gcal-token-plist - (plist-get org-gcal-token-plist :access_token) - (progn - (if (file-exists-p org-gcal-token-file) - (progn - (with-temp-buffer (insert-file-contents org-gcal-token-file) - (plist-get (plist-get (read (buffer-string)) :token) :access_token))) - (org-gcal--notify - (concat org-gcal-token-file " is not exists" ) - (concat "Make " org-gcal-token-file)))))) - (defun org-gcal--safe-substring (string from &optional to) "Call the `substring' function safely. No errors will be returned for out of range values of FROM and @@ -638,12 +481,6 @@ TO. Instead an empty string is returned." (+ (if tz (car (org-gcal--time-zone seconds)) 0) seconds)))))) -(defun org-gcal--param-date (str) - (if (< 11 (length str)) "dateTime" "date")) - -(defun org-gcal--param-date-alt (str) - (if (< 11 (length str)) "date" "dateTime")) - (defun org-gcal--get-calendar-id-of-buffer () "Find calendar id of current buffer." (or (cl-loop for (id . file) in org-gcal-file-alist @@ -653,91 +490,46 @@ TO. Instead an empty string is returned." "please check/configure `org-gcal-file-alist'") (buffer-name)))) +(defun org-gcal-start-date (time) + (if (< 11 (length time)) + `("start" ("dateTime" . ,time) ("date" . nil)) + `("start" ("date" . ,time) ("dateTime" . nil)))) + +(defun org-gcal-end-date (time) + (if (< 11 (length time)) + `("end" ("dateTime" . nil) ("date" . nil)) + `("end" ("date" . ,(org-gcal--iso-next-day time)) ("dateTime" . nil)))) + (defun org-gcal--post-event (start end smry loc desc &optional id a-token skip-import skip-export) - (let ((stime (org-gcal--param-date start)) - (etime (org-gcal--param-date end)) - (stime-alt (org-gcal--param-date-alt start)) - (etime-alt (org-gcal--param-date-alt end)) - (a-token (if a-token - a-token - (org-gcal--get-access-token)))) - (request - (concat - (format org-gcal-events-url (org-gcal--get-calendar-id-of-buffer)) - (when id - (concat "/" id))) - :type (if id "PATCH" "POST") - :headers '(("Content-Type" . "application/json")) - :data (encode-coding-string - (json-encode `(("start" (,stime . ,start) (,stime-alt . nil)) - ("end" (,etime . ,(if (equal "date" etime) - (org-gcal--iso-next-day end) - end)) (,etime-alt . nil)) - ("summary" . ,smry) - ("location" . ,loc) - ("description" . ,desc))) - 'utf-8) - :params `(("access_token" . ,a-token) - ("key" . ,org-gcal-client-secret) - ("grant_type" . "authorization_code")) - - :parser 'org-gcal--json-read - :error (cl-function - (lambda (&key response &allow-other-keys) - (let ((status (request-response-status-code response)) - (error-msg (request-response-error-thrown response))) - (cond - ((eq status 401) - (progn - (org-gcal--notify - "Received HTTP 401" - "OAuth token expired. Now trying to refresh-token") - (org-gcal-refresh-token 'org-gcal--post-event skip-export start end smry loc desc id))) - (t - (org-gcal--notify - (concat "Status code: " (pp-to-string status)) - (pp-to-string error-msg))))))) - :success (cl-function - (lambda (&key data &allow-other-keys) - (progn - (org-gcal--notify "Event Posted" - (concat "Org-gcal post event\n " (plist-get data :summary))) - (unless skip-import (org-gcal-fetch)))))))) - -(defun org-gcal--delete-event (event-id &optional a-token) - (let ((a-token (if a-token - a-token - (org-gcal--get-access-token))) - (calendar-id (org-gcal--get-calendar-id-of-buffer))) - (request - (concat - (format org-gcal-events-url calendar-id) - (concat "/" event-id)) - :type "DELETE" - :headers '(("Content-Type" . "application/json")) - :params `(("access_token" . ,a-token) - ("key" . ,org-gcal-client-secret) - ("grant_type" . "authorization_code")) - :error (cl-function - (lambda (&key response &allow-other-keys) - (let ((status (request-response-status-code response)) - (error-msg (request-response-error-thrown response))) - (cond - ((eq status 401) - (progn - (org-gcal--notify - "Received HTTP 401" - "OAuth token expired. Now trying to refresh-token") - (org-gcal-refresh-token 'org-gcal--delete-event nil nil nil nil nil nil event-id))) - (t - (org-gcal--notify - (concat "Status code: " (pp-to-string status)) - (pp-to-string error-msg))))))) - :success (cl-function - (lambda (&key &allow-other-keys) - (progn - (org-gcal-fetch) - (org-gcal--notify "Event Deleted" "Org-gcal deleted event"))))))) + (let ((method (if id "PATCH" "POST")) + (id (or id "")) + b) + (setq b (oauth2-url-retrieve-synchronously + (org-gcal-auth) + (concat (format org-gcal-events-url (org-gcal--get-calendar-id-of-buffer)) "/" id) + method + (encode-coding-string + (json-encode `(,(org-gcal-start-date start) + ,(org-gcal-end-date end) + ("summary" . ,smry) + ("location" . ,loc) + ("description" . ,desc))) + 'utf-8) + '(("Content-Type" . "application/json")))) + (org-gcal-fetch))) + +(defun org-gcal--delete-event (event-id) + "EVENT-ID is nice." + (interactive) + (oauth2-url-retrieve-synchronously + (rgc-auth) + (format "%s/%s" + (format org-gcal-events-url + (org-gcal--get-calendar-id-of-buffer)) + event-id) + "DELETE") + (org-gcal-fetch)) + (defun org-gcal--capture-post () (dolist (i org-gcal-file-alist) @@ -747,17 +539,6 @@ TO. Instead an empty string is returned." (add-hook 'org-capture-before-finalize-hook 'org-gcal--capture-post) -(defun org-gcal--ensure-token () - (cond - (org-gcal-token-plist t) - ((and (file-exists-p org-gcal-token-file) - (ignore-errors - (setq org-gcal-token-plist - (with-temp-buffer - (insert-file-contents org-gcal-token-file) - (plist-get (read (current-buffer)) :token))))) t) - (t (org-gcal-request-token)))) - (defun org-gcal--timestamp-successor () "Search for the next timestamp object. Return value is a cons cell whose CAR is `timestamp' and CDR is @@ -789,7 +570,6 @@ beginning position." (plist-get plst :mon) (plist-get plst :year)))) - (provide 'org-gcal) ;;; org-gcal.el ends here From 902ba3be96c675a85f6f250032b18a6daa39f0ad Mon Sep 17 00:00:00 2001 From: Raimon Grau Date: Sun, 22 Jul 2018 15:56:52 +0100 Subject: [PATCH 02/15] remove unused a-token param --- org-gcal.el | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/org-gcal.el b/org-gcal.el index 563fa2a..ecd3f23 100644 --- a/org-gcal.el +++ b/org-gcal.el @@ -136,10 +136,10 @@ Predicate functions take an event, and if they return nil the (org-gcal--sync cal (json-read) skip-export)))) ;;;###autoload -(defun org-gcal-sync (&optional a-token skip-export silent) +(defun org-gcal-sync (&optional skip-export) "Import events from calendars. -Using A-TOKEN and export the ones to the calendar if unless -SKIP-EXPORT. Set SILENT to non-nil to inhibit notifications." +Export the ones to the calendar unless SKIP-EXPORT. Set SILENT +to non-nil to inhibit notifications." (interactive) ;; (org-gcal--ensure-token) (when org-gcal-auto-archive @@ -179,7 +179,7 @@ SKIP-EXPORT. Set SILENT to non-nil to inhibit notifications." finally (kill-buffer buf)) (sit-for 2) - (org-gcal-sync nil t t))) + (org-gcal-sync t t))) (erase-buffer) (let ((items (org-gcal--filter (plist-get data :items )))) (if (assoc (car x) org-gcal-header-alist) @@ -204,7 +204,7 @@ SKIP-EXPORT. Set SILENT to non-nil to inhibit notifications." (defun org-gcal-fetch () "Fetch event data from google calendar." (interactive) - (org-gcal-sync nil t)) + (org-gcal-sync t)) (defun org-gcal--filter (items) "Filter ITEMS on an AND of `org-gcal-fetch-event-filters' functions. From 8c3ac278af7a74efa7f905eaad33112296774d25 Mon Sep 17 00:00:00 2001 From: Raimon Grau Date: Sun, 22 Jul 2018 16:07:59 +0100 Subject: [PATCH 03/15] use asynchronous http requests --- org-gcal.el | 61 +++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/org-gcal.el b/org-gcal.el index ecd3f23..89799af 100644 --- a/org-gcal.el +++ b/org-gcal.el @@ -130,10 +130,9 @@ Predicate functions take an event, and if they return nil the (defun rgc-cb (b cal skip-export) (interactive) - (with-current-buffer b - ;; (search-forward "\n\n") - (let ((json-object-type 'plist)) - (org-gcal--sync cal (json-read) skip-export)))) + (search-forward "\n\n") + (let ((json-object-type 'plist)) + (org-gcal--sync cal (json-read) skip-export))) ;;;###autoload (defun org-gcal-sync (&optional skip-export) @@ -147,16 +146,16 @@ to non-nil to inhibit notifications." (with-current-buffer (find-file-noselect (cdr i)) (org-gcal--archive-old-event)))) - (mapc (lambda (cal) - (let ((b (oauth2-url-retrieve-synchronously - (org-gcal-auth) - (format "%s?%s" - (format org-gcal-events-url (first cal)) - (format "singleEvents=True&orderBy=startTime&timeMin=%s&timeMax=%s" - (org-gcal--subtract-time) - (org-gcal--add-time)))))) - (rgc-cb b cal skip-export))) - org-gcal-file-alist)) + (dolist (cal org-gcal-file-alist) + (let ((b (oauth2-url-retrieve + (org-gcal-auth) + (format "%s?%s" + (format org-gcal-events-url (first cal)) + (format "singleEvents=True&orderBy=startTime&timeMin=%s&timeMax=%s" + (org-gcal--subtract-time) + (org-gcal--add-time))) + 'rgc-cb + (list cal skip-export))))))) (defun org-gcal--sync (x data &optional skip-export) "An X. Also data." @@ -504,31 +503,33 @@ TO. Instead an empty string is returned." (let ((method (if id "PATCH" "POST")) (id (or id "")) b) - (setq b (oauth2-url-retrieve-synchronously - (org-gcal-auth) - (concat (format org-gcal-events-url (org-gcal--get-calendar-id-of-buffer)) "/" id) - method - (encode-coding-string - (json-encode `(,(org-gcal-start-date start) - ,(org-gcal-end-date end) - ("summary" . ,smry) - ("location" . ,loc) - ("description" . ,desc))) - 'utf-8) - '(("Content-Type" . "application/json")))) - (org-gcal-fetch))) + (oauth2-url-retrieve + (org-gcal-auth) + (concat (format org-gcal-events-url (org-gcal--get-calendar-id-of-buffer)) "/" id) + (lambda (status) (org-gcal-fetch)) + nil + method + (encode-coding-string + (json-encode `(,(org-gcal-start-date start) + ,(org-gcal-end-date end) + ("summary" . ,smry) + ("location" . ,loc) + ("description" . ,desc))) + 'utf-8) + '(("Content-Type" . "application/json"))))) (defun org-gcal--delete-event (event-id) "EVENT-ID is nice." (interactive) - (oauth2-url-retrieve-synchronously + (oauth2-url-retrieve (rgc-auth) (format "%s/%s" (format org-gcal-events-url (org-gcal--get-calendar-id-of-buffer)) event-id) - "DELETE") - (org-gcal-fetch)) + (lambda (status) (org-gcal-fetch)) + nil + "DELETE")) (defun org-gcal--capture-post () From df309324f5ece76b65205088ad23c2b46345cfd1 Mon Sep 17 00:00:00 2001 From: Raimon Grau Date: Sun, 22 Jul 2018 16:51:49 +0100 Subject: [PATCH 04/15] removed useless parameters --- org-gcal.el | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/org-gcal.el b/org-gcal.el index 89799af..3f13e73 100644 --- a/org-gcal.el +++ b/org-gcal.el @@ -178,7 +178,7 @@ to non-nil to inhibit notifications." finally (kill-buffer buf)) (sit-for 2) - (org-gcal-sync t t))) + (org-gcal-sync t))) (erase-buffer) (let ((items (org-gcal--filter (plist-get data :items )))) (if (assoc (car x) org-gcal-header-alist) @@ -243,7 +243,7 @@ filter returns NIL, discard the item." (lambda (hl) (org-element-property :end hl))))))))))) ;;;###autoload -(defun org-gcal-post-at-point (&optional skip-import) +(defun org-gcal-post-at-point () "Post entry at point to current calendar. If SKIP-IMPORT is not nil, do not import events from the current calendar." @@ -251,8 +251,7 @@ current calendar." (save-excursion (end-of-line) (org-back-to-heading) - (let* ((skip-import skip-import) - (elem (org-element-headline-parser (point-max) t)) + (let* ((elem (org-element-headline-parser (point-max) t)) (tobj (progn (re-search-forward "<[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]" (save-excursion (outline-next-heading) (point))) (goto-char (match-beginning 0)) @@ -285,7 +284,7 @@ current calendar." (buffer-substring-no-properties (plist-get (cadr elem) :contents-begin) (plist-get (cadr elem) :contents-end))))) ""))) - (org-gcal--post-event start end smry loc desc id nil skip-import)))) + (org-gcal--post-event start end smry loc desc id)))) ;;;###autoload (defun org-gcal-delete-at-point () @@ -499,10 +498,9 @@ TO. Instead an empty string is returned." `("end" ("dateTime" . nil) ("date" . nil)) `("end" ("date" . ,(org-gcal--iso-next-day time)) ("dateTime" . nil)))) -(defun org-gcal--post-event (start end smry loc desc &optional id a-token skip-import skip-export) +(defun org-gcal--post-event (start end smry loc desc &optional id) (let ((method (if id "PATCH" "POST")) - (id (or id "")) - b) + (id (or id ""))) (oauth2-url-retrieve (org-gcal-auth) (concat (format org-gcal-events-url (org-gcal--get-calendar-id-of-buffer)) "/" id) From 3db1dcfa07f05f058a6ef44c1ba2dcacd01ffbd7 Mon Sep 17 00:00:00 2001 From: Raimon Grau Date: Sun, 22 Jul 2018 19:47:33 +0100 Subject: [PATCH 05/15] fix end date param on POST --- org-gcal.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-gcal.el b/org-gcal.el index 3f13e73..1b324f8 100644 --- a/org-gcal.el +++ b/org-gcal.el @@ -495,7 +495,7 @@ TO. Instead an empty string is returned." (defun org-gcal-end-date (time) (if (< 11 (length time)) - `("end" ("dateTime" . nil) ("date" . nil)) + `("end" ("dateTime" . ,time) ("date" . nil)) `("end" ("date" . ,(org-gcal--iso-next-day time)) ("dateTime" . nil)))) (defun org-gcal--post-event (start end smry loc desc &optional id) From 5121427fd6040a0d89d21421cda1d8d4e79cfa9b Mon Sep 17 00:00:00 2001 From: Raimon Grau Date: Sun, 22 Jul 2018 21:37:04 +0100 Subject: [PATCH 06/15] add org-gcal--post-event-internal Entrypoint to creating an event in a google calendar without relying on current-file, location or anything --- org-gcal.el | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/org-gcal.el b/org-gcal.el index 1b324f8..a87e6da 100644 --- a/org-gcal.el +++ b/org-gcal.el @@ -501,20 +501,25 @@ TO. Instead an empty string is returned." (defun org-gcal--post-event (start end smry loc desc &optional id) (let ((method (if id "PATCH" "POST")) (id (or id ""))) - (oauth2-url-retrieve - (org-gcal-auth) - (concat (format org-gcal-events-url (org-gcal--get-calendar-id-of-buffer)) "/" id) - (lambda (status) (org-gcal-fetch)) - nil - method - (encode-coding-string - (json-encode `(,(org-gcal-start-date start) - ,(org-gcal-end-date end) - ("summary" . ,smry) - ("location" . ,loc) - ("description" . ,desc))) - 'utf-8) - '(("Content-Type" . "application/json"))))) + (org-gcal--post-event-internal + start end smry loc desc method + (concat (format org-gcal-events-url (org-gcal--get-calendar-id-of-buffer)) "/" id)))) + +(defun org-gcal--post-event-internal (start end smry loc desc method url) + (oauth2-url-retrieve + (org-gcal-auth) + url + (lambda (status) (org-gcal-fetch)) + nil + method + (encode-coding-string + (json-encode `(,(org-gcal-start-date start) + ,(org-gcal-end-date end) + ("summary" . ,smry) + ("location" . ,loc) + ("description" . ,desc))) + 'utf-8) + '(("Content-Type" . "application/json")))) (defun org-gcal--delete-event (event-id) "EVENT-ID is nice." From 0cceb1e5f0a67640e8da061fd56844682af9e9d4 Mon Sep 17 00:00:00 2001 From: Raimon Grau Date: Tue, 24 Jul 2018 10:08:55 +0100 Subject: [PATCH 07/15] Fix asynconicity with threads oauth automatic token-refreshing does not work with asynconous url-retrieve. That happens because dynamic bindings are not held through the request (except a few chosen ones `url-http'). This commit comes over the synchronous + refresh problem by using multiple threads in a synchronous way. --- org-gcal.el | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/org-gcal.el b/org-gcal.el index a87e6da..23e04fd 100644 --- a/org-gcal.el +++ b/org-gcal.el @@ -130,10 +130,15 @@ Predicate functions take an event, and if they return nil the (defun rgc-cb (b cal skip-export) (interactive) - (search-forward "\n\n") (let ((json-object-type 'plist)) (org-gcal--sync cal (json-read) skip-export))) +(defun rgc-cb-sync (b cal skip-export) + (interactive) + (with-current-buffer b + (let ((json-object-type 'plist)) + (org-gcal--sync cal (json-read) skip-export)))) + ;;;###autoload (defun org-gcal-sync (&optional skip-export) "Import events from calendars. @@ -147,15 +152,18 @@ to non-nil to inhibit notifications." (find-file-noselect (cdr i)) (org-gcal--archive-old-event)))) (dolist (cal org-gcal-file-alist) - (let ((b (oauth2-url-retrieve - (org-gcal-auth) - (format "%s?%s" - (format org-gcal-events-url (first cal)) - (format "singleEvents=True&orderBy=startTime&timeMin=%s&timeMax=%s" - (org-gcal--subtract-time) - (org-gcal--add-time))) - 'rgc-cb - (list cal skip-export))))))) + (make-thread (lambda () + (let ((b (oauth2-url-retrieve-synchronously + (org-gcal-auth) + (format "%s?%s" + (format org-gcal-events-url (first cal)) + (format "singleEvents=True&orderBy=startTime&timeMin=%s&timeMax=%s" + (org-gcal--subtract-time) + (org-gcal--add-time))) + ;; 'rgc-cb + ;; (list cal skip-export) + ))) + (rgc-cb-sync b cal skip-export)))))) (defun org-gcal--sync (x data &optional skip-export) "An X. Also data." From 4fa4092949b580a9c1a4da2f52dba7cf79afdf8f Mon Sep 17 00:00:00 2001 From: Raimon Grau Date: Tue, 24 Jul 2018 12:33:40 +0100 Subject: [PATCH 08/15] avoid multiple token fetch --- org-gcal.el | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/org-gcal.el b/org-gcal.el index eec9c4b..e8f8c79 100644 --- a/org-gcal.el +++ b/org-gcal.el @@ -157,25 +157,25 @@ control categories, archive locations, and other local variables." Export the ones to the calendar unless SKIP-EXPORT. Set SILENT to non-nil to inhibit notifications." (interactive) - ;; (org-gcal--ensure-token) (when org-gcal-auto-archive (dolist (i org-gcal-file-alist) (with-current-buffer (find-file-noselect (cdr i)) (org-gcal--archive-old-event)))) - (dolist (cal org-gcal-file-alist) - (make-thread (lambda () - (let ((b (oauth2-url-retrieve-synchronously - (org-gcal-auth) - (format "%s?%s" - (format org-gcal-events-url (first cal)) - (format "singleEvents=True&orderBy=startTime&timeMin=%s&timeMax=%s" - (org-gcal--subtract-time) - (org-gcal--add-time))) - ;; 'rgc-cb - ;; (list cal skip-export) - ))) - (rgc-cb-sync b cal skip-export)))))) + (let ((token (org-gcal-auth))) + (dolist (cal org-gcal-file-alist) + (make-thread (lambda () + (let ((b (oauth2-url-retrieve-synchronously + token + (format "%s?%s" + (format org-gcal-events-url (first cal)) + (format "singleEvents=True&orderBy=startTime&timeMin=%s&timeMax=%s" + (org-gcal--subtract-time) + (org-gcal--add-time))) + ;; 'rgc-cb + ;; (list cal skip-export) + ))) + (rgc-cb-sync b cal skip-export))))))) (defun org-gcal--sync (x data &optional skip-export) "An X. Also data." From 0e52bb0d0ff9b962ec79f1a835a17ad5d8b2a6b1 Mon Sep 17 00:00:00 2001 From: Raimon Grau Date: Tue, 24 Jul 2018 13:18:21 +0100 Subject: [PATCH 09/15] fix function names and params missmatch --- org-gcal.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/org-gcal.el b/org-gcal.el index e8f8c79..b070a8f 100644 --- a/org-gcal.el +++ b/org-gcal.el @@ -194,7 +194,7 @@ to non-nil to inhibit notifications." (plist-get (read (buffer-string)) (intern (concat ":" (car x)))))))))) do (goto-char pos) - (org-gcal-post-at-point t) + (org-gcal-post-at-point) finally (kill-buffer buf)) (sit-for 2) @@ -308,7 +308,7 @@ current calendar." (plist-get (cadr elem) :contents-begin) (plist-get (cadr elem) :contents-end))))) ""))) - (org-gcal--post-event start end smry loc desc id nil skip-import)))) + (org-gcal--post-event start end smry loc desc id)))) ;;;###autoload (defun org-gcal-delete-at-point () @@ -547,7 +547,7 @@ TO. Instead an empty string is returned." "EVENT-ID is nice." (interactive) (oauth2-url-retrieve - (rgc-auth) + (org-gcal-auth) (format "%s/%s" (format org-gcal-events-url (org-gcal--get-calendar-id-of-buffer)) From 9555a26e6e7c6a85011e714800741847048d43fd Mon Sep 17 00:00:00 2001 From: Raimon Grau Date: Wed, 25 Jul 2018 12:23:45 +0100 Subject: [PATCH 10/15] remove thread epxeriment --- org-gcal.el | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/org-gcal.el b/org-gcal.el index b070a8f..84485b3 100644 --- a/org-gcal.el +++ b/org-gcal.el @@ -164,18 +164,17 @@ to non-nil to inhibit notifications." (org-gcal--archive-old-event)))) (let ((token (org-gcal-auth))) (dolist (cal org-gcal-file-alist) - (make-thread (lambda () - (let ((b (oauth2-url-retrieve-synchronously - token - (format "%s?%s" - (format org-gcal-events-url (first cal)) - (format "singleEvents=True&orderBy=startTime&timeMin=%s&timeMax=%s" - (org-gcal--subtract-time) - (org-gcal--add-time))) - ;; 'rgc-cb - ;; (list cal skip-export) - ))) - (rgc-cb-sync b cal skip-export))))))) + (let ((b (oauth2-url-retrieve-synchronously + token + (format "%s?%s" + (format org-gcal-events-url (first cal)) + (format "singleEvents=True&orderBy=startTime&timeMin=%s&timeMax=%s" + (org-gcal--subtract-time) + (org-gcal--add-time))) + ;; 'rgc-cb + ;; (list cal skip-export) + ))) + (rgc-cb-sync b cal skip-export))))) (defun org-gcal--sync (x data &optional skip-export) "An X. Also data." From e09fe27aea9109191e858f8d4a6b38ff3263c460 Mon Sep 17 00:00:00 2001 From: Raimon Grau Date: Wed, 25 Jul 2018 17:22:27 +0100 Subject: [PATCH 11/15] remove call to non-existing function --- org-gcal.el | 1 - 1 file changed, 1 deletion(-) diff --git a/org-gcal.el b/org-gcal.el index 84485b3..7e15a58 100644 --- a/org-gcal.el +++ b/org-gcal.el @@ -313,7 +313,6 @@ current calendar." (defun org-gcal-delete-at-point () "Delete entry at point to current calendar." (interactive) - (org-gcal--ensure-token) (save-excursion (end-of-line) (org-back-to-heading) From 38c68fc55e1d634f3a72859801fb6d7457ac0899 Mon Sep 17 00:00:00 2001 From: Raimon Grau Date: Thu, 2 Aug 2018 13:31:47 +0100 Subject: [PATCH 12/15] reduce the number of times plstore asks for a password Lifting the call to `org-gcal-auth' was a bad idea because when the token expired, we were sending the expired token for each calendar we want to synchronize. Now the first one that gets the expired token asks for renewal, and next ones use the renewed token. --- org-gcal.el | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/org-gcal.el b/org-gcal.el index 7e15a58..942b601 100644 --- a/org-gcal.el +++ b/org-gcal.el @@ -162,19 +162,18 @@ to non-nil to inhibit notifications." (with-current-buffer (find-file-noselect (cdr i)) (org-gcal--archive-old-event)))) - (let ((token (org-gcal-auth))) - (dolist (cal org-gcal-file-alist) - (let ((b (oauth2-url-retrieve-synchronously - token - (format "%s?%s" - (format org-gcal-events-url (first cal)) - (format "singleEvents=True&orderBy=startTime&timeMin=%s&timeMax=%s" - (org-gcal--subtract-time) - (org-gcal--add-time))) - ;; 'rgc-cb - ;; (list cal skip-export) - ))) - (rgc-cb-sync b cal skip-export))))) + (dolist (cal org-gcal-file-alist) + (let ((b (oauth2-url-retrieve-synchronously + (org-gcal-auth) + (format "%s?%s" + (format org-gcal-events-url (first cal)) + (format "singleEvents=True&orderBy=startTime&timeMin=%s&timeMax=%s" + (org-gcal--subtract-time) + (org-gcal--add-time))) + ;; 'rgc-cb + ;; (list cal skip-export) + ))) + (rgc-cb-sync b cal skip-export)))) (defun org-gcal--sync (x data &optional skip-export) "An X. Also data." From dddfdcf91e5ff46d690d6616ea00159d93ab8466 Mon Sep 17 00:00:00 2001 From: Raimon Grau Date: Mon, 20 Aug 2018 21:06:05 +0100 Subject: [PATCH 13/15] update dpendencies. require oauth2.el --- org-gcal.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-gcal.el b/org-gcal.el index 942b601..be093ac 100644 --- a/org-gcal.el +++ b/org-gcal.el @@ -5,7 +5,7 @@ ;; Version: 0.3 ;; Maintainer: Raimon Grau ;; Copyright (C) :2014 myuhe all rights reserved. -;; Package-Requires: ((alert "1.1") (emacs "24") (cl-lib "0.5") (org "8.2.4")) +;; Package-Requires: ((alert "1.1") (oauth2 "0.11") (emacs "24") (cl-lib "0.5") (org "8.2.4")) ;; Keywords: convenience, ;; This program is free software; you can redistribute it and/or modify From c3ee02a1acecb2c07dcc2e071abd752a3fb67930 Mon Sep 17 00:00:00 2001 From: Raimon Grau Date: Mon, 20 Aug 2018 21:06:29 +0100 Subject: [PATCH 14/15] Fix docstring --- org-gcal.el | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/org-gcal.el b/org-gcal.el index be093ac..53d37ae 100644 --- a/org-gcal.el +++ b/org-gcal.el @@ -154,8 +154,7 @@ control categories, archive locations, and other local variables." ;;;###autoload (defun org-gcal-sync (&optional skip-export) "Import events from calendars. -Export the ones to the calendar unless SKIP-EXPORT. Set SILENT -to non-nil to inhibit notifications." +Export the ones to the calendar unless SKIP-EXPORT." (interactive) (when org-gcal-auto-archive (dolist (i org-gcal-file-alist) @@ -541,7 +540,7 @@ TO. Instead an empty string is returned." '(("Content-Type" . "application/json")))) (defun org-gcal--delete-event (event-id) - "EVENT-ID is nice." + "Delete event EVENT-ID." (interactive) (oauth2-url-retrieve (org-gcal-auth) From d2861b01dd839f2d1a6b22ee4b1981e09e88658d Mon Sep 17 00:00:00 2001 From: Raimon Grau Date: Mon, 27 Aug 2018 09:08:42 +0100 Subject: [PATCH 15/15] port #19 to oauth2 branch --- org-gcal.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-gcal.el b/org-gcal.el index 53d37ae..0794518 100644 --- a/org-gcal.el +++ b/org-gcal.el @@ -500,7 +500,7 @@ TO. Instead an empty string is returned." (defun org-gcal--get-calendar-id-of-buffer () "Find calendar id of current buffer." (or (cl-loop for (id . file) in org-gcal-file-alist - if (file-equal-p file (buffer-file-name)) + if (file-equal-p file (buffer-file-name (buffer-base-buffer))) return id) (user-error (concat "Buffer `%s' may not related to google calendar; " "please check/configure `org-gcal-file-alist'")