#!/usr/bin/perl -w -CSDA # # snJvi - shell command line editor interface for Joomla! 1.5 articles # # Copyright (c) 2008-2009 EPIPE Communications # # This program 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. # # This program 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. # # See for license terms. # # # Version: 1.2 # # Version history: # # - 1.2: changed the license to GPLv3 due to stupid new # JED requirements (2009-03-14) # # - 1.1: changed to use CMS::Joomla module (2008-11-14) # # - 1.0: initial release (2008-08-23) # # Download location: # # http://dist.epipe.com/joomla/perl/ # # Author contact information: # # info # epipe.com # http://epipe.com/ # # Description: # # snJvi is Unix command line article editor interface for Joomla! 1.5. # It may be appreciated by old-school folks who prefer using their # favourite Unix text editor (vi, emacs, etc.) over any web browser # based solution. # # snJvi can be used as an integration tool for automating Joomla! # article updates with externally sourced data. It is also possible to # export articles from Joomla! database. # # This script serves also as an example on how Joomla! configuration # parameters and database can be accessed and manipulated from a perl # script. Those folks who think that PHP sucks but still wish or are # forced to use Joomla! might appreciate this as a basis of their # own perl based Joomla! tools. # # # Requirements: # # - perl (recent version) # # - shell access to the Joomla! server # # - CMS::Joomla perl module (available from CPAN) # # - some common perl modules (available from CPAN) for database and # file access, see the "use" statements at the beginning of the code # # # Environment: # # The environment variable JOOMLA_CONF can be set to point to the # location of the desired Joomla! configuration.php file, so that # it does not need to be specified on command line each time. # # For example (depending on the type of your shell): # # export JOOMLA_CONF=/your/joomla/site/configuration.php # setenv JOOMLA_CONF /your/joomla/site/configuration.php # # The command for starting the desired editor program can be # defined in JOOMLA_EDITOR, EDITOR or VISUAL environment # variables. The first defined variable (in the same order as # above) is used for editor selection and if none is defined, # "/usr/bin/vi" is used as the default. # # For example (depending on the type of your shell): # # export JOOMLA_EDITOR=/usr/bin/vi # setenv JOOMLA_EDITOR /usr/local/bin/htmledit # # # Usage: # # snJvi [ -c /joomla/site/configuration.php ] [ -i | -o ] [ -s ] articleID # # If articleID consists entirely of numbers, it is assumed to be # an article ID number of an existing article in the Joomla! # database. # # Otherwise articleID is expected to be a title or an alias title # of the desired article. # # -c option is used to specify the location of the Joomla! # configuration.php file for the site. # # -i option is used to specify that snJvi should input new contents # of the specified article from standard input. # # -o option is used to specify that snJvi should output article # to the standard output. # # -s option is used to suppress meta-data from the article and # only edit/input/output the article content part. # # If neither -i nor -o options are defined, snJvi will edit the # article using an external editor (see Environment above on how # to specify the editor to use). # # # Examples: # # snJvi -c /your/joomla/site/configuration.php 555 # # echo "

Today is rainy in England.

" | snJvi -i -s weather # # snJvi -o "Customer References" > custref-article.txt # # # Limitations: # # Article modification time is not updated automatically. You need # to change it manually if you want to update it. # # It is not possible to create new articles. # # The script does not attempt to do any de-referencing of database # columns that are references to other tables. For example instead # of user names the script shows user ID numbers (indexes to user # table). It is easy to mess things up if you don't know what you # are doing or are careless (in that case you should use the normal # Joomla! WYSIWYG editor :). # # It is not known how well this script works with Unicode UTF-8 # characters (which I suppose is the Joomla! database format) # outside of the standard US-ASCII subset used by English language # websites. The author hopes that the perl -CSDA options at the # first line make this script automagically fully UTF-8 compliant. # That is probably not the case. # # # Safety Precautions: # # It is easy to mess up your articles with this script. Editing should # be done carefully. Every tab character needs to remain a tab and # not converted to spaces by the editor. # use strict; use Getopt::Std; use IO::File; use File::Temp; use File::stat; use DBI; use CMS::Joomla; use vars qw($opt_c $opt_i $opt_o $opt_s $jconf $jeditor); my (@jcolumns) = qw( id title alias introtext state sectionid mask catid created created_by created_by_alias modified modified_by checked_out checked_out_time publish_up publish_down images urls attribs version parentid ordering metakey metadesc access hits metadata introtext); # sub outmultiline ($) { my ($str) = shift; $str =~ s/\n/\n\t/g; return $str; } # sub outputfile ($$) { my ($fh) = shift; my ($ar) = shift; $fh = \*STDOUT unless defined($fh); if (!$opt_s) { foreach (@jcolumns) { next if $_ eq 'introtext'; print $fh "$_:\t" . outmultiline($ar->{$_}) . "\n"; } print $fh "\n"; } print $fh $ar->{introtext} . "\n"; } # sub inputfile ($) { my ($fn) = shift; my ($fh); my (%ar); if (defined($fn)) { $fh = new IO::File; if (!($fh->open($fn, '<'))) { die "$0: can not open temporary file $fn: $!\n"; } } else { $fh = \*STDIN; } if (!$opt_s) { my ($lastc); while (<$fh>) { last if /^$/; if (/^(\w+):\t/) { $ar{$1} = $'; $lastc = $1; next; } if (/^\t/ && defined($lastc)) { $ar{$lastc} .= $'; next; } die "$0: syntax error on input line $.\n"; } foreach my $k (keys %ar) { if (!grep($_ eq $k, @jcolumns)) { die "$0: undefined field \"$k\"\n"; } chomp($ar{$k}); } } $ar{introtext} = ''; while (<$fh>) { $ar{introtext} .= $_; } chomp($ar{introtext}); return \%ar; } # sub editor ($) { my ($fn) = shift; if (system($jeditor, $fn) == -1) { die "$0: running editor $jeditor failed: $!\n"; } } # sub jarticle ($) { my ($id) = shift; my ($idnumeric) = $id =~ /^\d+$/; my ($joomla) = CMS::Joomla->new($jconf); return 0 unless defined($joomla); my ($dbh) = $joomla->dbhandle( { RaiseError => 1, AutoCommit => 1 } ); return 0 unless defined($dbh); my $sth = $dbh->prepare('SELECT ' . join(', ', @jcolumns) . ' FROM ' . $joomla->cfg->{'dbprefix'} . 'content' . ' WHERE ' . ($idnumeric ? 'id = ?' : 'title = ? OR alias = ?')); if ($idnumeric) { $sth->execute($id); } else { $sth->execute($id, $id); } if ($sth->rows == 0 || $sth->rows == -1) { die "$0: no articles matching ID \"$id\"\n"; } if ($sth->rows > 1) { die "$0: too many (" . $sth->rows . ") articles matching ID \"$id\"\n"; } my ($ar) = $sth->fetchrow_hashref; foreach (@jcolumns) { if (!defined($ar->{$_})) { die "$0: column \"$_\" is missing from the article, database error?\n"; } } my ($tmpf, $tmpfn); if (!$opt_i && !$opt_o) { if (!($tmpf = new File::Temp(UNLINK => 0, SUFFIX => '.txt'))) { die "$0: can not create a temporary file: $!\n"; } $tmpfn = $tmpf->filename; } if (!$opt_i) { outputfile($tmpf, $ar); } return 0 if $opt_o; if (!$opt_i && !$opt_o) { my ($st1, $st2); undef $tmpf; # should remove the lock on temporary file $st1 = stat($tmpfn); editor($tmpfn); $st2 = stat($tmpfn); if ($st1->mtime == $st2->mtime) { # no changes were made return 0; } } my ($nar) = inputfile(defined($tmpfn) ? $tmpfn : undef); unlink($tmpfn) if defined($tmpfn); $nar->{id} = $ar->{id} unless defined($nar->{id}); foreach (@jcolumns) { if (!defined($nar->{$_})) { $nar->{$_} = $ar->{$_}; } } if ($nar->{id} != $ar->{id}) { die "$0: can not change article ID\n"; } my (@changed) = (); foreach (@jcolumns) { if ($ar->{$_} ne $nar->{$_}) { push(@changed, $_); } } if (!@changed) { return 0; } my ($sql) = 'UPDATE ' . $joomla->cfg->{'dbprefix'} . 'content SET '; my (@data) = (); foreach (@changed) { $sql .= "$_ = ?, "; push(@data, $nar->{$_}); } $sql =~ s/, $//; $sql .= 'WHERE id = ?'; $sth = $dbh->prepare($sql); $sth->execute(@data, $nar->{id}); $dbh->disconnect; return 1; } sub HELP_MESSAGE () { print STDERR "usage: $0 [ -c configuration.php ] "; print STDERR "[ -i | -o ] [ -s ] articleID\n"; exit(2); } sub VERSION_MESSAGE () { print STDERR "snJvi version 1.1 - http://dist.epipe.com/joomla/perl/\n"; exit(2); } # main() $Getopt::Std::STANDARD_HELP_VERSION = 1; getopts('c:ios'); $jconf = $ENV{'JOOMLA_CONF'} if defined $ENV{'JOOMLA_CONF'}; $jconf = $opt_c if defined $opt_c; $jeditor = '/usr/bin/vi'; $jeditor = $ENV{'VISUAL'} if defined $ENV{'VISUAL'}; $jeditor = $ENV{'EDITOR'} if defined $ENV{'EDITOR'}; $jeditor = $ENV{'JOOMLA_EDITOR'} if defined $ENV{'JOOMLA_EDITOR'}; if (@ARGV != 1 || (defined($opt_i) && defined($opt_o)) || !defined($jconf)) { HELP_MESSAGE(); } jarticle($ARGV[0]); exit(0); # eof