App::Office::Contacts - A web-based contacts manager
A classic CGI script, contacts.cgi:
#!/usr/bin/env perl use strict; use warnings; use CGI; use CGI::Snapp::Dispatch; # --------------------- my($cgi) = CGI -> new; CGI::Snapp::Dispatch -> new -> 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/env perl # # Run with: # starman -l 127.0.0.1:5003 --workers 1 httpd/cgi-bin/office/contacts.psgi & # or, for more debug output: # plackup -l 127.0.0.1:5003 httpd/cgi-bin/office/contacts.psgi & use strict; use warnings; use CGI::Snapp::Dispatch; use Plack::Builder; # --------------------- my($app) = CGI::Snapp::Dispatch -> new -> as_psgi ( prefix => 'App::Office::Contacts::Controller', table => [ '' => {app => 'Initialize', rm => 'display'}, ':app' => {rm => 'display'}, ':app/:rm/:id?' => {}, ], ); builder { enable "ContentLength"; enable 'Static', path => qr!^/(assets|favicon)!, root => '/dev/shm/html'; $app; };
The scripts discussed here, contacts.cgi and contacts.psgi, are shipped with this module, in the httpd/ directory.
For more on Plack, see My intro to Plack.
App::Office::Contacts implements a utf8-aware, web-based, private and group contacts manager.
App::Office::Contacts
Here 'private' means you can specify which contacts are not to appear in the search results of other people using the same database. You do this by setting their visibility to 'Just me'.
App::Office::Contacts uses the light-weight module Moo.
Major features:
These are displayed with the most recent notes first.
This is controlled via a config file.
4 was chosen just to limit the amount of screen real estate occupied. It can be easily changed.
For example, if you add a person to the staff list for an organization, and the details for that person are on another, hidden, tab (the organization tab must have the focus), then the list of occupations for that peson is updated as soon as they are added.
The list of fields which support autocomplete are listed both on the appropriate forms and on the default FAQ page.
But App::Office::Contacts::Donations has not yet been updated to match V 2.00 of App::Office::Contacts.
Screen shots:
The database schema.
Sample search results.
Sample personal details. The organizational details form is very similar.
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, share/.htoffice.contacts.conf, shipped with this distro.
share/.htoffice.contacts.conf
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 you have installed this config file, by running:
shell> perl scripts/copy.config.pl
The module which reads the file is App::Office::Contacts::Util::Config.
scripts/copy.config.pl installs .htoffice.contacts.conf into a shared directory.
.htoffice.contacts.conf
So, if you unpack the distro and edit the file within the unpacked code, you will still need to copy the patched version by running:
shell> perl scripts/copy.config.pl shell> perl scripts/find.config.pl (as a cross-check)
Alternately, edit the installed copy rather than the copy shipped with the distro.
There is no need to restart your web server after updating this file.
This module does not ship with any of these Javascript libraries. You can get them from:
http://jquery.com/ http://jqueryui.com/ http://datatables.net/
Most development was done using jQuery V 1.8.1, which ships with jQuery V 1.9.2. Lastly, DataTables V 1.9.4 was used too.
See share/.htoffice.contacts.conf, around lines 23 .. 25 and 61 .. 63, where it specifies the URLs used by the code to access these libs.
As always, do this after patching the config file:
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 share/.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:
cpanm App::Office::Contacts
or run
sudo cpan App::Office::Contacts
or unpack the distro, and then either:
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.
Copy the htdocs/assets/ directory, and all its subdirectories, from the distro to the doc root directory of your web server.
htdocs/assets/
Specifically, my doc root is /dev/shm/html/, so I end up with /dev/shm/html/assets/.
/dev/shm/html/
/dev/shm/html/assets/
Next, tell App::Office::Contacts your values for some options. This includes the path to the files used by Text::Xslate.
For that, see share/.htoffice.contacts.conf as discussed above.
After editing the config file, ensure you run scripts/copy.config.pl. It will copy the config file using File::ShareDir, to a directory where the run-time code in App::Office::Contacts will look for it.
scripts/copy.config.pl
shell>cd App-Office-Contacts-1.00 shell>perl scripts/copy.config.pl shell>perl scripts/find.config.pl
In share/.htoffice.contacts.conf there is a line:
program_faq_url = /assets/templates/app/office/contacts/faq.html
This page is displayed when the user clicks FAQ on the About tab.
A sample page is shipped in htdocs/assets/templates/app/office/contacts/faq.html.
htdocs/assets/templates/app/office/contacts/faq.html
So, copying the htdocs/assets/ directory, as above, will have installed this file. Alternately, replace it with your own.
As always after editing the config file, run:
Copy the httpd/cgi-bin/office/ directory to the cgi-bin/ directory of your web server, and make contacts.cgi executable.
httpd/cgi-bin/office/
cgi-bin/
My cgi-bin/ dir is /usr/lib/cgi-bin/, so I end up with /usr/lib/cgi-bin/office/contacts.cgi.
/usr/lib/cgi-bin/
/usr/lib/cgi-bin/office/contacts.cgi
Now I can run http://127.0.0.1/cgi-bin/office/contacts.cgi (but not yet!).
http://127.0.0.1/cgi-bin/office/contacts.cgi
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 App-Office-Contacts-1.00 # Naturally, you only drop /pre-existing/ tables :-), # so use drop.tables.pl later, when re-building the db. #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
Notes:
That is, Perl does not use the installed version of the code, if any.
If you unpack the distro, and run:
it will copy the config file to the install dir, and report where it is.
So, if you leave out the '-Ilib', Perl will use the version of the code which has been formally installed.
Point your broswer at http://127.0.0.1/cgi-bin/contacts.cgi.
http://127.0.0.1/cgi-bin/contacts.cgi
Your first search can then be just 'a', without the quotes.
Creates a HTML table for the About tab.
Note: The code does not currently use $user_id. It is present as provision if the code is patched to identify logged-on users. See the "FAQ" for a discussion of this issue.
Creates the basic web page in response to the very first request from the user.
Contains code shared by this module and App::Office::Contacts::Donations.
Shuts down database connexions, etc, as the program is exiting.
Search for them, and then set their visibility to No-one. Hence they stay in the database but are no longer visible.
Yes. Text::CSV::Encoded is used in App::Office::Contacts::Util::Import to read data/fake.people.txt.
App::Office::Contacts::Util::Import
See "Creating and populating the database" for a discussion of scripts/populate.fake.people.pl.
Do a search for Brocard, the author of the original GraphViz, and you will find Léon Brocard.
Also, see lines 48 .. 52 in the config file for options to control the utf8 setting in the connect() attributes as used by DBI. These are the defaults:
mysql_enable_utf8 = 1 # pg_enable_utf8 == 0 for DBD::Pg V 3.0.0 in my code. pg_enable_utf8 = 0 sqlite_unicode = 1
These values are used in App::Office::Contacts::Util::Logger lines 44 .. 57.
This is under consideration.
You cannot. I have not yet decided how to provide an on-screen mechanism to update this table.
MVC (Model-View-Controller).
The sample scripts contacts.cgi and contacts use
prefix => 'App::Office::Contacts::Controller'
so the files in lib/App/Office/Contacts/Controller and lib/App/Office/Contacts/Controller/Exporter are the modules which are run to respond to http requests.
lib/App/Office/Contacts/Controller
lib/App/Office/Contacts/Controller/Exporter
Files in lib/App/Office/Contacts/View implement views, and those in lib/App/Office/Contacts/Database implement the model.
lib/App/Office/Contacts/View
lib/App/Office/Contacts/Database
Files in lib/App/Office/Contacts/Util are a mixture:
lib/App/Office/Contacts/Util
This is used by all code.
This is just used to create tables, populate them, and drop them.
Hence it will not 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::Note and App::Office::Contacts::Donations::Controller::Note, could share a lot of code, but they had incompatible parents. Sub::Exporter solved this problem.
And since Controller.pm is derived from CGI::Snapp and not Moo, we cannot use Moo::Role.
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.
The file was created with dbigraph.pl.
dbigraph.pl ships with GraphViz::DBI. I patched it to use GraphViz::DBI::General.
GraphViz::DBI
GraphViz::DBI::General
The command is:
dbigraph.pl --dsn 'dbi:Pg:dbname=contacts' --user contact --pass contact > docs/contacts.schema.png
The username and password are as shipped in share/.htapp.office.contacts.conf.
share/.htapp.office.contacts.conf
Because the search feature always uses upper-case. And, e.g., phones can have eXtension information built-in, as in '123456x78'. So the 'x' in a search request needs to be upper-cased. And yes, I have worked on a personnel + phone number system (at Monash University) which stores (Malaysian) phone numbers like that.
The case for email addresses is rather more obvious.
The code is DBI-based, of course.
Also, the code assumes the database server supports $dbh -> last_insert_id(undef, undef, $table_name, undef).
Engine type defaults to innodb when you use MySQL in the dsn.
See share/.htapp.office.contacts.conf for the dsn and the source code of App::Office::Contacts::Util::Create for the create statements.
Do all of these things:
App::Office::Contacts::Util::Create
You will need code to create, drop and (perhaps) populate your new table.
There are many examples already in that module.
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 file names. Hence the '_' in the names of their data files.
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 database.
The licence says the list cannot be passed on in its original format, but encoding it with DBD::SQLite solves that problem :-).
Not specifically, although a huge range of labels is supported via PostScript::MailLabels.
Printing might one day be shipped as App::Office::Contacts::Export::StickyLabels.
App::Office::Contacts::Export::StickyLabels
Ahhh, you have 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 is 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.
Nothing. Very early versions of the code reserved this id, but that is not done now.
In a similar manner (to Person id == 1), there is a special occupation title with id == 1, whose name is '-'.
This allow you to indicate someone works for a given organization without knowing exactly what their job is.
You can search for all such special code with 'ack Special'. ack is part of App::Ack.
Do not delete this occupation! It is needed. The delete/update occupation code checks to ensure you do not delete it with this module, but of course there is always the possibility that you delete it using some other tool.
In a similar manner (to Occupation id == 1), there is a special organization with id == 1, whose name is '-'.
Do not delete this organization! It is needed. The delete/update organization code checks to ensure you do not delete it with this module, but of course there is always the possibility that you delete it using some other tool.
Their names match "data/fake.$table_name.txt".
Some CPAN testers test with users who do not have home directories.
2. One for DBIx::Simple (See App::Office::Contacts::Util::Logger), which is used throughout App::Office::Contacts::Database, and one for Log::Handler::Output::DBI (for which also see App::Office::Contacts::Util::Logger), which is used just for logging.
All scripts are shipped in the scripts/ directory.
This compares the CGI form field names in the add_org_form CGI form to their equivalents in the Javascript in htdocs/assets/templates/app/office/contacts/homepage.tx, and reports discrepancies.
The form is shipped in docs/add.organization.form.html which I copied from the web page displayed when the program starts.
This just prints the output of a HTML template, to help debugging.
This copy share/.htapp.office.contactcs.conf to a shared directory, as per the dist_dir() method in File::ShareDir.
This creates the database tables. See "Creating and populating the database".
This drops the database tables. See "Creating and populating the database".
This exports just the name and upper-case name from the people table. This is not really useful, but does provide a template if you wish to expand the code.
It outputs to the file specified by the output_file option.
It has a standalone_page option for using either a web page or just a table as the template.
standalone_page
It outputs a string.
This tells you where share/.htapp.office.contactcs.conf is installed, after running copy.config.pl.
A bash script which runs a set of programs.
Warning: This includes drop.tables.pl.
This populates some database tables. See "Creating and populating the database".
This populates vital database tables. See "Creating and populating the database".
This helps me fight the dread utf8.
This prints singly- and doubly-encoded and decoded string, as a debugging aid.
This will be replaced with a menu when donations are re-implemented in V 2.00 of *::Donations.
Currently, the focus goes to the Reset button and not the Add button in these cases (Occupation and Staff).
The error message is lost in these cases, and I cannot explain that.
See View::*::format_*().
The 2 enclosing divs in basic.table.tx could be optional, perhaps via a separate template.
Code could be shifted into Database::*::save_*().
Done.
This provides N sites per person or organization.
The country/state/locality/postcode (zipcode) data will be shipped in SQLite format, as part of this module.
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 is another SQLite database.
Email the author, or log a bug on RT:
https://rt.cpan.org/Public/Dist/Display.html?Name=App-Office-Contacts.
App::Office::Contacts was written by Ron Savage <ron@savage.net.au> in 2009.
Home page.
Australian copyright (c) 2013, 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 V 2, 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
CPAN shell
perl -MCPAN -e shell install App::Office::Contacts
For more information on module installation, please visit the detailed CPAN module installation guide.