| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619 | 
							- ;;; org-contacts.el --- Contacts management
 
- ;; Copyright (C) 2010-2011 Julien Danjou <julien@danjou.info>
 
- ;; Author: Julien Danjou <julien@danjou.info>
 
- ;; Keywords: outlines, hypermedia, calendar
 
- ;;
 
- ;; This file is NOT part of GNU Emacs.
 
- ;;
 
- ;; GNU Emacs 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 3 of the License, or
 
- ;; (at your option) any later version.
 
- ;; GNU Emacs 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.  If not, see <http://www.gnu.org/licenses/>.
 
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
- ;;
 
- ;;; Commentary:
 
- ;; This file contains the code for managing your contacts into Org-mode.
 
- ;; To enter new contacts, you can use `org-capture' and a template just like
 
- ;; this:
 
- ;;         ("c" "Contacts" entry (file "~/Org/contacts.org")
 
- ;;          "* %(org-contacts-template-name)
 
- ;; :PROPERTIES:
 
- ;; :EMAIL: %(org-contacts-template-email)
 
- ;; :END:")))
 
- ;;
 
- ;;; Code:
 
- (eval-when-compile
 
-   (require 'cl))
 
- (eval-and-compile
 
-   (require 'org))
 
- (defgroup org-contacts nil
 
-   "Options concerning contacts management."
 
-   :group 'org)
 
- (defcustom org-contacts-files nil
 
-   "List of Org files to use as contacts source.
 
- If set to nil, all your Org files will be used."
 
-   :type '(repeat file)
 
-   :group 'org-contacts)
 
- (defcustom org-contacts-email-property "EMAIL"
 
-   "Name of the property for contact email address."
 
-   :type 'string
 
-   :group 'org-contacts)
 
- (defcustom org-contacts-address-property "ADDRESS"
 
-   "Name of the property for contact address."
 
-   :type 'string
 
-   :group 'org-contacts)
 
- (defcustom org-contacts-birthday-property "BIRTHDAY"
 
-   "Name of the property for contact birthday date."
 
-   :type 'string
 
-   :group 'org-contacts)
 
- (defcustom org-contacts-birthday-format "Birthday: %l (%Y)"
 
-   "Format of the anniversary agenda entry. The following replacements are available:
 
-   %h - Heading name
 
-   %l - Link to the heading
 
-   %y - Number of year
 
-   %Y - Number of year (ordinal)"
 
-   :type 'string
 
-   :group 'org-contacts)
 
- (defcustom org-contacts-last-read-mail-property "LAST_READ_MAIL"
 
-   "Name of the property for contact last read email link storage."
 
-   :type 'string
 
-   :group 'org-contacts)
 
- (defcustom org-contacts-icon-property "ICON"
 
-   "Name of the property for contact icon."
 
-   :type 'string
 
-   :group 'org-contacts)
 
- (defcustom org-contacts-nickname-property "NICKNAME"
 
-   "Name of the property for IRC nickname match."
 
-   :type 'string
 
-   :group 'org-contacts)
 
- (defcustom org-contacts-icon-size 32
 
-   "Size of the contacts icons."
 
-   :type 'string
 
-   :group 'org-contacts)
 
- (defcustom org-contacts-icon-use-gravatar (fboundp 'gravatar-retrieve)
 
-   "Whether use Gravatar to fetch contact icons."
 
-   :type 'boolean
 
-   :group 'org-contacts)
 
- (defcustom org-contacts-completion-ignore-case t
 
-   "Ignore case when completing contacts."
 
-   :type 'boolean
 
-   :group 'org-contacts)
 
- (defcustom org-contacts-group-prefix "+"
 
-   "Group prefix."
 
-   :type 'string
 
-   :group 'org-contacts)
 
- (defcustom org-contacts-matcher (concat org-contacts-email-property "<>\"\"")
 
-   "Matching rule for finding heading that are contacts.
 
- This can be a tag name, or a property check."
 
-   :type 'string
 
-   :group 'org-contacts)
 
- (defcustom org-contacts-email-link-description-format "%s (%d)"
 
-   "Format used to store links to email.
 
- This overrides `org-email-link-description-format' if set."
 
-   :group 'org-contacts
 
-   :type 'string)
 
- (defcustom org-contacts-vcard-file "contacts.vcf"
 
-   "Default file for vcard export."
 
-   :group 'org-contacts
 
-   :type 'file)
 
- (defvar org-contacts-keymap
 
-   (let ((map (make-sparse-keymap)))
 
-     (define-key map "M" 'org-contacts-view-send-email)
 
-     (define-key map "i" 'org-contacts-view-switch-to-irc-buffer)
 
-     map)
 
-   "The keymap used in `org-contacts' result list.")
 
- (defun org-contacts-files ()
 
-   "Return list of Org files to use for contact management."
 
-   (or org-contacts-files (org-agenda-files t 'ifmode)))
 
- (defun org-contacts-filter (&optional name-match tags-match)
 
-   "Search for a contact maching NAME-MATCH and TAGS-MATCH.
 
- If both match values are nil, return all contacts."
 
-   (let ((tags-matcher
 
-          (if tags-match
 
-              (cdr (org-make-tags-matcher tags-match))
 
-            t))
 
-         (name-matcher
 
-          (if name-match
 
-              '(org-string-match-p name-match (org-get-heading t))
 
-            t))
 
-         (contacts-matcher
 
-          (cdr (org-make-tags-matcher org-contacts-matcher)))
 
-         markers result)
 
-     (dolist (file (org-contacts-files))
 
-       (org-check-agenda-file file)
 
-       (with-current-buffer (org-get-agenda-file-buffer file)
 
-         (unless (org-mode-p)
 
-           (error "File %s is no in `org-mode'" file))
 
-         (org-scan-tags
 
-          '(add-to-list 'markers (set-marker (make-marker) (point)))
 
-          `(and ,contacts-matcher ,tags-matcher ,name-matcher))))
 
-     (dolist (marker markers result)
 
-       (org-with-point-at marker
 
-         (add-to-list 'result
 
-                      (list (org-get-heading t) marker (org-entry-properties marker 'all)))))))
 
- (when (not (fboundp 'completion-table-case-fold))
 
-   ;; That function is new in Emacs 24...
 
-   (defun completion-table-case-fold (table string pred action)
 
-     (let ((completion-ignore-case t))
 
-       (complete-with-action action table string pred))))
 
- (defun org-contacts-complete-name (&optional start)
 
-   "Complete text at START with a user name and email."
 
-   (let* ((end (point))
 
-          (start (or start
 
-                     (save-excursion
 
-                       (re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*")
 
-                       (goto-char (match-end 0))
 
-                       (point))))
 
-          (orig (buffer-substring start end))
 
-          (completion-ignore-case org-contacts-completion-ignore-case)
 
-          (group-completion-p (org-string-match-p (concat "^" org-contacts-group-prefix) orig))
 
-          (completion-list
 
-           (if group-completion-p
 
-               (mapcar (lambda (group) (propertize (concat org-contacts-group-prefix group) 'org-contacts-group group))
 
-                       (org-uniquify
 
-                        (loop for contact in (org-contacts-filter)
 
-                              with group-list
 
-                              nconc (org-split-string
 
-                                     (or (cdr (assoc-string "ALLTAGS" (caddr contact))) "") ":"))))
 
-             (loop for contact in (org-contacts-filter)
 
-                   ;; The contact name is always the car of the assoc-list
 
-                   ;; returned by `org-contacts-filter'.
 
-                   for contact-name = (car contact)
 
-                   ;; Build the list of the user email addresses.
 
-                   for email-list = (split-string (or
 
-                                                   (cdr (assoc-string org-contacts-email-property (caddr contact)))
 
-                                                   ""))
 
-                   ;; If the user has email addresses…
 
-                   if email-list
 
-                   ;; … append a list of USER <EMAIL>.
 
-                   nconc (loop for email in email-list
 
-                               collect (org-contacts-format-email contact-name email)))))
 
-          (completion-list (all-completions orig completion-list)))
 
-     ;; If we are completing a group, and that's the only group, just return
 
-     ;; the real result.
 
-     (when (and group-completion-p
 
-                (= (length completion-list) 1))
 
-       (setq completion-list
 
-             (list (concat (car completion-list) ";: "
 
-                           (mapconcat 'identity
 
-                                      (loop for contact in (org-contacts-filter
 
-                                                            nil
 
-                                                            (get-text-property 0 'org-contacts-group (car completion-list)))
 
-                                            ;; The contact name is always the car of the assoc-list
 
-                                            ;; returned by `org-contacts-filter'.
 
-                                            for contact-name = (car contact)
 
-                                            ;; Grab the first email of the contact
 
-                                            for email = (car (split-string (or
 
-                                                                            (cdr (assoc-string org-contacts-email-property (caddr contact)))
 
-                                                                            "")))
 
-                                            ;; If the user has an email address, append USER <EMAIL>.
 
-                                            if email collect (org-contacts-format-email contact-name email))
 
-                                      ", ")))))
 
-     (list start end (if org-contacts-completion-ignore-case
 
- 			(apply-partially #'completion-table-case-fold completion-list)
 
- 		      completion-list))))
 
- (defun org-contacts-message-complete-function ()
 
-   "Function used in `completion-at-point-functions' in `message-mode'."
 
-   (let ((mail-abbrev-mode-regexp
 
-          "^\\(Resent-To\\|To\\|B?Cc\\|Reply-To\\|From\\|Mail-Followup-To\\|Mail-Copies-To\\|Disposition-Notification-To\\|Return-Receipt-To\\):"))
 
-         (when (mail-abbrev-in-expansion-header-p)
 
-           (org-contacts-complete-name))))
 
- (defun org-contacts-gnus-get-name-email ()
 
-   "Get name and email address from Gnus message."
 
-   (if (gnus-alive-p)
 
-       (gnus-with-article-headers
 
-         (mail-extract-address-components
 
-          (or (mail-fetch-field "From") "")))))
 
- (defun org-contacts-gnus-article-from-get-marker ()
 
-   "Return a marker for a contact based on From."
 
-   (let* ((address (org-contacts-gnus-get-name-email))
 
-          (name (car address))
 
-          (email (cadr address)))
 
-     (cadar (or (org-contacts-filter
 
-                 nil
 
-                 (concat org-contacts-email-property "={\\b" (regexp-quote email) "\\b}"))
 
-                (when name
 
-                  (org-contacts-filter
 
-                   (concat "^" name "$")))))))
 
- (defun org-contacts-gnus-article-from-goto ()
 
-   "Go to contact in the From address of current Gnus message."
 
-   (interactive)
 
-   (let ((marker (org-contacts-gnus-article-from-get-marker)))
 
-     (when marker
 
-       (switch-to-buffer-other-window (marker-buffer marker))
 
-       (goto-char marker)
 
-       (when (org-mode-p)
 
-         (org-show-context 'agenda)
 
-         (save-excursion
 
-           (and (outline-next-heading)
 
-                ;; show the next heading
 
-                (org-flag-heading nil)))))))
 
- (defun org-contacts-anniversaries (&optional field format)
 
-   "Compute FIELD anniversary for each contact, returning FORMAT.
 
- Default FIELD value is \"BIRTHDAY\".
 
- Format is a string matching the following format specification:
 
-   %h - Heading name
 
-   %l - Link to the heading
 
-   %y - Number of year
 
-   %Y - Number of year (ordinal)"
 
-   (let ((calendar-date-style 'american)
 
-         (entry ""))
 
-     (unless format (setq format org-contacts-birthday-format))
 
-     (loop for contact in (org-contacts-filter)
 
-           for anniv = (let ((anniv (cdr (assoc-string
 
-                                          (or field org-contacts-birthday-property)
 
-                                          (caddr contact)))))
 
-                         (when anniv
 
-                           (calendar-gregorian-from-absolute
 
-                            (org-time-string-to-absolute anniv))))
 
-           ;; Use `diary-anniversary' to compute anniversary.
 
-           if (and anniv (apply 'diary-anniversary anniv))
 
-           collect (format-spec format
 
-                                `((?l . ,(org-with-point-at (cadr contact) (org-store-link nil)))
 
-                                  (?h . ,(car contact))
 
-                                  (?y . ,(- (calendar-extract-year date)
 
-                                            (calendar-extract-year anniv)))
 
-                                  (?Y . ,(let ((years (- (calendar-extract-year date)
 
-                                                         (calendar-extract-year anniv))))
 
-                                           (format "%d%s" years (diary-ordinal-suffix years)))))))))
 
- (defun org-completing-read-date (prompt collection
 
-                                         &optional predicate require-match initial-input
 
-                                         hist def inherit-input-method)
 
-   "Like `completing-read' but reads a date.
 
- Only PROMPT and DEF are really used."
 
-   (org-read-date nil nil nil prompt nil def))
 
- (add-to-list 'org-property-set-functions-alist
 
-              `(,org-contacts-birthday-property . org-completing-read-date))
 
- (defun org-contacts-template-name (&optional return-value)
 
-   "Try to return the contact name for a template.
 
- If not found return RETURN-VALUE or something that would ask the user."
 
-   (or (car (org-contacts-gnus-get-name-email))
 
-       return-value
 
-       "%^{Name}"))
 
- (defun org-contacts-template-email (&optional return-value)
 
-   "Try to return the contact email for a template.
 
- If not found return RETURN-VALUE or something that would ask the user."
 
-   (or (cadr (org-contacts-gnus-get-name-email))
 
-       return-value
 
-       (concat "%^{" org-contacts-email-property "}p")))
 
- (defun org-contacts-gnus-store-last-mail ()
 
-   "Store a link between mails and contacts.
 
- This function should be called from `gnus-article-prepare-hook'."
 
-   (let ((marker (org-contacts-gnus-article-from-get-marker)))
 
-     (when marker
 
-       (with-current-buffer (marker-buffer marker)
 
-         (save-excursion
 
-           (goto-char marker)
 
-           (let* ((org-email-link-description-format (or org-contacts-email-link-description-format
 
-                                                         org-email-link-description-format))
 
-                  (link (gnus-with-article-buffer (org-store-link nil))))
 
-             (org-set-property org-contacts-last-read-mail-property link)))))))
 
- (defun org-contacts-icon-as-string ()
 
-   (let ((image (org-contacts-get-icon)))
 
-     (concat
 
-      (propertize "-" 'display
 
-                  (append
 
-                   (if image
 
-                       image
 
-                     `'(space :width (,org-contacts-icon-size)))
 
-                   '(:ascent center)))
 
-      " ")))
 
- ;;;###autoload
 
- (defun org-contacts (name)
 
-   "Create agenda view for contacts matching NAME."
 
-   (interactive (list (read-string "Name: ")))
 
-   (let ((org-agenda-files (org-contacts-files))
 
-         (org-agenda-skip-function
 
-          (lambda () (org-agenda-skip-if nil `(notregexp ,name))))
 
-         (org-agenda-format (propertize
 
-                             "%(org-contacts-icon-as-string)% p% s%(org-contacts-irc-number-of-unread-messages)%+T"
 
-                             'keymap org-contacts-keymap))
 
-         (org-agenda-overriding-header
 
-          (or org-agenda-overriding-header
 
-              (concat "List of contacts matching `" name "':"))))
 
-     (setq org-agenda-skip-regexp name)
 
-     (org-tags-view nil org-contacts-matcher)
 
-     (with-current-buffer org-agenda-buffer-name
 
-       (setq org-agenda-redo-command
 
-             (list 'org-contacts name)))))
 
- (defun org-contacts-completing-read (prompt
 
-                                      &optional predicate
 
-                                      initial-input hist def inherit-input-method)
 
-   "Call `completing-read' with contacts name as collection."
 
-   (org-completing-read
 
-    prompt (org-contacts-filter) predicate t initial-input hist def inherit-input-method))
 
- (defun org-contacts-format-email (name email)
 
-   "Format a mail address."
 
-   (unless email
 
-     (error "`email' cannot be nul"))
 
-   (if name
 
-       (concat name " <" email ">")
 
-     email))
 
- (defun org-contacts-check-mail-address (mail)
 
-   "Add MAIL address to contact at point if it does not have it."
 
-   (let ((mails (org-entry-get (point) org-contacts-email-property)))
 
-     (unless (member mail (split-string mails))
 
-       (when (yes-or-no-p
 
-              (format "Do you want to this address to %s?" (org-get-heading t)))
 
-         (org-set-property org-contacts-email-property (concat mails " " mail))))))
 
- (defun org-contacts-gnus-check-mail-address ()
 
-   "Check that contact has the current address recorded.
 
- This function should be called from `gnus-article-prepare-hook'."
 
-   (let ((marker (org-contacts-gnus-article-from-get-marker)))
 
-     (when marker
 
-       (org-with-point-at marker
 
-         (org-contacts-check-mail-address (cadr (org-contacts-gnus-get-name-email)))))))
 
- (defun org-contacts-gnus-insinuate ()
 
-   "Add some hooks for Gnus user.
 
- This adds `org-contacts-gnus-check-mail-address' and
 
- `org-contacts-gnus-store-last-mail' to
 
- `gnus-article-prepare-hook'. It also adds a binding on `;' in
 
- `gnus-summary-mode-map' to `org-contacts-gnus-article-from-goto'"
 
-   (require 'gnus)
 
-   (require 'gnus-art)
 
-   (define-key gnus-summary-mode-map ";" 'org-contacts-gnus-article-from-goto)
 
-   (add-hook 'gnus-article-prepare-hook 'org-contacts-gnus-check-mail-address)
 
-   (add-hook 'gnus-article-prepare-hook 'org-contacts-gnus-store-last-mail))
 
- (when (boundp 'completion-at-point-functions)
 
-   (add-hook 'message-mode-hook
 
- 	    (lambda ()
 
- 	      (add-to-list 'completion-at-point-functions
 
- 			   'org-contacts-message-complete-function))))
 
- (defun org-contacts-wl-get-from-header-content ()
 
-   "Retrieve the content of the `From' header of an email.
 
- Works from wl-summary-mode and mime-view-mode - that is while viewing email.
 
- Depends on Wanderlust been loaded."
 
-   (with-current-buffer (org-capture-get :original-buffer)
 
-     (cond
 
-      ((eq major-mode 'wl-summary-mode) (when wl-summary-buffer-elmo-folder
 
-                                          (elmo-message-field
 
-                                           wl-summary-buffer-elmo-folder
 
-                                           (wl-summary-message-number)
 
-                                           'from)))
 
-      ((eq major-mode 'mime-view-mode) (std11-narrow-to-header)
 
-                                       (prog1
 
-                                           (std11-fetch-field "From")
 
-                                         (widen))))))
 
- (defun org-contacts-wl-get-name-email ()
 
-   "Get name and email address from wanderlust email.
 
- See `org-contacts-wl-get-from-header-content' for limitations."
 
-   (let ((from (org-contacts-wl-get-from-header-content)))
 
-     (when from
 
-       (list (wl-address-header-extract-realname from)
 
- 	    (wl-address-header-extract-address from)))))
 
- (defun org-contacts-template-wl-name (&optional return-value)
 
-   "Try to return the contact name for a template from wl.
 
- If not found return RETURN-VALUE or something that would ask the user."
 
-   (or (car (org-contacts-wl-get-name-email))
 
-       return-value
 
-       "%^{Name}"))
 
- (defun org-contacts-template-wl-email (&optional return-value)
 
-   "Try to return the contact email for a template from wl.
 
- If not found return RETURN-VALUE or something that would ask the user."
 
-   (or (cadr (org-contacts-wl-get-name-email))
 
-       return-value
 
-       (concat "%^{" org-contacts-email-property "}p")))
 
- (defun org-contacts-view-send-email (&optional ask)
 
-   "Send email to the contact at point.
 
- If ASK is set, ask for the email address even if there's only one address."
 
-   (interactive "P")
 
-   (let ((marker (org-get-at-bol 'org-hd-marker)))
 
-     (org-with-point-at marker
 
-       (let ((emails (org-entry-get (point) org-contacts-email-property)))
 
-         (if emails
 
-             (let ((email-list (split-string emails)))
 
-               (if (and (= (length email-list) 1) (not ask))
 
-                   (compose-mail (org-contacts-format-email
 
-                                  (org-get-heading t) emails))
 
-                 (let ((email (completing-read "Send mail to which address: " email-list)))
 
-                   (org-contacts-check-mail-address email)
 
-                   (compose-mail (org-contacts-format-email (org-get-heading t) email)))))
 
-           (error (format "This contact has no mail address set (no %s property)."
 
-                          org-contacts-email-property)))))))
 
- (defun org-contacts-get-icon (&optional pom)
 
-   "Get icon for contact at POM."
 
-   (setq pom (or pom (point)))
 
-   (catch 'icon
 
-     ;; Use `org-contacts-icon-property'
 
-     (let ((image-data (org-entry-get pom org-contacts-icon-property)))
 
-       (when image-data
 
-         (throw 'icon
 
-                (if (fboundp 'gnus-rescale-image)
 
-                    (gnus-rescale-image (create-image image-data)
 
-                                        (cons org-contacts-icon-size org-contacts-icon-size))
 
-                  (create-image image-data)))))
 
-     ;; Next, try Gravatar
 
-     (when org-contacts-icon-use-gravatar
 
-       (let* ((gravatar-size org-contacts-icon-size)
 
-              (email-list (org-entry-get pom org-contacts-email-property))
 
-              (gravatar
 
-               (when email-list
 
-                 (loop for email in (split-string email-list)
 
-                       for gravatar = (gravatar-retrieve-synchronously email)
 
-                       if (and gravatar
 
-                               (not (eq gravatar 'error)))
 
-                       return gravatar))))
 
-         (when gravatar (throw 'icon gravatar))))))
 
- (defun org-contacts-irc-buffer (&optional pom)
 
-   "Get the IRC buffer associated with the entry at POM."
 
-   (setq pom (or pom (point)))
 
-   (let ((nick (org-entry-get pom org-contacts-nickname-property)))
 
-     (when nick
 
-       (let ((buffer (get-buffer nick)))
 
-         (when buffer
 
-           (with-current-buffer buffer
 
-             (when (eq major-mode 'erc-mode)
 
-               buffer)))))))
 
- (defun org-contacts-irc-number-of-unread-messages (&optional pom)
 
-   "Return the number of unread messages for contact at POM."
 
-   (when (boundp 'erc-modified-channels-alist)
 
-     (let ((number (cadr (assoc (org-contacts-irc-buffer pom) erc-modified-channels-alist))))
 
-       (if number
 
-           (format (concat "%3d unread message" (if (> number 1) "s" " ") " ") number)
 
-         (make-string 21 ? )))))
 
- (defun org-contacts-view-switch-to-irc-buffer ()
 
-   "Switch to the IRC buffer of the current contact if it has one."
 
-   (interactive)
 
-   (let ((marker (org-get-at-bol 'org-hd-marker)))
 
-     (org-with-point-at marker
 
-       (switch-to-buffer-other-window (org-contacts-irc-buffer)))))
 
- (defun org-contacts-completing-read-nickname (prompt collection
 
-                                                      &optional predicate require-match initial-input
 
-                                                      hist def inherit-input-method)
 
-   "Like `completing-read' but reads a nickname."
 
-   (org-completing-read prompt (append collection (erc-nicknames-list)) predicate require-match
 
-                        initial-input hist def inherit-input-method))
 
- (defun erc-nicknames-list ()
 
-   "Return all nicknames of all ERC buffers."
 
-   (if (fboundp 'erc-buffer-list)
 
-       (loop for buffer in (erc-buffer-list)
 
-             nconc (with-current-buffer buffer
 
-                     (loop for user-entry in (mapcar 'car (erc-get-channel-user-list))
 
-                           collect (elt user-entry 1))))))
 
- (add-to-list 'org-property-set-functions-alist
 
-              `(,org-contacts-nickname-property . org-contacts-completing-read-nickname))
 
- (defun org-contacts-vcard-escape (str)
 
-   "Escape ; , and \n in STR for use in the VCard format.
 
- Thanks to http://www.emacswiki.org/cgi-bin/wiki/bbdb-vcard-export.el for the regexp."
 
-   (when str
 
-     (replace-regexp-in-string "\n" "\\\\n" (replace-regexp-in-string "\\(;\\|,\\|\\\\\\)" "\\\\\\1" str))))
 
- (defun org-contacts-vcard-encode-name (name)
 
-   "Try to encode NAME as VCard's N property. The N property expects FamilyName;GivenName;AdditionalNames;Prefix;Postfix.
 
- Org-contacts does not specify how to encode the name. So we try to do our best."
 
-   (concat (replace-regexp-in-string "\\(\\w+\\) \\(.*\\)" "\\2;\\1" name) ";;;"))
 
- (defun org-contacts-vcard-format (contact)
 
-   "Formats CONTACT in VCard 3.0 format."
 
-   (let* ((properties (caddr contact))
 
- 	 (name (org-contacts-vcard-escape (car contact)))
 
- 	 (n (org-contacts-vcard-encode-name name))
 
- 	 (email (org-contacts-vcard-escape (cdr (assoc-string org-contacts-email-property properties))))
 
- 	 (bday (org-contacts-vcard-escape (cdr (assoc-string org-contacts-birthday-property properties))))
 
- 	 (addr (cdr (assoc-string org-contacts-address-property properties)))
 
- 	 (nick (org-contacts-vcard-escape (cdr (assoc-string org-contacts-nickname-property properties))))
 
- 	 (head (format "BEGIN:VCARD\nVERSION:3.0\nN:%s\nFN:%s\n" n name)))
 
-     (concat head
 
- 	    (when email (format "EMAIL:%s\n" email))
 
- 	    (when addr
 
- 	      (format "ADR:;;%s\n" (replace-regexp-in-string "\\, ?" ";" addr)))
 
- 	    (when bday
 
- 	      (let ((cal-bday (calendar-gregorian-from-absolute (org-time-string-to-absolute bday))))
 
- 		(format "BDAY:%04d-%02d-%02d\n"
 
- 			(calendar-extract-year cal-bday)
 
- 			(calendar-extract-month cal-bday)
 
- 			(calendar-extract-day cal-bday))))
 
- 	    (when nick (format "NICKNAME:%s\n" nick))
 
- 	    "END:VCARD\n\n")))
 
- (defun org-contacts-export-as-vcard (&optional name file to-buffer)
 
-   "Export all contacts matching NAME as VCard 3.0. It TO-BUFFER is nil, the content is written to FILE or `org-contacts-vcard-file'. If TO-BUFFER is non-nil, the buffer is created and the VCard is written into that buffer."
 
-   (interactive) ; TODO ask for name?
 
-   (let* ((filename (or file org-contacts-vcard-file))
 
- 	 (buffer (if to-buffer
 
- 		     (get-buffer-create to-buffer)
 
- 		     (find-file-noselect filename))))
 
-     (message "Exporting...")
 
-     (set-buffer buffer)
 
-     (let ((inhibit-read-only t)) (erase-buffer))
 
-     (fundamental-mode)
 
-     (org-install-letbind)
 
-     (when (fboundp 'set-buffer-file-coding-system)
 
-       (set-buffer-file-coding-system coding-system-for-write))
 
-     (loop for contact in (org-contacts-filter name)
 
- 	 do (insert (org-contacts-vcard-format contact)))
 
-     (if to-buffer
 
- 	(current-buffer)
 
- 	(progn (save-buffer) (kill-buffer)))))
 
- (defun org-contacts-show-map (&optional name)
 
-   "Show contacts on a map. Requires google-maps-el."
 
-   (interactive)
 
-   (unless (fboundp 'google-maps-static-show)
 
-     (error "org-contacts-show-map requires google-maps-el."))
 
-   (google-maps-static-show
 
-    :markers
 
-    (loop
 
-       for contact in (org-contacts-filter name)
 
-       for addr = (cdr (assoc-string org-contacts-address-property (caddr contact)))
 
-       if addr
 
-       collect (cons (list addr) (list :label (string-to-char (car contact)))))))
 
- (provide 'org-contacts)
 
 
  |