forked from sachac/org-toodledo
-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathorg-toodledo.el
3165 lines (2837 loc) · 119 KB
/
org-toodledo.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;; org-toodledo.el --- Toodledo integration for Emacs Org mode
;; Copyright (C) 2011-2012 Christopher J. White
;; Author: Christopher J. White <[email protected]>
;; Created: 7 Sep 2011
;; Version: 2.16
;; Keywords: outlines, data
;; GNU General Public License v2 (GNU GPL v2),
;; inspired by work from Sacha Chua
;;
;; This file is not part of GNU Emacs.
;;
;; This 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 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., 59 Temple Place - Suite 330, Boston,
;; MA 02111-1307, USA.
;;; Commentary:
;;
;; This package adds the ability to sync org-mode tasks with
;; Toodledo, a powerful web-based todo list manager that welcomes 3rd
;; party integrations. (See http://www.toodledo.com/)
;;
;; This version of `org-toodledo' utilizes version 2.0 of the Toodledo API.
;;
;; See https://github.com/christopherjwhite/org-toodledo
;;
;;; Change Log:
;;
;; 2011-09-07 (cjwhite)
;; - First release for general distribution based on API 2.0
;;
;; 2011-09-18 (cjwhite)
;; - (Bug fix) Properly create contexts that are missing on the
;; server. (cjwhite)
;; - (Bug fix) Eliminate hyphens in the tag/properties that are saved (cjwhite)
;; - Implemented sub-tasks -- requires pro account subscription (cjwhite)
;; - Added customization variable `org-toodledo-sync-import-new-tasks'
;;
;; 2011-09-24 (cjwhite)
;; - Use https if pro subscription and patch installed (url-http appears broken
;; for POSTs with data and https, at least on my box). To enable, apply the
;; patch as follows:
;; $ cd $emacs_install_dir/lisp/url
;; $ patch < $path_to_patch/url-http.el.emacs-23.3.patch
;; Then in emacs:
;; M-x byte-compile-file $emacs_install_dir/lisp/url/url-http.el
;; This patch seems to apply cleanly to 23.2 as well, but is not
;; tested there. Search below for "bugreport" for more details and
;; bug references.
;;
;; - Added `org-toodledo-run-tests' to load and run tests in
;; org-toodledo-test.el. This uses the active account,
;; creating/modifying/deleting tasks with the prefix
;; 'ORGTOODLEDOTEST'. All other tasks are ignored, so it *should*
;; operate cleanly on an active toodledo account with multiple
;; tasks. If you run this and it does not pass all tests, please
;; let me know (cjwhite)
;;
;; 2011-09-26 (cjwhite)
;; - Bug fix for checking boundp (myuhe)
;; - Support special chars (verified titles/notes) (myuhe)
;; - Added `org-toodledo-inhibit-https' to disable https
;;
;; 2011-09-29 (cjwhite)
;; - Bug fix: marking a task completed on the server did not update locally
;;
;; 2011-10-16 (cjwhite)
;; - Bug fix: first time sync of tasks with folders failed with org-mode 6.33x
;;
;; 2011-12-05 (cjwhite)
;; - Bug fix: folder / id mapping was reversed
;; - Bug fix: added require for aput / assoc
;; - Properly clear fields that are not set locally, ensuring they get
;; cleared on server
;;
;; 2012-01-29 (cjwhite) - Version 2.2.0
;; - Added support for starttime / duetime (merged in partical changes
;; for myuhe). Note that the web site does not seem properly handle
;; timezones. The time in org-mode is properly converted to a unix
;; timestamp (adjusting for timezone) and sent to the server, but
;; the displayed time on toodledo.com and in apps (at least iPad
;; app) is the time component in GMT, not the timezone set in the
;; account settings. Waiting for a response from toodledo.com on
;; this.
;;
;; - Added `org-toodledo-flatten-all-tasks' which disables
;; parent/child tasking. Set to true if you have more than 2 levels
;; and wish to sync tasks to the server as a flat list at least for
;; backup.
;;
;; - Improved debug logging, use `org-toodledo-toggle-debug' to turn
;; on debug logging
;;
;; - Updated `org-toodledo-run-tests' to use a test account instead of
;; the users account.
;;
;; 2012-01-30 (cjwhite) - Version 2.3
;; - Bug fix - major problem found whereby sync / modified times would
;; be off just slightly when syncing local changes up to the server.
;; This would make it look like the local task was still modified
;; even after sending it up to the server. As such, any changes to
;; the task on the server would result in duplicate tasks in
;; org-mode on the next sync, because that's how changes on both
;; sides are handled. As part of this fix I completely eliminated
;; the per task Modified and Sync properities as these are pretty
;; much unneeded because of the detection of changes by the hash
;; code
;;
;; - Bug fix for startdate / duedate. These were probably working ok,
;; but may have misbehaved if the timezone were more than 12h off
;; GMT, which I think can only happen in a very few rare cases.
;;
;; - Fixed up the starttime / duetime. Turns out that toodledo.com
;; treats these times like alarms, so a duetime of "7:00am" is 7am,
;; regardless of what timezone you are in. That means you can
;; change your timezone at will and the duetime is still 7am *local*
;; time. This is stored as an offset from midnight GMT on the due
;; date.
;;
;; - Added a version variable / function `org-toodledo-version'. This
;; is checked on sync and may do some cleanup of things that have
;; changed in various versions. This will make it easier down the
;; road to detect what version someone is running as well.
;;
;; - Added some delays into the devtest -- seems it may have been
;; syncing back and forth too fast (within the same second) such
;; that changes may not be perceived
;;
;; - Added a lot more debug messages to help with timing / sync
;; problems
;;
;; 2012-02-09 (cjwhite) - Version 2.4
;; - Added new patch for url-http. This should address at least some
;; of the spurious problems that caused an XML parse error. If the
;; XML response from the server was just around 1200 bytes, url-http
;; was not handling it properly and cutting off the last few bytes.
;; See the Installation instructions below for installing the
;; patches
;;
;; 2012-02-09 (cjwhite) - Version 2.5
;; - Bug fix - requesting a token was still using https even if
;; `org-toodledo-inhibit-https' was set to true.
;;
;; 2012-02-19 (cjwhite) - Version 2.6
;; - Added `org-toodledo-agenda-mark-task-deleted' function and
;; suggested binding to mark tasks deleted from agenda mode. See
;; Installation for info
;;
;; - When a task is marked for deleted, move the task to a task titled
;; "Deleted Tasks" to get it out of the way
;;
;; - Handle more than 2-levels of hierarchy by flatting all children.
;; The hierarchy will be preserved. See SUBTASKS above for more
;; details.
;;
;; - Realign all tags after sync according to the width of the current window
;;
;; - Better handling of 'duplicate' tasks -- When a task has been
;; modified locally and on the server, the user is prompted to
;; resolve the difference by selecting the local copy, the server
;; copy, or editing and resolving manually
;;
;; 2012-02-19 (cjwhite) - Version 2.6.1
;; - Fix for use of http vs https
;;
;; 2012-02-20 (cjwhite) - Version 2.7
;; - Added support for tags, they are added as org-mode tags.
;;
;; - Fixed handling of tasks with an embedded '*' - this would
;; completely mess things up of the '*' was the first char of
;; a line
;;
;; - Added customization `org-toodledo-indent-task-note', set to t
;; by default. This indents the note according to the task level.
;; This has a cleaner look and reduces the chance of errors like
;; the above with '*' as the first char.
;;
;; - Better detection of actual note text, eliminating blank lines
;;
;; - Fixed handling of "Invalid Key" error that indicates the token
;; needs to be refreshed
;;
;; - Fix bug for subtasks if parent is not at top-level
;;
;; 2012-03-13 (cjwhite) - Version 2.8
;; - Significantly better error handling for adding, editng and deleting
;; tasks during synchronization. If errors occurred, only the offending
;; task should be affected, allowing all other tasks to be synced. The
;; specific error message is logged for better debugging.
;;
;; - Added customization `org-toodledo-folder-support-mode'. Setting this
;; to `heading' will create heading tasks according to the folder name
;; and put all tasks in that folder as child tasks of the heading.
;; No support yet for creating folders in org mode, create the folders
;; on toodledo.com first and put at least one task in the folder. This
;; feature is somewhat experimental.
;;
;; - Improved handling of token expiration / invalid key errors
;;
;; - Added simulation mode for testing without actually hitting the server.
;; Allows simulating error conditions that are potentially hard to
;; reproduce with a real connection to the server. See org-toodledo-sim.el
;; and org-toodledo-test.el where sim calls are used.
;;
;; 2012-07-21 (cjwhite) - Version 2.9
;; - Added 'length' to the list of fields to ignore if 0 for computing
;; a hash
;;
;; - Removed deprecated/obsolete use of aput/adelete from assoc.el
;;
;; - Hopefully finally fixed spurious issue where syncing resulted in
;; "Failed to find todo entry...". When processing incoming tasks
;; from the server, process parent tasks *then* child tasks. It seems
;; in some cases the child task may show up first before the parent
;; is known.
;;
;; 2013-03-24 (cjwhite) - Version 2.10
;; - Renamed folder/goal properties to ToodledoFolder and ToodledoGoal
;;
;; - Added `org-toodledo-reset' function to eliminate any trace of
;; toodledo from an org file (great to use if you want to refresh
;; the server from your master org file)
;;
;; - Fixed a bug where parent/child tasks may not sync properly.
;;
;; - Improved folder support, works well now supporting creating folders
;; and moving tasks around to folders, including subtasks.
;;
;; - Fixed a bug that was causing issues running tests
;;
;; 2013-06-08 (cjwhite) - Version 2.11
;; - Fixed github issue #15, org-sync failed with org 8.x due to
;; missing function
;;
;; 2013-08-20 (cjwhite) - Version 2.12
;; - Revamp how deleted tasks are handled, just store the deleted
;; task ids instead of the "Deleted Tasks" folder
;;
;; - Support for `org-toodledo-archive-deleted-tasks'. When non-nil
;; deleted tasks are automatically archived after synced to the
;; the server. This is related to github issue #17.
;;
;; - Fixed github issue #21, error adding context that already exists.
;; The issue occurred because org is case sensitive, whereas toodledo
;; is not. Fixed by ignoring case when looking for contexts and folders.
;;
;; - Fixed github issue #22, toodledo does not support empty tasks. Added
;; a check to skip over any such tasks
;;
;; 2013-09-01 (cjwhite) - Version 2.13
;; - Support for `org-toodledo-archive-completed-tasks'. When non-nil
;; completed tasks are archived. This supports archiving tasks completed
;; locally via org as well as syncing-in tasks that were completed on
;; Toodledo.
;;
;; 2013-09-02 (cjwhite) - Version 2.14
;; - Fixed issue #24 - pay attention to org-default-priority
;;
;; 2013-09-28 (cjwhite) - Version 2.15
;; - Fixed issue #26 - look at `org-toodledo-sync-new-completed-tasks'
;; when syncing for the first time. If t, sync all completed tasks
;;
;; - Fixed issue #27 - support priority -1 as org levels E-Z
;;
;; - Fixed issue #28 - drop extra space in dates when no repeat
;;
;; - Fixed issue #31 - cleaned up compiler warnings
;;
;; - Fixed issue #29 - added `org-toodledo-post-sync-hook' to call after
;; each synchronization, even if errors occurred.
;;
;; 2013-11-30 (jeffkowalski) - Version 2.16
;; - Whitespace cleanup
;;
;; - Support for 'org-toodledo-preserve-drawers'. When non-nil drawer
;; properties will be preserved in entry text. As a consequence of this
;; modification, incoming entry text will have properties pulled out.
;;
;; - Set Repeat even if only the SCHEDULED/start repeats, and the DEADLINE
;; does not.
;;
;; 2014-03-30 (cjwhite) - Version 2.16
;; - Fixed problem with "Effort" showing up in drawers when not expected
;;
;;; Installation:
;;
;; 1. Required emacs packages:
;; * `w3m' or `w3mexcerpt' -- see Notes below
;; * `http-post-simple' -- http://www.emacswiki.org/emacs/http-post-simple.el
;;
;; Note, if you see messages like "(lambda (field) ...) quoted with ' rather
;; than with #'" related to http-post-simple, see this
;; [StackOverflow Question](http://stackoverflow.com/questions/17285048).
;; It seems there are 5 places in http-post-simple.el that use
;; ='(lambda...)= where just =(lambda...)= would be fine.
;;
;; 2. Put this file in your load path, byte compile the file for best
;; performance, see `byte-compile-file'.
;;
;; 3. Put the following in your .emacs:
;;
;; (push "<path-to-this-file>" load-path)
;; (require 'org-toodledo)
;; (setq org-toodledo-userid "<toodledo-userid>") << *NOT* your email!
;; (setq org-toodledo-password "<toodled-password>")
;;
;; ;; Useful key bindings for org-mode
;; (add-hook 'org-mode-hook
;; (lambda ()
;; (local-unset-key "\C-o")
;; (local-set-key "\C-od" 'org-toodledo-mark-task-deleted)
;; (local-set-key "\C-os" 'org-toodledo-sync)
;; )
;; )
;; (add-hook 'org-agenda-mode-hook
;; (lambda ()
;; (local-unset-key "\C-o")
;; (local-set-key "\C-od" 'org-toodledo-agenda-mark-task-deleted)
;; )
;; )
;;
;; 4. Install 2 patches for url-http.el (these are written for 23.3, but may
;; work for other versions, if not patch manually, as the diffs are
;; not that complex)
;;
;; url-http.el.emacs-23.3.patch
;; - addresses http://debbugs.gnu.org/cgi/bugreport.cgi?bug=9592,
;; involving attempted connection reuse
;; - addresses http://debbugs.gnu.org/cgi/bugreport.cgi?bug=8931,
;; problem when sending a request with no data
;;
;; url-http.el.emacs-23.3.patch2
;; - addresses http://debbugs.gnu.org/cgi/bugreport.cgi?bug=10768
;; fixes a problem with responses that are barely longer than 1
;; TCP data packet (about 1200 bytes)
;;
;; To install the patches:
;; $ cd $emacs_install_dir/lisp/url
;; $ patch < $path_to_patch/url-http.el.emacs-23.3.patch
;; $ patch < $path_to_patch/url-http.el.emacs-23.3.patch2
;;
;; Then in emacs:
;; M-x byte-compile-file $emacs_install_dir/lisp/url/url-http.el
;;
;; This patch seems to apply cleanly to 23.2 as well, but is not tested there.
;;
;;; Code:
(require 'org)
(unless (require 'w3m nil t)
(require 'w3mexcerpt))
(require 'xml)
(require 'json)
(require 'http-post-simple)
(require 'url)
(require 'url-http)
(require 'org-agenda)
(declare-function org-columns-quit "org-colview.el")
(declare-function org-toodledo-get-contexts "org-toodledo.el")
(declare-function org-toodledo-get-goals "org-toodledo.el")
(declare-function org-toodledo-test "org-toodledo-test.el")
(declare-function org-toodledo-sim-http-post "org-toodledo-sim.el")
;;
;; User customizable variables
;;
(defgroup org-toodledo nil
"Toodledo integration for Emacs Org mode"
:prefix "org-toodledo-"
:group 'org
:group 'outlines
:group 'hypermedia)
(defcustom org-toodledo-userid ""
"UserID from Toodledo (not your e-mail address).
See http://www.toodledo.com/info/api_doc.php"
:group 'org-toodledo
:type 'string)
(defcustom org-toodledo-password ""
"Password for Toodledo."
:group 'org-toodledo
:type 'string)
(defcustom org-toodledo-sync-on-save "ask"
"Action on save of a orgfile with toodledo tasks in it:
no - nothing
ask - ask the user to sync
yes - always sync"
:group 'org-toodledo
:type 'string
)
(defcustom org-toodledo-sync-import-new-tasks t
"If non-nil, import new tasks from the server, otherwise only edits
to existing tasks from the server are processed."
:group 'org-toodledo
:type 'boolean
)
(defcustom org-toodledo-status-to-org-map
'(
("Active" . "TODO")
("None" . "TODO")
("Next Action" . "TODO")
("Planning" . "TODO")
("Delegated" . "DELEGATED")
("Waiting" . "WAITING")
("Someday" . "SOMEDAY")
("Hold" . "SOMEDAY")
("Postponed" . "SOMEDAY")
("Canceled" . "CANCELED")
("Reference" . "REFERENCE")
)
"Map of Toodledo API 'status' names to org-mode TODO states."
:group 'org-toodledo
:type '(alist :key-type string :value-type string)
)
(defcustom org-toodledo-sync-new-completed-tasks nil
"When nil, new tasks downloaded from the server are not added if they
are already marked completed. Existing tasks in the buffer are always
updated. Set to t to sync completed tasks into the local buffer."
:group 'org-toodledo
:type 'boolean
)
(defcustom org-toodledo-inhibit-https nil
"Set to t to inhibit the use of HTTPS even if it's available"
:group 'org-toodledo
:type 'boolean
)
(defcustom org-toodledo-flatten-all-tasks nil
"Set to t to always flatten all tasks, ignoring any parent/child
relationship"
:group 'org-toodledo
:type 'boolean
)
(defcustom org-toodledo-indent-task-note t
"Set to t to indent the task note body according to the level of the task"
:group 'org-toodledo
:type 'boolean
)
(defcustom org-toodledo-folder-support-mode nil
"The method for handling folders."
:group 'org-toodledo
:type '(choice (const :tag "Store folder as property only" nil)
(const :tag "Treat folders as headings" heading)))
(defcustom org-toodledo-archive-deleted-tasks nil
"Set to t to archive deleted tasks once they are synced to the server."
:group 'org-toodledo
:type 'boolean
)
(defcustom org-toodledo-archive-completed-tasks nil
"Set to t to archive completed tasks once they are synced to the server."
:group 'org-toodledo
:type 'boolean
)
(defcustom org-toodledo-post-sync-hook nil
"Hook(s) to call after synchronization is complete. This will be run
whether the synchronization was successful or not.
Each hook is called with a single argument, the result list:
(list tot imod idel onew omod odel errors)
Where:
tot - total number of changes processed
imod idel inew - number of received modifications, deletions, and new tasks
onew omod odel - number of sent new, modificaitons, and deletions
errors - number of errors
"
:group 'org-toodledo
:type 'hook
)
(defcustom org-toodledo-preserve-drawers nil
"Set to t to preserve drawer properties in entry text."
:group 'org-toodledo
:type 'boolean
)
;;
;; Internal variables for tracking org-toodledo state
;;
(defvar org-toodledo-token-expiry nil "Expiry time for authentication token.")
(defvar org-toodledo-token nil "Authentication token.")
(defvar org-toodledo-key nil "Authentication key.")
(defvar org-toodledo-pro nil "Non-nil if Toodledo account is a pro account")
(defvar org-toodledo-pro-cached nil "Non-nil means pro variable is cached")
(defvar org-toodledo-test-mode nil "Non-nil used for testing")
(defvar org-toodledo-sync-message-time 2 "Seconds to pause after displaying sync message")
(defvar org-toodledo-use-https nil "Use HTTPS for all calls. This requires pro *and* a patched url-http.el.")
(defvar org-toodledo-log-level 1 "Level of logs to save to log buffer, 0 is error, 1 is info, 2 is debug")
(defvar org-toodledo-msg-level 1 "Level of logs to also send to message, 0 is error, 1 is info, 2 is debug")
(defvar org-toodledo-debug nil "Generate debug messages")
(defvar org-toodledo-folders nil "Map of folder names to ids")
(defvar org-toodledo-goals nil "Map of goal names to ids")
(defvar org-toodledo-contexts nil "Map of context names to ids")
(defvar org-toodledo-sim-mode nil "Set to t to simulate http posts, used for testing")
(defvar org-toodledo-last-parsed-response nil "Used to store the last parsed xml response when debug enabled")
(defvar org-toodledo-errors nil "List of errors for the last operation")
;; Registered application ID and token for Toodledo API 2.0
(defconst org-toodledo-appid "orgtoodledo2" "Toodledo registered appid for API 2.0")
(defconst org-toodledo-apptoken "api4e4fbf7454eeb" "Toodledo apptoken associated with appid for API 2.0")
(defconst org-toodledo-version "2.16")
(defmacro org-toodledo-make-lookup-function (name)
"Create a lookup function and caching functions for NAME.
variable: org-toodledo-NAMEs
functions: org-toodledo-get-NAMEs
org-toodledo-NAME-to-id
org-toodledo-id-to-NAME
"
(let ((cache-var (concat "org-toodledo-" name "s"))
(get-func (concat "org-toodledo-get-" name "s"))
(add-method (concat name "s/add"))
(get-method (concat name "s/get")))
(list
'progn
`(defun ,(intern get-func) (&optional force)
,(concat "Store an alist of (title . id) in `" cache-var "'.
Reload if FORCE is non-nil.")
(if (or force (null ,(intern cache-var)))
(setq ,(intern cache-var)
(mapcar
(lambda (node)
(cons
(caddar (xml-get-children node 'name))
(caddar (xml-get-children node 'id))))
(xml-get-children
(car (org-toodledo-call-method ,get-method))
(quote ,(intern name)))))
,(intern cache-var)))
`(defun ,(intern (concat "org-toodledo-" name "-to-id")) (item)
"Return numeric ID for CONTEXT, creating if necessary."
(let ((lookups ,(list (intern get-func))))
(if (null (assoc-string item lookups t))
;; Create it if it does not yet exist
(let ((result
(org-toodledo-call-method
,add-method
(list (cons "name" item)))))
(if (eq (caar result) 'error)
(org-toodledo-die
(format "Failed to add new %s: %s" ,name item))
(setq ,(intern cache-var)
(cons
(cons item
(caddar (xml-get-children
(car (xml-get-children
(car result)
(quote ,(intern name))))
'id)))
,(intern cache-var))
lookups ,(intern cache-var)))))
(cdr (assoc-string item lookups t))))
`(defun ,(intern (concat "org-toodledo-id-to-" name)) (id)
"Return name for context by ID."
(let ((lookups ,(list (intern get-func))))
(if (null (rassoc id lookups))
nil
(car (rassoc id lookups))))
)
)
)
)
(eval-when-compile
(org-toodledo-make-lookup-function "context")
(org-toodledo-make-lookup-function "goal")
(defconst org-toodledo-fields
'(
;; Toodledo recongized fields
"id" "title" "status" "completed" "repeat" "repeatfrom" "context"
"duedate" "duetime" "startdate" "starttime" "modified" "folder"
"goal" "priority" "note" "length" "parent" "tag"
;; org-toodledo only fields
"sync" "hash")
"All fields related to a task"
)
(defconst org-toodledo-fields-check-empty-or-zero
'("folder" "goal" "context" "length")
"Fields that should be set to nil if either \"0\" or \"\""
)
;; Create a convenience function "org-toodled-task-<field>" for each field
;; of a task
(mapc (lambda (field)
(if (member field org-toodledo-fields-check-empty-or-zero)
(eval
`(defun ,(intern (concat "org-toodledo-task-" field)) (task)
,(concat "Return the task property '" field
"' for TASK, return nil if \"0\" or \"\"")
(let ((value (cdr (assoc ,field task))))
(if (and value (not (equal value "0"))
(not (equal value "")))
value
nil))))
(eval `(defun ,(intern (concat "org-toodledo-task-" field)) (task)
,(concat "Return the task property '" field "' for TASK")
(cdr (assoc ,field task))))))
org-toodledo-fields)
)
(defconst org-toodledo-fields-dont-ask
'(
;; Fields that toodledo always returns, thus cannot be asked for
"id" "title" "modified" "completed"
;; org-toodledo only fields
"sync" "hash")
"Fields that must not be asked for from the server, either because the server
returns them automatically, or because they are internal only fields"
)
(defconst org-toodledo-fields-dont-send
'(
;; Toodledo automatically sets modified, so don't attempt to push it
"modified"
;; org-toodledo only fields
"sync" "hash")
"Fields that shouldn't be sent to the server"
)
(defconst org-toodledo-hash-fields
'( "title" "status" "completed" "repeat" "repeatfrom" "context"
"duedate" "duetime" "startdate" "starttime" "folder" "goal"
"priority" "note" "length" "parent" "tag")
"Fields that are used to compute the hash of a task for detecting
when a task changed."
)
(defconst org-toodledo-hash-fields-skip-if-zero
'( "duetime" "starttime" "length")
"Fields that are skipped if they are 0 when computing the hash.
This prevents newly supported fields from causing all tasks to
appear to have been modified." )
(defvar org-toodledo-fields-ask
(remove nil
(mapcar
(lambda (f) (if (member f org-toodledo-fields-dont-ask) nil f))
org-toodledo-fields))
"Fields that can be asked for (fields minus org-toodledo-fields-dont-ask)"
)
(defvar org-toodledo-fields-send
(remove nil
(mapcar
(lambda (f) (if (member f org-toodledo-fields-dont-send) nil f))
org-toodledo-fields))
"Fields that should be encoded and sent for new/modified
tasks (fields minus org-toodled-fields-dont-send)" )
(defconst org-toodledo-error-code-map
'(
("1" missing-key "You did not specify a key for authentication")
("2" invalid-key "The authentication key that you provided has expired or is invalid")
("3" too-many-tasks "Only 50 tasks can be added/edited/deleted at a time")
("4" no-tasks "You didn't specify any tasks to add/edit/delete")
("5" empty-task-title "The task's title cannot be blank")
("6" max-tasks-reached "The maximum number of tasks allowed per account (20000) has been reached")
("7" invalid-task-id "Invalid task ID number")
("8" invalid-folder-id "Invalid folder ID")
("9" invalid-context-id "Invalid context ID")
("10" invalid-goal-id "Invalid goal ID")
("11" invalid-location-id "Invalid location ID")
("12" no-changes "Nothing was changed. You'll get this error if you attempt to edit a task but don't pass any parameters to edit")
("13" invalid-parent-id "Invalid parent ID")
("100" unknown-error "Unknown Error")
("500" server-offline "The Toodledo server is offline for maintenance")
("501" ssl-requires-pro "SSL connections require a Pro subscription"))
"Map of Toodledo API error codes")
(defconst org-toodledo-api-status-map
'(("0" . "None")
("1" . "Next Action")
("2" . "Active")
("3" . "Planning")
("4" . "Delegated")
("5" . "Waiting")
("6" . "Hold")
("7" . "Postponed")
("8" . "Someday")
("9" . "Canceled")
("10" . "Reference")
)
"Map of Toodledo API 'status' field values to names for easy
reference. The possible values represent the keys for use in
org-toodledo-status-to-org-map" )
(defconst org-toodledo-property-names
'("ToodledoLastSync"
"ToodledoLastEdit"
"ToodledoLastDelete"
"OrgToodledoVersion"
"ToodledoID"
"Hash"
"ToodledoFolder"
"ToodledoGoal"
"ToodledoFolderID"
"ToodledoSyncError"
)
"List of org properties added by org-toodledo")
(defvar org-toodledo-tmp-ref -1
"Temporary ID used to tag new tasks when synced in bulk to the
server. These ids should only be used for the short period of
time when a new task is ")
;; Replacements for obsolete aput/adelete from assoc.el
(defmacro alist-put (alist key val)
`(let ((cons (assoc ,key ,alist)))
(if cons
(setq ,alist (delq (assoc ,key ,alist) ,alist)))
(push (cons ,key ,val) ,alist)
,alist))
(defmacro alist-delete (alist key)
`(setq ,alist (delq (assoc ,key ,alist) ,alist)))
;; There's a bug in url-http that seems to attempt connection reuse
;; when the connection is not valid. This seems to only affect https
;; but just disable if set.
;;
;; See http://debbugs.gnu.org/cgi/bugreport.cgi?bug=9592
;;
(when (boundp 'url-http-inhibit-connection-reuse)
(setq url-http-inhibit-connection-reuse t))
(defun org-toodledo-initialize (&optional default-heading)
"Setup current item in an org file with Toodledo tasks. If not "
(interactive)
(when (not (eq major-mode 'org-mode))
(error "Toodledo initialization must be performed on an org-mode file"))
(save-excursion
(if (org-toodledo-goto-base-entry t)
(org-toodledo-info "Org-toodled already initialized")
(let ((item default-heading)
result)
(setq org-toodledo-folders nil)
(setq org-toodledo-goals nil)
(setq org-toodledo-contexts nil)
(unless item
(condition-case nil
(progn
(org-back-to-heading t)
(setq item (read-from-minibuffer
"Default heading for Toodledo tasks: "
(elt (org-heading-components) 4)))
)
(error
(setq item (read-from-minibuffer
"Default heading for Toodledo tasks: " "TASKS"))))
)
(when item
(goto-char (point-min))
(unless (re-search-forward (format "^\*+[ \t]* %s"
(regexp-quote item)) nil t)
(if (y-or-n-p
(format "No heading found matching '%s', create? " item))
(progn
(goto-char (point-min))
(insert (concat "* " item "\n"))
(forward-line -1)
)
(error "Aborted")))
(org-entry-put (point) "ToodledoLastSync" "0")
(org-entry-put (point) "OrgToodledoVersion" org-toodledo-version)
(setq result (org-toodledo-sync nil nil t))
(goto-char (point-min))
(re-search-forward (format "^\*+[ \t]* %s" (regexp-quote item)))
(org-overview)
(org-content)
)
result
)
)
)
)
(defun org-toodledo-reset ()
"Remove any hint of toodledo from the current file"
(interactive)
(goto-char (point-min))
(while (re-search-forward
(concat "^ *:\\("
(mapconcat 'identity org-toodledo-property-names "\\|")
"\\):") nil t)
(org-entry-delete nil (match-string 1) "PROPERTIES"))
(goto-char (point-min))
)
(defun org-toodledo-version ()
"Display the current version of org-toodledo"
(message "org-toodledo version %s" org-toodledo-version))
(defun org-toodledo-toggle-debug ()
"Toggle debug messages. Messages are sent to the buffer *Org-toodledo-log*"
(interactive)
(setq org-toodledo-debug (not org-toodledo-debug))
(setq org-toodledo-log-level
(if org-toodledo-debug 3 1))
(if org-toodledo-debug
(message "Debug enabled - see buffer *Org-toodledo-log*")
(message "Debug disabled"))
)
(defun org-toodledo-clear-cached-vars ()
"Clear all cached variables such as the token, local list of
folders and contexts, etc. Call this if switching accounts."
(interactive)
(setq org-toodledo-token nil)
(setq org-toodledo-pro nil)
(setq org-toodledo-pro-cached nil)
(setq org-toodledo-folders nil)
(setq org-toodledo-goals nil)
(setq org-toodledo-contexts nil)
(setq org-toodledo-use-https nil)
)
(defun org-toodledo-check-version ()
(save-excursion
(if (not (org-toodledo-goto-base-entry))
(org-toodledo-initialize)
(let ((version (or (org-entry-get (point) "OrgToodledoVersion") "0")))
(if (version= version org-toodledo-version)
(org-toodledo-info
"org-toodledo buffer at latest version %s" version)
(org-toodledo-info
"org-toodledo is using older version %s than current org-toodledo version %s, upgrading"
version org-toodledo-version)
(when (version< version "2.3")
;; Fixup tags to eliminate the hyphen, which really
;; shouldn't be used in tag / property names
(goto-char (point-min))
(while (re-search-forward
"Toodledo\\(-\\)\\(lastsync\\|ID\\|lastedit_task\\|lastdelete_task\\)"
nil t)
(let ((str2 (match-string 2)))
(replace-match "" nil nil nil 1)
(cond
((string= str2 "lastsync")
(replace-match "LastSync" nil nil nil 2))
((string= str2 "lastedit_task")
(replace-match "LastEdit" nil nil nil 2))
((string= str2 "lastdelete_task")
(replace-match "LastDelete" nil nil nil 2)))))
(goto-char (point-min))
(while (re-search-forward ":\\(Modified\\|Sync\\):" nil t)
(org-entry-delete (point) "Modified")
(org-entry-delete (point) "Sync")))
(when (version< version "2.10")
;; Prefix Folder/Goal tags with Toodledo
(goto-char (point-min))
(while (re-search-forward ":\\(Folder\\|Goal\\):" nil t)
(replace-match "Toodledo\\1" nil nil nil 1)))
(when (version< version "2.12")
;; Cull "Deleted Tasks" into the OrgToodledoPendingDeletes variable
(goto-char (point-min))
(let ((regexp (concat "^\\*+[ \t]+\\(" org-todo-regexp "\\)"))
(deleted-tasks-str nil))
(while (re-search-forward regexp nil t)
;; 'task' is the current state of the task at point
;; and is parsed from the buffer after all tasks above
;; this point have been processed. That means parent
;; tasks either have a toodledoid, or were assigned a
;; tmp-ref
(let* ((task (org-toodledo-parse-current-task))
(id (org-toodledo-task-id task))
(deleted (org-entry-get (point) "Deleted")))
(when deleted
(setq deleted-tasks-str
(if deleted-tasks-str
(concat deleted-tasks-str " " id)
id))
(org-back-to-heading t)
(if org-toodledo-archive-deleted-tasks
;; Archive the task
(org-archive-subtree)
;; Just delete the task
(org-cut-subtree)))))
(when deleted-tasks-str
(org-toodledo-goto-base-entry)
(org-entry-put
(point) "OrgToodledoPendingDeletes" deleted-tasks-str)))
(let ((m (org-find-exact-headline-in-buffer "Deleted Tasks")))
(when m
(goto-char m)
(org-cut-subtree)
)
)
)
;; Finally, updated the version of the file
(if (org-toodledo-goto-base-entry)
(org-entry-put
(point) "OrgToodledoVersion" org-toodledo-version)))))))
;;
;; Token / key functions
;;
(defun org-toodledo-token-valid ()
"Return if org-toodledo-token is both non-null and not expired."
(and org-toodledo-token
org-toodledo-token-expiry
(time-less-p (current-time) org-toodledo-token-expiry)))
(defun org-toodledo-token ()
"Retrieve authentication token valid for four hours. This
token is used for all interaction with the server. If the token
expires, a new token is automatically retrieved. "
(if (or (string= org-toodledo-userid "")
(string= org-toodledo-password ""))
(error "Please set 'org-toodledo-userid' and 'org-toodledo-password or input the password"))
(if (org-toodledo-token-valid)
;; Return cached token
org-toodledo-token
;; Else retrieve a new token
(let* ((request-url (concat (if org-toodledo-inhibit-https "http" "https")
"://api.toodledo.com/2/account/token.php?f=xml"
";userid=" org-toodledo-userid
";appid=" org-toodledo-appid
";sig=" (md5 (concat org-toodledo-userid
org-toodledo-apptoken))))
response parsed-response)
(org-toodledo-debug "org-toodledo-token: '%s'" request-url)
(setq response
(if org-toodledo-sim-mode
(car (org-toodledo-sim-http-post "account/token"))
(with-current-buffer
(url-retrieve-synchronously
(concat (if org-toodledo-inhibit-https "http" "https")
"://api.toodledo.com/2/account/token.php?f=xml"
";userid=" org-toodledo-userid
";appid=" org-toodledo-appid
";sig=" (md5 (concat org-toodledo-userid
org-toodledo-apptoken))))
(buffer-substring (point-min) (point-max)))))
(setq parsed-response
(with-temp-buffer
(insert response)
(xml-parse-region (point-min) (point-max))))
(when (>= org-toodledo-log-level 2)
(org-toodledo-debug2
"org-toodledo-token:\n--- response:\n%S\n--- parsed response:\n%S\n---"
response parsed-response))
(if (equal (car (car parsed-response)) 'error)
(progn
(setq org-toodledo-token nil
org-toodledo-key nil