The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Mojolicious::Plugin::ContentManagement - Content management for Mojolicious

VERSION

Version 0.012

SYNOPSIS

    use Mojolicious::Lite;

    # Change this!
    my $admin_route = app->routes->bridge('/admin')->to( cb => sub {
        my $self = shift;
        my $user = $self->param('user') || 'foo';
        my $pass = $self->param('pass') || 'bar';

        return 1 if $user eq $pass;
        
        $self->res->code(401);
        $self->res->body(<<'EOF');
    <!doctype html><html>
    <head><title>Authorization required</title></head>
    <body><h1>401 Authorization required</h1></body>
    EOF
    });

    plugin content_management => {
        source      => 'filesystem',
        source_conf => { directory => 'content' },
        type        => 'markdown',
        type_conf   => { empty_element_suffix => '>' },
        forbidden   => [ '/foo.html', qr|/bar/\d{4}/baz.html| ],
        admin_route => $admin_route,
    };

    get '/(*everything)' => ( content_management => 1 ) => 'page';

    # your webapp stuff goes here,
    # avoid routes that aren't matched by the forbidden rules above

    __DATA__

    @@ page.html.ep
    % layout 'default';
    %== $content_page->html;

DESCRIPTION

This is a simple but flexible and extendable content management system that seamlessly integrates into your Mojolicious or Mojolicious::Lite app. You can use your own actions to display generated content and create your own bridge or waypoint routes for the optional admin interface.

First, Mojolicious::Plugin::ContentManagement (called MPCM from now on) comes as a Mojolicious plugin that can be used with the standard plugin code:

    # Mojolicious
    sub startup {
        my $self = shift;

        $self->plugin( content_management => $conf );
        ...
    }

    # Mojolicious::Lite
    plugin content_management => $conf;

CONFIGURATION

The $conf scalar needs to be a hashref with the following keys:

source

The source class used to store and generate content pages. The value of this option is camelized before used to find the right class, for example you write

    source => 'filesystem'

and MPCM loads Mojolicious::Plugin::ContentManagement::Source::Filesystem for you.

See Mojolicious::Plugin::ContentManagement::Source for more implementations.

source_conf

A configuration hashref that is passed to your source class. See the documentation of your source class for more details.

type

The type class. This is a translator for your content pages. The value of this option is camelized before used, like the source. See Mojolicious::Plugin::ContentManagement::Type for implementations.

type_conf

A configuration hashref that is passed to your type class. See the documentation of your source class for more details.

forbidden

An arrayref with paths and path regexes which must not be managed by MPCM (because you need them for your own routes and actions).

admin_route

If you want an admin interface for MPCM, you can pass a route object, so MPCM can set up routes to let you list or edit the pages. A typical way to do this is to set up a bridge for the path '/admin' with some kind of authentication and passing this bridge to MPCM:

    my $admin_route = $routes->bridge('/admin')->to( cb => sub {
        my $self = shift;
        my $user = $self->param('user') || 'foo';
        my $pass = $self->param('pass') || 'bar';

        return 1 if $user eq $pass;
        
        $self->res->code(401);
        $self->res->body(<<'EOF');
    <!doctype html><html>
    <head><title>Authorization required</title></head>
    <body><h1>401 Authorization required</h1></body>
    EOF
    });

I'm sure you can do that more securely, but I hope it shows what I mean. The $admin_route can now be passed to MPCM and it will create things like /admin/edit/foo.html which are only accessable if the user passes the bridge.

CONTENT HANDLING

You need to set up a controller or something like that which displays the managed content. But how do you dispatch? No problem, this is what the content_management condition is for:

    # Mojolicious::Lite
    get '/(*everything)' => ( content_management => 1 ) => sub {
        my $self = shift;
        # do something with $self->stash('content_page')
    }) => 'content';

    # Mojolicious
    $r->route('/(*everything)')->over( content_management => 1 )
      ->to('foo#content');

If this matches, you have access to the stash element 'content_page', which is a Mojolicious::Plugin::ContentManagement::Page object. For most cases, this will do it:

    get '/(*everything)' => ( content_management => 1 ) => 'page'

    __DATA__

    @@page.html.ep
    % layout 'default';
    %== $content_page->html

GENERATING THE ADMIN TEMPLATES

The (optional) admin interface ships with templates. This is how you generate them in your app home dir:

    $ mojolicious generate content_management_templates

Change the templates if you want and if you know what you're doing.

BUGS

Please use githubs issue tracker at http://github.com/memowe/mojolicious-plugin-content_management.

If you want to provide patches, feel free to fork and pull request me.

AUTHOR, COPYRIGHT AND LICENSE

Copyright (c) 2010-2011 Mirko Westermeier, <mail at memowe.de>

Released under the MIT license (see MIT-LICENSE) for details.