This package allows easy contact-management and inspection via native org-mode facilities.
People can be discovered from specific named files, and if none are specified then they
are discovered within each of your org-agenda files (i.e. We default to the files listed in org-agenda-files).
- People names are expected to be headlines.
- With data about them stored in property drawers.
This is an example of a pair of contacts, and you can see more within the test/org-people-test.org file:
* People
** Alice :family:contact:
:PROPERTIES:
:ADDRESS: 32 Something Street
:EMAIL: alice@example.com
:PHONE: +123 456 789
:CHILDREN: Mallory
:NICKNAME: Allu
:END:
** Bob :colleague:contact:
:PROPERTIES:
:ADDRESS: 32 Something Lane
:EMAIL: bob@example.com
:PHONE: +123 456 987
:END:
It is assumed that you'll have "ADDRESS", "EMAIL", "PHONE", and other similar properties - but you can add as many properties as you wish and later retrieve/view them. So long as there is a contact tag and at least one property then the entry will be recognized.
The package doesn't mandate the use of any specific properties, however there are a couple of properties which get special handling:
- If
:NICKNAMEis present it will be offered a completion-target. - If
:WEBSITEis present it will be used when contact-links are exported to HTML.
The summary view of all known contacts (M-x org-people-summary), and the CSV/vCARD exportors, will default to using the ADDRESS, EMAIL, and PHONE properties, however these can be changed.
Here's the default view, a list of contacts showing :NAME, :EMAIL, :PHONE and :TAGS:
I've enabled hl-line-mode here, to highlight the current row, which is an optional enhancement. Within the list you can mark [multiple] people and apply operations to them, for example opening
the :WEBSITE of each person.
The legacy way to install would be to clone this repository and ensure the directory is available upon your load-path, or copy your local lisp tree. The package is available upon MELPA, and can be installed from there in the standard way.
Suggested usage if you're using the traditional approach:
(require 'org-people)
; insert a contact "thing" at the current point.
(global-set-key (kbd "C-c p") 'org-people-insert)
; Show a table of all known contacts. The table is sortable, filterable, & etc.
(global-set-key (kbd "C-c P") 'org-people-summary)
If you prefer use-package then this works:
(use-package org-people
:after org
:bind
(("C-c p" . org-people-insert)
("C-c P" . org-people-summary))
:hook
(org-people-summary-mode . hl-line-mode))
That block enables hl-line-mode within the summary-buffer, which has special support to only highlight people-names rather than any inline property values. To enable this manually you could use this:
(add-hook 'org-people-summary-mode-hook #'(lambda () (hl-line-mode 1)))
This package is pretty new, and there are several existing packages with overlapping functionality you might wish to compare against.
- org-contacts
- Stable for many years. It has integration with IRC, rmail, etc
- Defines a custom link-type
org-contact:XXXsimilar to theorg-people:XXXthis package supports. - However org-contacts has nothing like our summary-view, and feels more like a fancy auto-completion framework.
- org-vcard
- Much more complete support for export, and has import too (org-people has no input facilities).
- Assumes people-data is stored in [sub]headlines, rather than properties.
- Limited additional functionality.
The main difference between this package and others I looked at is the interactive summary-view, which allows showing all known people in a flexible way, with sorting, filtering & etc. This package also has the utility functions for generating/maintaining org-mode tables populated with contacts, or their details.
If you use org-capture you may use the following template to add a new entry:
(setq org-capture-templates
(append org-capture-templates
'(("p" "People" entry (file+headline "~/Private/Org/PEOPLE.org" "People")
"* %^{Name}\n:PROPERTIES:\n:EMAIL: %^{Email}\n:PHONE: %^{Phone}\n:END:\n%?"
:empty-lines 1))))
These are the main user-focused functions within the package to work with contacts:
org-people-insert- Insert contact-data, via interactive prompts (with
completing-read).
- Insert contact-data, via interactive prompts (with
org-people-summary- Parse all known contacts and pop to a buffer containing a summary of their details.
- This uses
tabulated-list-modeand is documented further below.- But in brief you can mark, filter, and adjust columns and their contents pretty flexibly.
org-people-tags-to-table- Designed to create auto-updating tables inside
org-modedocuments.
- Designed to create auto-updating tables inside
org-people-person-to-table- Designed to create auto-updating tables inside
org-modedocuments.
- Designed to create auto-updating tables inside
No special configuration is required, although if you wish to use a different tag to identify the contacts you may specify that via org-people-search-tag. Similarly many of the default operations may be updated via the appropriate configuration values, and these are documented within the package itself.
If you wished to limit parsing to only named file(s) you could set org-people-search-type to be a list containing the path(s) to process. Otherwise all agenda-files will be read.
The configuration of the columns, within the org-people-summary view, has been expanded in recent releases. Rather than only allowing a name/width to be specified you may now add optional configuration to override the column names, the width and even the function which populates the value. This allows you to create dynamic values. For example see the last two items here:
(setq org-people-summary-properties
'((:NAME :width 25)
(:EMAIL :width 30)
:TAGS
(:PHONE :width 15 :title "Digits")
(:MEOW :getter (lambda (plist) (concat (plist-get plist :COUNTRY) " [" (plist-get plist :FLAG) "]" )))))
If you have two contacts with the same name one will overwrite the other. This is annoying, but not a bug.
This package defines a custom org-mode link-type for the org-people: protocol, which will jump to the definition of the given contact when clicked/followed. You can add such a link via C-c C-l, as expected, and you'll find TAB completion works for populating the protocol-name, and the person's name. The description will default to their name too.
A link might look like this for example:
* This is a headline
[[org-people:Steve Kemp]] wrote this package.
When exported to HTML the contact name will be converted to a hyperlink pointing to the user's :WEBSITE property, if present, otherwise it will be left unchanged.
The utility function org-people-add-descriptions will update all org-people: links within the current document to ensure the description matches the link target, which makes the display more readable.
There are a pair of functions provided for the complete-at-point functionality:
org-people-capf- Complete contact names. (e.g.
Alice Smith)
- Complete contact names. (e.g.
org-people-email-capf- Complete contact emails. (e.g.
"Mallory Jones" <foo@bar.com>").
- Complete contact emails. (e.g.
They might be enabled like so using the standard capf:
(add-hook 'message-mode-hook
(lambda ()
(add-hook 'completion-at-point-functions
#'org-people-email-capf
nil t)))
(add-hook 'text-mode-hook
(lambda ()
(add-hook 'completion-at-point-functions
#'org-people-capf
nil t)))
If you tag the contacts with more than just the contacts value then you may use those tags to build simple tables of matching entries. For example the following can auto-update:
#+NAME: get-family-contacts
#+BEGIN_SRC elisp :results value table
(org-people-tags-to-table "family")
#+END_SRC
If you prefer to include different columns in your generated table you can specify them directly:
#+NAME: get-family-contacts
#+BEGIN_SRC elisp :results value table
(org-people-tags-to-table "family" '(:LINK :PHONE))
#+END_SRC
You may also create a table including all known data about a single named individual:
#+NAME: steve-kemp
#+BEGIN_SRC elisp :results value table :colnames '("Field" "Value")
(org-people-person-to-table "Steve Kemp")
#+END_SRC
#+RESULTS: steve-kemp
| Field | Value |
|-------------+-----------------------------------------|
| Address | Helsinki, Finland |
| Category | PEOPLE |
| Country | Finland |
| Email | steve@steve.fi |
| Name | Steve Kemp |
| Phone | +358123456789 |
| Tags | (me) |
In this case properties listed in org-people-ignored-properties will be ignored and excluded from the generated table.
The org-people-summary function shows a table of all your known contacts.
You can customize the displayed fields, or their order, by modifying the org-people-summary-properties variable, as noted earlier in this documentation. The default setting is to show the name, email, phone-number and tags associated with each entry.
NOTE: If a given column would be 100% empty (i.e. no known contacts have a property with that name) then the column will be removed from display.
Some keybindings are setup in the org-people-summary-mode-map, everything will be visible if you press ?.
In brief though:
RETjump to the definition of the contact.cCopy the field under the point.fFilter the view, by property.- Even properties which are not visible can be used.
- e.g. ":ADDRESS" "Finland" will show only Finnish residents.
rreset the state of columns.Rclear the cache, reload all agenda files, and refresh the display.sInitiate a search forward, viaisearch-forward.tToggle visibility of a named column.THide the current column.v- Export the current contact, or all marked contacts, to vCARD format.C- Export the current contact, or all marked contacts, to CSV.
People may be marked with m (the current row), or M (all rows), and unmarked with u (current row), or U (all rows). There are a couple of functions which operate upon all marked rows:
- CSV Export (bound to
Cby default). - Email user(s) (bound to
eby default). - Open website(s) of user(s) (bound to
wby default). - vCARD Export (bound to
Cby default).
For simplicity you can toggle the display of known properties inline via TAB, which avoids the need to leave the view to see them. (Otherwise RET and jumping to their definition would be the thing to do.)
The org-people-summary-marked-or-current function to allow you to easily define your own custom routines that can operate either on:
- The contact on the row containing the point.
- The arbitrary number of marked people.
- Marks being set with
m/Mand cleared withu/U.
- Marks being set with
This is a brief example:
(defun show-marked-users ()
"Proof of concept to show the names of marked people.
If no people are marked show the name of the person in the row
containing the point."
(interactive)
(org-people-summary-marked-or-current
(lambda (name) (message "called with person: %s" name))))
(define-key org-people-summary-mode-map (kbd "x") #'show-marked-users)
You can run make test via the supplied Makefile to run the tests in a batch-mode, otherwise load the file org-people-test.el and run M-x eval buffer, you should see the test results in a new buffer.
If any tests fail that's a bug.
The package uses semantic versioning, and labeling.
Whenever a pull request is merged a new release is automatically tagged, and our org-people.el file is automatically updated to include the new version number.
This magic is handled via release.yml located within our github actions directory.

