WebGUI: Kickstarter for a Modern Perl CMS

Tags:

UPDATE: The Kickstarter project reached its funding goal! Codename for Version 8 will be Allium Cepa. There is a project page on GitHub.

Scott Walters' 2014 Kickstarter, "Create Perl Competition to the PHP Content Management System" is somewhat open ended as to what this Perl-based CMS would be, but likely it will be based on the GPL'd WebGUI system created by Plain Black Corp. According to Scott's writeup,

…Version 7 is still being maintained, but 8 was a massive modernization effort that reworked core to use Moose, Plack, Try::Tiny, and cleaned things up.

Thanks to fantastic test suite coverage, the API and core of 8 work well, but the rewritten admin needs attention. Also, WebGUI has traditionally targeted large companies, so the difficult install process was not a major liability. To compete, installation has to be dead simple. The themes are not adequately modern (which is to say they look a little outdated).

I plan to get wG8 out of alpha, move it to a community development model, finish the installer I created for it and OSX support, and work with designers to create a modernized theme.

The installation script is currently Curses based, eliminating the web hole even in the first steps. A Docker install may also be included.

Why?

…wG is one of the most impressive and mature things ever created in Perl, and the community often raves to me about how much better they like it than competing systems.

− scrottie in the WebGUI Forums

More from scrottie (July, 2014): Perl Needs a User Friendly CMS [blogs.perl.org]

Background

From the Abstract, "Programming WebGUI: WebGUI 8 as a Web App Framework" by Scott Walters, (October 2011)

the WebGUI content management system.

Building apps on top of CMSes has become trendy in the Python and PHP camps, and for good reason: CMSes provide the primitives to build the most common types of Web applications, they're easy to interface with, and the idea of a tree of editable objects translates well to interactive Web sites.

WebGUI ships with a pile of ready to use pieces of logic (called "assets", implementing things such as shopping carts and Wikis) for laypersons to build a site out of, but it's also a fantastically programmer friendly framework for building more of these assets in.

WebGUI 8 is the upcoming iteration, sporting a new Plack interface, Moose attribute based asset definition, and countless modernizations and improvements. WebGUI handles the tedious work of data persistence, permissions, generating edit forms, pagination, and more.

See Also

Interpreting a WordPress Export XML file

Tags:

In preparation to writing a tool that can import most of an existing WordPress site into WebGUI (see the Kickstarter to release WebGUI Version 8) the good news is that a WordPress export file can be loaded into Perl quite easily:

#!/usr/bin/perl use XML::Simple qw(:strict); my $xs = XML::Simple->new(); my $ref = $xs->XMLin($ARGV[0], ForceArray => 1, KeyAttr => []);

Pass that script the name of your export file, and you can peruse the results with

use Data::Dumper; print Dumper($ref);

Here's what you will find:

WordPress contents

That's the more-or-less organization of this file. As you can see it delightfully shows that some of WordPress's foundations still suffer original cracks.


Example code:

#!/usr/bin/perl

use XML::Simple qw(:strict);
binmode(STDOUT, ":utf8");   # enable Unicode output

my $xs = XML::Simple->new();
my $ref = $xs->XMLin($ARGV[0], ForceArray => 1, KeyAttr => []);

use Data::Dumper;

use HTML::TreeBuilder;
sub rectify_html {

    my $munged_text = shift;
    $munged_text =~ s/\n\n/\n<p>/g;

    my @pre_segments;
    my $seg_id=0;
    $munged_text =~ s{<pre\b(.*?)>(.*?)</pre\s*>}{$pre_segments[++$seg_id]=$2; "<pre data-seg=\"$seg_id\"$1></pre>";}gsex;

    my $atree = HTML::TreeBuilder->new();

    # Prepare to store comments. Requires wrapping in <html><body>
    $atree->store_comments(1);
    $atree->parse("<html><body>$munged_text</body></html>");

    # Replace original text contents for <pre> elements
    foreach my $pre_element ($atree->look_down('_tag', 'pre')) {
    $pre_element->push_content($pre_segments[$pre_element->attr('data-seg')]);
    $pre_element->attr('data-seg',undef);
    }

    # print Dumper($atree);
    print $atree->as_HTML(undef, ' ', {});
}


# Here we show just the contents of the posts and pages.

my $posts = $ref->{channel}[0]->{item};

foreach my $post (@{$posts}) {
    if ($post->{'wp:post_type'}[0] =~ /^post|page$/) {
    print "Entry with ID=$post->{'wp:post_id'}[0] of type $post->{'wp:post_type'}[0] contains:\n";
    print $post->{'content:encoded'}[0];
    print "\n-----------------\n";
    print rectify_html($post->{'content:encoded'}[0]);
    print "\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n";
    } else {
    print Dumper($post->{'wp:post_type'});
    
    }
}

Setting up Asterisk VOIP on a Linode with Debian

Tags:

Asterisk is an open-source telephone solution that runs over the internet instead of running through copper lines. It offers a variety of features such as voicemail and conference calling, much like a land line telephone can.

For this guide we will install Asterisk from source rather than from Ubuntu's repositories. The newer version offers several additional features, including the ability to integrate a Google Voice account as a trunk. We will use FreePBX as a web interface for our Asterisk configuration.

Read the rest of the story on Linode's site

I used Debian 7.5 instead of Ubuntu. When I got to the section where it said to:

apt-get install linux-virtual

I actually had to follow the instructions here to get the local kernel matching the dahdi_dummy kernel module.

I also had to install the correct kernel source, from the hint here, before the make commands would work:

apt-get install linux-headers-`uname -r`

The php5-suhosin package does not exist in the stable repository, so I had to omit that from the php apt-get line.

By default, Debian has the Apache document root at /var/www; we want to change that to /var/www/html. The VirtualHost directive is in file /etc/apache2/sites-available/default − change the appropriate lines:

…
        DocumentRoot /var/www/html
        <Directory />
                Options FollowSymLinks
                AllowOverride None
        </Directory>
        <Directory /var/www/html/>
…

To be able to install the Google Voice module, I had to:

# su - asterisk 
$ cd /var/www/html/admin/modules/
$ mkdir _cache

Shell script to open mysql with WordPress parameters

Tags:

#!/bin/bash mysqlgrep DB_NAME ~/$1wp-config.php | cut -d \' -f 4-ugrep DB_USER ~/$1wp-config.php | cut -d \' -f 4 --password=grep DB_PASS ~/$1wp-config.php | cut -d \' -f 4``

Save this as a script, such as ~/wp-mysql and do a chmod a+x on it.

Rather than lots of copying and pasting from wp-config.php, we pull the values directly from the file. Note that this assumes you have put wp-config.php in your home directory and not in public_html. If you want to use a different wp-config, pass the subdirectory name as the first argument. For example,

~/wp-mysql domains/blog.example.com/

Putting the password on the command line is not much of a security risk, as mysql replaces the actual password in the command line with 'xxxxxx' as you can see by doing ps aux while the mysql monitor is running.

Writing Extensible Systems with Pluggable Modules in Perl

Tags:

Module::Pluggable is an under-appreciated way to write extensible programs. It has been in the Perl core modules since Perl 5.8. [1] The author maintains a Github repository.

use Module::Pluggable instantiate => function_name;

Instantiates an object for each pluggable module by calling the named function (method); often you will want to pass "new". Or you might just want to load (require) the modules, and not instantiate them:

use Module::Pluggable require => 1;

The return value to plugins() will then change from a list of the installed module names, to a list of all the objects created. Note that the change in return-value is undocumented. See also the option, on_instantiate_error which controls what happens when things go wrong.

Module::Pluggable does expect the following protocol:

use Module::Pluggable search_path => "Plugins", instantiate => 'new';

searches the subdirectory ./Plugins for files named *.pm (although you can change this with the file_regex parameter). For example, if you are writing a Mojolicious model that should have pluggable submodules, you could do this:

# in lib/MyApp/Model/MyGeneralModel.pm
use Module::Pluggable require => 1, search_path => __PACKAGE__;

which would load all files matching lib/MyApp/Model/MyGeneralModel/*.pm

Each file, for example Plugins/plug1.pm in the first example above, is expected to contain a co-ordinating definition properly placed in the object namespace hierarchy by matching the filename:

package Plugins::plug1;

sub new {
  my $class = shift;
  my $self = {};
  return bless \$self, $class;
}

sub some_handler {
  my $class = shift;
  …
}

In the main program or calling class, then, you might do something like:

# Save list of created plugin objects
my @plugs = plugins(optional => 'arguments');

…

my @values;
foreach my $p (@plugs) {
  push @values, $p->some_handler()
    if $p->can('some_handler');
}

Notes

  1. One quick way to determine this is to use the also built-in Module::CoreList function first_release() from the command line: perl -MModule::CoreList -e "print Module::CoreList->first_release('Module::Pluggable')"