NAME
App::Office::CMS - The Canny, Microlight and Simple CMS
Synopsis
The scripts discussed here, cms.cgi and cms.psgi, are shipped with this module.
A classic CGI script, cms.cgi:
#!/usr/bin/perl
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::CMS::Controller',
table =>
[
'' => {app => 'Initialize', rm => 'display'},
':app' => {rm => 'display'},
':app/:rm/:id?' => {},
],
);
A Plack script, cms.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::CMS::Controller',
table =>
[
'' => {app => 'Initialize', rm => 'display'},
':app' => {rm => 'display'},
':app/:rm/:id?' => {},
],
);
builder
{
enable "Static",
path => qr!^/(assets|favicon|yui)/!,
root => '/var/www';
$app;
};
For more on Plack, see my intro to Plack: http://savage.net.au/Perl/html/plack.for.beginners.html.
Description
App::Office::CMS is the Canny, Microlight and Simple Content Management System.
- o Canny
-
The canniness comes in observing that almost all web sites have a menu for navigation.
So, this module gives you (the content manager) a menu designer.
The idea is that the menu you design is 'live', so you use the web site's own menu to design both the menu itself and the whole web site.
- o Microlight
-
This means Any::Moose to get Mouse rather than Moose.
For the same reason, DBIx::Simple rather than DBIx::Class.
And hence Brannigan rather than Data::Verifier, because the latter uses Moose::Util::TypeConstraints, and hence a whole slew of Moose::Meta::* modules.
Lastly, I'm not claiming this package is as small as it could possibly be. For example, I could replace Text::Xslate with HTML::Template to shrink it, but I don't want to do that.
- o Simple
-
The other motivation is to deliver an application which anyone, with minimal training, can master.
Overview
Up to V 1.00, 99.99% of the effort went into the structure of the code, i.e. the mangement of a web site, and not into supporting content generation.
That's why the features provided for generating content are minimalistic.
The end-user docs are discussed below, under FAQ, and are strongly recommended reading.
This is a simplified version:
- o Multiple web sites
-
You can create any number of web sites.
- o Multiple designs per web site
-
Each web site can have any number of designs.
Every design has a menu of web pages.
- o Multiple web pages per design
-
As expected, each design consists of any number of web pages.
- o Auto-generation of web pages and assets.
-
When you create a new site and design, a default web page is created.
This also happens if you change the name of a web site or of a design, and click Save.
This web page has a default asset (output file template), and default (empty) content.
- o Why is the name of the home page in the config file?
-
To make it easy to edit!
Also note that there are 2 template files shipped with this distro, one for the home page and one for any other page. These templates are not fancy, they are just there as guidelines for how you should develop your own templates.
See htdocs/assets/templates/app/office/cms/page.templates/*.tx for the templates.
See also data/asset_types.txt for the other place template information is stored.
- o Web page attributes
-
So far, each web page is given a small number of attributes, including the template to be used to create the corresponding output web page (at generation time).
Using the Javascript editor, you create the content for each page.
- o Output
-
Web pages are generated by pouring the content into the template.
Two (2) sample templates are provided.
- o Configuration
-
See lib/App/Office/CMS/.htoffice.cms.conf.
- o Errors
-
As far as possible, Try::Tiny is used to catch non-DBI errors.
DBI errors are caught using the HandleError attribute key in the call to DBI's connect() method.
Security
Minimal effort has been made to sanitize error messages, so there's a risk that information you don't wish to leak out may be displayed on the end-user's screen.
Feel free to recommend changes in this area.
CGI form field data is passed thru CGI::Untaint and, optionally, HTML::Defang.
Distributions
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.
Installation Pre-requisites
The Yahoo User Interface (YUI)
This module does not ship with YUI. You can get it from: http://developer.yahoo.com/yui.
All development was done using V 2.8.1.
Currently, I have no plans to port this code to V 3 of YUI.
See also lib/App/Office/CMS/.htoffice.cms.conf, where it specifies the URL used by the code to find YUI's JavaScript files.
The output templates use these 5 YUI files:
- o CSS: yui/build/treeview/assets/skins/sam/treeview.css
- o JS: yui/build/yahoo/yahoo-min.js
- o JS: yui/build/dom/dom-min.js
- o JS: yui/build/event/event-min.js
- o JS: yui/build/treeview/treeview.js
More on Configuration
At various places I refer to a file, lib/App/Office/CMS/.htoffice.cms.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::CMS::Util::Config
.
Both Build.PL or Makefile.PL install .htoffice.cms.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.
Creating the database
OK, here we go...
I use SQLite because it's shipped with recent versions of Perl.
Running scripts/create.tables.pl (below) will create the database.
If you use Postgres, do this to create the database:
shell>psql -U postgres
psql>create role cms login password 'cms';
psql>create database cms owner cms encoding 'UTF8';
psql>\q
Creating and populating the database tables
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 database using programs in the distro. All such programs are in the scripts/ directory.
After unpacking the distro, create and populate the database thus:
shell>cd App-Office-CMS-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/report.tables.pl -v
See also scripts/new.db.sh.
Note: The '-Ilib' means 2 things:
- o Perl looks in the current directory structure for the modules
-
That is, Perl does not use the installed version of the code, if any.
- o The code looks in the current directory structure for .htoffice.cms.conf
-
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 previously installed, and then the code will look in the same place for .htoffice.cms.conf.
Installing the module
Install App::Office::CMS as you would for any Perl
module:
Run:
cpanm App::Office::CMS
or run:
sudo cpan App::Office::CMS
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.
Install the Text::Xslate (HTML and Javascript) template files
Copy the distro's htdocs/assets/ directory to your web server's doc root.
Specifically, my doc root is /dev/shm/html, so I end up with /dev/shm/html/assets/.
/dev/shm is Debian's RAM disk. Your doc root might be /var/www, or even /var/www/html.
Install the FAQ web page
This FAQ is for using App::Office::CMS
via its CGI scripts, not for the generated web site.
In lib/App/Office/CMS/.htoffice.cms.conf there is a line:
program_faq_url=/assets/templates/app/office/cms/cms.help.html
This page is displayed when the user clicks FAQ on the About tab.
A sample page is shipped in docs/html/cms.help.html. It has been built from docs/pod/cms.help.pod (by running a script I wrote, pod2html.pl, which in turn is a simple wrapper around Pod::Simple::HTML).
So, copy the sample HTML file into your web server's doc root, or generate another version of the page, using docs/pod/cms.faq.pod as input.
Install the trivial CGI script and the Plack script
Copy the distro's httpd/cgi-bin/office/ directory to your web server's cgi-bin/ directory, and make cms.cgi executable.
If I used Apache, my cgi-bin/ dir would be /usr/lib/cgi-bin/, so I would end up with /usr/lib/cgi-bin/office/cms.cgi.
Actually, I run nginx (Engine X) http://wiki.nginx.org/Main, which does not serve CGI scripts, and tiny (thttpd) http://acme.com/software/thttpd/, which does.
See http://wiki.nginx.org/ThttpdCGI for patching tiny.
See also http://savage.net.au/Perl/html/env.report.html.
Start testing
Try:
starman -l 127.0.0.1:5006 --workers 1 httpd/cgi-bin/office/cms.psgi &
Or, for good debug output:
plackup -l 127.0.0.1:5006 httpd/cgi-bin/office/cms.psgi &
Or, install cms.cgi and point your browser at:
http://127.0.0.1/cgi-bin/cms.cgi.
The Generate Button
Mostly, you'll be working on the Edit Content tab, so that's where the [Generate] button is.
Clicking [Generate] generates all of the pages in the current design. You do not have to be editing content for the home page to use the [Generate] button.
The disk directory structure created matches the tree structure you have created via the Edit Pages tab.
So, if you created a page called Offices, and under that 2 children called Locations and Staff, then these files will be output:
There are various things to note:
- o Case: File and Directory names are output in lower case
-
This conversion is done with String::Dirify.
- o Hierarchy: Child nodes are in a sub-directory under their parent
-
This structure is generated by tracing the ancestors of each page up the tree of pages, to the root (but excluding the root), and using the parent, grand-parent, etc, names as directory names.
- o Root directory
-
When a site is created, you specify an output directory, and the directories mentioned in the previous items are created under the site's directory.
So, if this site's output directory was in Debian's RAM disk at /dev/shm/html, then the output files' full paths would be:
- o /dev/shm/html/offices.html
- o /dev/shm/html/offices/locations.html
- o /dev/shm/html/offices/staff.html
Path::Class is used to construct these paths in an OS-independent manner, which means you must provide the site's output directory in a format suitable for your OS.
FAQ
- o This module doesn't really create entire web sites!
-
True, but since it's Open Source, you can extend it yourself.
- o But why don't you add millions of features!
-
I can give you 3 reasons for that: Canny, Microlight and Simple.
- o Can I have 2 pages with the same name?
-
No. When user input is being checked, the page is searched for by name, and a match means the new input is an update for the existing page.
- o How is the code structured?
-
MVC (Model-View-Controller).
The sample scripts cms.cgi and cms.psgi use
prefix => 'App::Office::CMS::Controller'
so the files in lib/App/Office/CMS/Controller are the modules which are run to respond to http requests.
Files in lib/App/Office/CMS/View implement views, and those in lib/App/Office/CMS/Database implement the model.
Files in lib/App/Office/CMS/Util are a mixture:
- o Config.pm
-
This is used by all code.
- o Create.pm
-
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.
- o Logger.pm
-
This simplifies logging by allowing me to say:
$self -> log(debug => 'A message'); $self -> log(info => 'Another message');
- o Validator.pm
-
This is used to validate CGI form data.
- o What's the database schema?
-
See docs/cms.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.
- o Does the database server have pre-requisites?
-
The code is DBI-based, of course.
Also, the code assumes the database server supports $dbh -> last_insert_id(undef, undef, $table_name, undef).
- o How do I back up the database?
-
See the config file .htoffice.cms.conf:
backup_command = pg_dump -U cms cms backup_file = /tmp/pg.cms.backup.dat
When backup_command has a value, the Edit Contents tab gets a [Backup] button, and when this button is clicked:
- o The command is run
- o STDOUT and STDERR are captured
- o If STDERR contains anything, the program exits
- o Otherwise, STDOUT is written to the output file
So, why are there 2 lines, and not something like 'pg_dump -U cms cms > /tmp/pg.cms.backup.dat'?
Because I use Capture::Tiny, which does not want you to use redirection.
Lastly, the output is written using File::Slurp.
-
It's the value ("$site_id/$design_id") which ties those 2 tables together, just like a foreign key.
-
I originally allowed the user to select a horizontal or vertical menu format, when generating pages.
The vertical menu is just the tree I ended up with.
I decided not to support horizontal menus, at least in the short term, because of the width of such a menu when the page names became long. It would have appeared just above the site map on the Edit Pages tab, and would have been clickable in the same way the site map tree is.
The os_type_id is for the unsupported code which generates pages in a directory structure for an OS different from the one the code is running on.
You can ignore these 2 fields, and the other 2 tables, menu_orientations and os_types.
- o I added a field to update_page_form, but it's data vanishes.
-
Basdically, copy the code dealing with 'homepage'.
At the very least, ensure you've updated:
- o App::Office::CMS::Util::Create.create_pages_table()
- o App::Office::CMS::Database.build_default_page()
- o App::Office::CMS::Controller::Page
- o App::Office::CMS::Util::Validator.validate_page()
- o App::Office::CMS::Database::Page.save_page_record()
- o App::Office::CMS::View::Page.build_update_page_html()
For site data, start with App::Office::CMS::Controller.build_site_hash().
- o How to I replace the fundamental editing functionality?
-
Simply.
-
This is in site.tx. It's id is submit_edit_design. Leave it as is.
- o The 'Edit design' Javascript
-
Clicking [Edit design] triggers a call to update_site_onsubmit(). This is in site.js.
- o Calling the update_page_callback() function (1 of 2)
-
This is called from within update_site_onsubmit(). The call is in site.js.
The path info is 'page/edit', which is added to the base '$form_action/'.
As per CGI::Application::Dispatch, this calls
App::Office::Controller::Page
's edit() method.Change this path info to call a different module. So, 'custom_page/my_edit' will call
App::Office::Controller::CustomPage
's my_edit() method. - o CustomPage.pm
-
You write *::Controller::CustomPage.pm, based (obviously) on *::Controller::Page.pm.
You can still let *::View::Page process your updated page.tx and page.js templates. See below for details.
- o Validator.pm
-
This is
App::Office::CMS::Util::Validator
. You'll have to edit the validate_design() method. - o The declaration of update_page_callback() (2 of 2)
-
This is in page.js. This is the Javascript function which receives the output of CustomPage.pm.
Replace the code in this function as needed.
- o *::View::Page
-
You'll need to patch the build_head_js() and build_update_page_html() methods in this class.
- o page.tx
-
This is populated by code in the *::View::Page::build_update_page_html() method.
- o page.js
-
This is populated by code in *::View::Page::build_head_js() method.
- o Site attributes 'v' Design attributes
-
When a site/design is submitted for saving, some attributes are saved with the site, and some with the design.
If writing to the database fails, it's probably because the attributes for the design were not copied from the CGI form fields.
Check
App::Office::CMS::Database::Design
's site2design() method.
-
- o Please explain the program, text file, and database table names
-
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 create.tables.pl and populate.tables.pl, use table names for their data files' names. Hence the '_' in the names of their data files.
- o Will you re-write it to use a different Javascript library?
-
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.
- o How do I change the generated page formats?
-
The templates used for generating pages are found via the config option page_template_path.
All *.tx files in this directory are put into the page template menu on the 'Update Design' page.
The default home page template is home.page.tx.
The default generic page template is generic.page.tx.
- o Why didn't you use accessors::classic/Class::Accessor/Object::Tiny/...?
-
For various reasons:
- o accessors::classic ...
-
... does not parse the parameters to new().
- o I wanted ...
-
... to have BUILD() (or even init() ) called after new().
- o Class::Accessor::Constructor ...
-
... will call init(), but it also drags in another set of dependencies.
- o Object::Tiny ...
-
... does not have attribute setters.
- o With Mouse, ....
-
... a syntax-friendly upgrade path the Moose is preserved.
- o Will you adopt DBIx::Connector?
-
Probably.
- o Do I need Apache?
-
No. Starman, part of Perl's Plack project, is recommended.
See the sample code in httpd/cgi-bin/office.
- o What other CMS's are there?
-
Heaps: http://cmsmatrix.org/.
- o Lastly, a plea
-
With your co-operation, I'd like to reserve the namespace
App::Office::CMS
, and perhaps evenApp::Office::Wiki
, for my own code.Of course, you're much better off using a TiddlyWiki http://tiddlywiki.org/wiki/Main_Page than waiting for me to start writing a wiki.
For massive wikiness (as distinct from wickedness :-) I draw your attention to both Silki and http://foswiki.org/Home/WebHome.
TODO
- o Adopt Git::Repository for versioned backup
- o Clean up error handling
-
For example, when build_error_result is called, rather than build_success_result, the data sent to Javascript must be handled slightly differently.
This includes HandleError in DBI's connect() attributes.
- o Make asset handling more sophisticated
- o Add begin/end transaction
-
This would allow the client to pass the menu item's id to the server, instead of the text
- o Need to document handling of &
- o Do we need separate editor windows for each page's head and body?
-
We don't.
- o Ship with SQLite activated, not Postgres
- o Consider using <span> instead of <div>
- o Auto-generate a site
- o Auto-generate a design for a site
-
This saves the user the effort of going to the Search tab to find a site or design
- o When clicking on the site map, the Edit Pages fields are updated, but the Edit Content fields are not
- o Add an option, perhaps, to escape entities when inputting HTML
- o Adopt DBIx::Connector
- o Implement user-initiated backup and restore
- o Change class hierarchy
-
This is so View does not have to pass so many parameters to its 'has-a' attributes
- o Adopt CGI::Untaint::html or HTML::Defang
-
Considered and rejected: HTML::Sanitizer, HTML::Scrubber.
- o Test CGI::Untaint as to its handling of <script>...</script>
- o Investigate Quicki's revision system
Support
Email the author, or log a bug on RT:
https://rt.cpan.org/Public/Dist/Display.html?Name=App-Office-CMS.
Author
App::Office::CMS was written by Ron Savage <ron@savage.net.au> in 2010.
Home page: http://savage.net.au/index.html.
Copyright
Australian copyright (c) 2010, 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