App::Office::Contacts - A web-based contacts manager
App::Office::Contacts
The scripts discussed here, contacts.cgi and contacts.psgi, are shipped with this module.
A classic CGI script, contacts.cgi:
use strict; use warnings; use CGI; use CGI::Application::Dispatch; # --------------------- my($cgi) = CGI -> new; CGI::Application::Dispatch -> dispatch ( args_to_new => {QUERY => $cgi}, prefix => 'App::Office::Contacts::Controller', table => [ '' => {app => 'Initialize', rm => 'display'}, ':app' => {rm => 'display'}, ':app/:rm/:id?' => {}, ], );
A Plack script, contacts.psgi:
#!/usr/bin/perl use strict; use warnings; use CGI::Application::Dispatch::PSGI; use Plack::Builder; # --------------------- my($app) = CGI::Application::Dispatch -> as_psgi ( prefix => 'App::Office::Contacts::Controller', table => [ '' => {app => 'Initialize', rm => 'display'}, ':app' => {rm => 'display'}, ':app/:rm/:id?' => {}, ], ); builder { enable "Plack::Middleware::Static", path => qr!^/(assets|yui)/!, root => '/var/www'; $app; };
For more on Plack, see My intro to Plack.
App::Office::Contacts implements a web-based, private and group, contacts manager.
App::Office::Contacts uses Moose.
Moose
Once such a structure is in place, then we can have multiple sites per organization, or multiple occupations per person, or multiple donations per entity (person or organization).
For the latter, see App::Office::Contacts::Donations.
App::Office::Contacts::Donations
This module is available as a Unix-style distro (*.tgz).
See http://savage.net.au/Perl-modules/html/installing-a-module.html for help on unpacking and installing distros.
At various places I refer to a file, lib/CGI/Office/Contacts/.htoffice.contacts.conf, shipped in this distro.
Please realize that if you edit this file, you must ensure the copy you are editing is the one used by the code at run-time.
After a module such as this is installed, the code will look for that file in the directory where Build.PL or Makefile.PL has installed the code.
The module which reads the file is App::Office::Contacts::Util::Config.
App::Office::Contacts::Util::Config
Both Build.PL or Makefile.PL install .htoffice.contacts.conf along with the Perl modules.
So, if you unpack the distro and edit the file within the unpacked code, you'll still need to copy the patched version into the installed code's directory structure.
There is no need to restart your web server after updating this file.
This module does not ship with YUI. You can get it from:
http://developer.yahoo.com/yui
Most development was done using V 2.8.0r4. The original work was done with an earlier version of YUI.
Currently, I have no plans to port this code to V 3 of YUI.
See lib/CGI/Office/Contacts/.htoffice.contacts.conf, around line 70, where it specifies the URL used by the code to access the YUI.
I use Postgres.
So, I create a user and a database, via psql, using:
shell>psql -U postgres psql>create role contact login password 'contact'; psql>create database contacts owner contact encoding 'UTF8'; psql>\q
Then, to view the database after using the shipped Perl scripts to create and populate it:
shell>psql -U contact contacts (password...) psql>...
If you use another server, patch lib/CGI/Office/Contacts/.htoffice.contacts.conf, around lines 22 and 36, where it specifies the database DSN and the CGI::Session driver.
Install App::Office::Contacts as you would for any Perl module:
Perl
Run cpan: shell>sudo cpan App::Office::Contacts
or unpack the distro, and then either:
perl Build.PL ./Build ./Build test sudo ./Build install
or:
perl Makefile.PL make (or dmake) make test make install
Either way, you need to install all the other files which are shipped in the distro.
HTML::Template
Copy the distro's htdocs/assets/ directory to your web server's doc root.
Specifically, my doc root is /var/www/, so I end up with /var/www/assets/.
In lib/CGI/Office/Contacts/.htoffice.contacts.conf there is a line:
program_faq_url=/contacts.faq.html
This page is displayed when the user clicks FAQ on the About tab.
A sample page is shipped in docs/html/contacts.faq.html. It has been built from docs/pod/contacts.faq.pod.
So, copy the latter into your web server's doc root, or generate another version of the page, using docs/pod/contacts.faq.pod as input.
Copy the distro's httpd/cgi-bin/office/ directory to your web server's cgi-bin/ directory, and make contacts.cgi executable.
My cgi-bin/ dir is /usr/lib/cgi-bin/, so I end up with /usr/lib/cgi-bin/office/contacts.cgi.
Now I can run http://127.0.0.1/cgi-bin/office/contacts.cgi (but not yet!).
The distro contains a set of text files which are used to populate constant tables. All such data is in the data/ directory.
This data is loaded into the 'contacts' database using programs in the distro. All such programs are in the scripts/ directory.
After unpacking the distro, create and populate the database:
shell>cd CGI-Office-Contacts-1.00 shell>perl -Ilib scripts/drop.tables.pl -v shell>perl -Ilib scripts/create.tables.pl -v shell>perl -Ilib scripts/populate.tables.pl -v shell>perl -Ilib scripts/populate.fake.data.pl -v shell>perl -Ilib scripts/report.tables.pl -v
Note: The '-Ilib' means 2 things:
That is, Perl does not use the installed version of the code, if any.
That is, it does not use the installed version of this file, if any.
So, if you leave out the '-Ilib', Perl will use the version of the code which has been formally installed, and then the code will look in the same place for .htoffice.contacts.conf.
Point your broswer at http://127.0.0.1/cgi-bin/contacts.cgi.
Your first search can then be just 'a', without the quotes.
This code has been written, and runs in a private module, Local::Contacts.
Local::Contacts
Local::Contacts actually contains all of App::Office::Contacts, including donations, importing vCards, occupations, sites and a start to sticky label printing.
Local::Contacts has been re-written to split it into several modules, which are being released one at a time, and to remove the Apache-specific code, and to start using REST [1] as a way of structuring path infos.
[1] http://en.wikipedia.org/wiki/Representational_State_Transfer
App::Office::Contacts::Donations will be released shortly.
App::Office::Contacts::Export::StickyLabels
Much of this code has already been written, but is not yet grafted in from Local::Contacts.
See scripts/mail.labels.pl (not yet shipped) for details. This program creates data/label_brands.txt and data/label_codes.txt.
These text files are then imported when running scripts/populate.tables.pl.
App::Office::Contacts::Export::StickyLabels will be released shortly.
App::Office::Contacts::Import::vCards
This code has also been written, but is not yet grafted in from Local::Contacts.
App::Office::Contacts::Import::vCards will be released shortly.
App::Office::Contacts::Sites
The country/state/locality/postcode (zipcode) data will be shipped in SQLite format, as part of App::Office::Contacts::Sites.
Data for Australia and America with be included in the distro.
Note: The country/etc data is imported into whatever database you choose to use for your contacts database, even if that's another SQLite database.
App::Office::Contacts::Sites will be released shortly.
Lastly, the occupations per person code is not being shipped yet.
Nope, wrong again. These subs are meant to be called twice.
ron@zoe:~/perl.modules/CGI-Office-Contacts$ ack build_notes_js lib/CGI/Office/Contacts/Controller/Initialize.pm 104: my($organization_notes_js) = $self -> param('view') -> notes -> build_notes_js('organization'); 105: my($person_notes_js) = $self -> param('view') -> notes -> build_notes_js('person');
This applies to build_donations_js and build_notes_js, at lease.
Perhaps you have not dropped and created the tables properly.
You should edit these files (all in the App::Office::Contacts::Donations scripts/ directory), to suit yourself, and then run them in this order:
This last one is for testing only, of course.
The thing to note is that scripts/populate.all, when processing the reports table, only adds records which are not there already. It can do this because data/reports.txt for App::Office::Contacts contains 1 record ('Records'), but data/reports.txt for App::Office::Contacts::Donations only contains records pertaining to donations.
This still means that the entries in the reports table must be names exactly as expected by the if statement in report.js, function report_onsubmit(). You have been warned.
The corresponding Perl code is in App::Office::Contacts::View::Report and App::Office::Contacts::Donations::View::Report.
App::Office::Contacts::View::Report
App::Office::Contacts::Donations::View::Report
Ahh, yes. That is a design fault.
MVC (Model-View-Controller).
The sample scripts contacts.cgi and contacts use
prefix => 'App::Office::Contacts::Controller'
so the files in lib/CGI/Office/Contacts/Controller are the modules which are run to respond to http requests.
Files in lib/CGI/Office/Contacts/View implement views, and those in lib/CGI/Office/Contacts/Database implement the model.
Files in lib/CGI/Office/Contacts/Util are a mixture:
This is used by all code.
This is just used to create tables, populate them, and drop them.
Hence it won't be used by CGI scripts, unless you write such a script yourself.
CGI
This is used to validate CGI form data.
The way I wrote the code, various pairs of classes, e.g. App::Office::Contacts::Controller::Notes and App::Office::Contacts::Donations::Controller::Notes, could share a lot of code, but they had incompatible parents. Sub::Exporter solved this problem.
App::Office::Contacts::Controller::Notes
App::Office::Contacts::Donations::Controller::Notes
It may happen that one day the code is restructured to solve this differently.
Yes I do. I think in terms of the nature of each element, not the storage mechanism.
I have switched to plurals for the names of database tables though.
See docs/contacts.schema.png.
The file was created with scripts/schema.sh, which uses dbigraph.pl.
dbigraph.pl ships with GraphViz::DBI. I patched it to use GraphViz::DBI::General.
GraphViz::DBI
GraphViz::DBI::General
The code is DBI-based, of course.
Also, the code assumes the database server supports $dbh -> last_insert_id(undef, undef, $table_name, undef).
Do all of these things:
The table names in this file are stored in the table called table_names, in the order read in from this file.
Do not change the order of records in this file if you are going to update the table_names table without recreating the database.
The code has to know which table has which id in that table (table_names), so that donations, notes and sites can be associated with the correct table, and with the correct id within that table.
App::Office::Contacts::Util::Create
You'll need code to create, drop and (perhaps) populate your new table.
There are many examples already in that module.
Do this with a one-off SQL statement, or by following the instructions above about creating and populating the database.
Programs are shipped in scripts/, and data files in data/.
I prefer to use '.' to separate words in the names of programs.
However, for database table names, I use '_' in case '.' would case problems.
Programs such as mail.labels.pl and populate.tables.pl, use table names for their data files' names. Hence the '_' in the names of their data files.
These buttons are at the bottom of the detail forms for entities (i.e. People and Organizations).
They are deliberately in (English) alphabetical order, left-to-right.
So, if the Donations add-on is installed, the Donations button will be between the Delete and Notes buttons.
In Australia, a list of localities and postcodes is available from http://www1.auspost.com.au/postcodes/.
In America, you can buy a list from companies such as http://www.zipcodeworld.com/index.htm, who are an official re-seller of US Mail's database.
Subsets of the entities can be selected for printing to sticky labels.
A huge range of labels is supported via PostScript::MailLabels.
Printing will be shipped as App::Office::Contacts::Export::StickyLabels.
No, that would be an unproductive use of my time.
Other such libraries might do a good job, but I don't believe they'll do a better job.
I have published a review of various Javascript libraries [1], and IMHO YUI is the best.
[1] http://use.perl.org/~Ron+Savage/journal/37726
Ahhh, you've been reading the source code, eh? Well done!
Originally (i.e. in my home-use module Local::Contacts), users had to log on to use this code.
So, there was a known user at all times, and the modules used user_id to identify that user.
Then, when records in (some) tables were created, the value of user_id was stored in the creator_id field.
Now I take the view that you should implement Single Sign-on, meaning this set of modules is never responsible to tracking who's logged on.
Hence this line in App::Office::Contacts::Controller:
App::Office::Contacts::Controller
$self -> param(user_id => 0); # 0 means we don't have anyone logged on.
That in turn means there is now no knowledge of the id of the user who is logged on, if any.
To match this, various table definitions have been changed, so that instead of App::Office::Contacts::Util::Create using:
creator_id integer not null, references people(id),
the code says:
creator_id integer not null,
This allows a user_id of 0 to be stored in those tables.
Also, the transaction logging code (since deleted) could identify the user who made each edit.
Originally, I stored my own name in the people table, with an id of 1. Well, this was good for testing, if nothing else.
And there was code in App::Office::Contacts::Database::Person, sub get_people(), to ensure searches would return my name, when it matched the search key.
App::Office::Contacts::Database::Person
In other places, updates and deletes of the person with id == 1 were forbidden.
Also, code in App::Office::Contacts::Database::Organization, sub get_organizations(), ensured that when I was logged on, my searches would return all organizations which matched the search key.
App::Office::Contacts::Database::Organization
The effect was to override the code which implemented private address books. And this was for the purpose of providing support for users of the code.
Such code has been commented out. I did not delete it in case it needs to be re-activated in certain circumstances.
I suggest, now, that the Help tab should point to a web page giving details of whatever support you offer.
In a similar manner (to Person id == 1), there is a special organization with id == 1, whose name is '-'.
Code relating to this organization has not been commented out.
Do not delete this organization! It is needed.
You can search for all such special code with 'ack Special'. ack is part of App::Ack.
Log bug reports with RT.
The mailing list details are:
Mail list: cgi-office@X Help address: cgi-office-help@X Subscription address: cgi-office-subscribe@X Unsubscription address: cgi-office-unsubscribe@X
where X is as per my email address at the bottom of my home page (below).
On-line help for ezmlm: http://www.ezmlm.org/manual/
App::Office::Contacts was written by Ron Savage <ron@savage.net.au> in 2009.
Home page: http://savage.net.au/index.html
Australian copyright (c) 2009, Ron Savage. All Programs of mine are 'OSI Certified Open Source Software'; you can redistribute them and/or modify them under the terms of The Artistic License, a copy of which is available at: http://www.opensource.org/licenses/index.html
To install App::Office::Contacts, copy and paste the appropriate command in to your terminal.
cpanm
cpanm App::Office::Contacts
CPAN shell
perl -MCPAN -e shell install App::Office::Contacts
For more information on module installation, please visit the detailed CPAN module installation guide.