NAME

App::ZofCMS::Plugin::PreferentialOrder - Display HTML snippets in user-controllable, savable order

EXTRA RESOURCES (BEYOND PERL)

This plugin was designed to be used in conjunction with JavaScript (JS) code that controls the order of items on the page and submits that information to the server.

If you wish to use a different, your own front-end, please study JS code provided at the end of this documentation to understand what is required.

SYNOPSIS

In your HTML::Template template:

<tmpl_var name='plug_pref_order_form'>
<tmpl_var name='plug_pref_order_list'>
<tmpl_var name='plug_pref_order_disabled_list'>

In your ZofCMS template:

plugins => [ qw/PreferentialOrder/, ],

# except for the mandatory argument `items`, the default values are shown
plug_preferential_order => {
    items => [ # four value type variations shown here
        forum3  => '<a href="#">Forum3</a>',
        forum4  => [ 'Last forum ":)"',   \'forum-template.tmpl', ],
        forum   => [ 'First forum ":)"',  '<a href="#">Forum</a>',  ],
        forum2  => [
            'Second forum ":)"',
            sub {
                my ( $t, $q, $config ) = @_;
                return '$value_for_the_second_element_in_the_arrayref';
            },
        ],
    ],
    dsn            => "DBI:mysql:database=test;host=localhost",
    user           => '',
    pass           => undef,
    opt            => { RaiseError => 1, AutoCommit => 1 },
    users_table    => 'users',
    order_col      => 'plug_pref_order',
    login_col      => 'login',
    order_login    => sub { $_[0]->{d}{user}{login} },
    separator      => ',',
    has_disabled   => 1,
    enabled_label  => q|<p class="ppof_label">Enabled items</p>|,
    disabled_label => q|<p class="ppof_label">Disabled items</p>|,
    submit_button  => q|<input type="submit" class="input_submit"|
                        . q| value="Save">|,
},

DESCRIPTION

The module is a plugin for App::ZofCMS that provides means to have a sortable list of custom HTML snippets. The order can be defined by each individual user to suit their needs. The order is defined using a form provided by the plugin, the actual sorting is done by MooTools (http://mootools.net) JS framework. Use of this framework is not a necessity; it's up to you what you'll use as a front-end. An example of MooTools front-end is provided at the end of this documentation.

The plugin provides two modes: single sortable list, and double lists, where the second list represents "disabled" items, although that can well be used for having two lists with items being sorted between each of them (e.g. primary and secondary navigations).

This documentation assumes you've read App::ZofCMS, App::ZofCMS::Config and App::ZofCMS::Template

FIRST-LEVEL ZofCMS TEMPLATE AND MAIN CONFIG FILE KEYS

plugins

plugins => [ qw/PreferentialOrder/ ],

Mandatory. You need to include the plugin in the list of plugins to execute.

plug_preferential_order

# except for the mandatory argument `items`, the default values are shown
plug_preferential_order => {
    items => [ # four value type variations shown here
        forum3  => '<a href="#">Forum3</a>',
        forum4  => [ 'Last forum ":)"',   \'forum-template.tmpl', ],
        forum   => [ 'First forum ":)"',  '<a href="#">Forum</a>',  ],
        forum2  => [
            'Second forum ":)"',
            sub {
                my ( $t, $q, $config ) = @_;
                return '$value_for_the_second_element_in_the_arrayref';
            },
        ],
    ],
    dsn            => "DBI:mysql:database=test;host=localhost",
    user           => '',
    pass           => undef,
    opt            => { RaiseError => 1, AutoCommit => 1 },
    users_table    => 'users',
    order_col      => 'plug_pref_order',
    login_col      => 'login',
    order_login    => sub { $_[0]->{d}{user}{login} },
    separator      => ',',
    has_disabled   => 1,
    enabled_label  => q|<p class="ppof_label">Enabled items</p>|,
    disabled_label => q|<p class="ppof_label">Disabled items</p>|,
    submit_button  => q|<input type="submit" class="input_submit"|
                        . q| value="Save">|,
},

# or
plug_preferential_order => sub {
    my ( $t, $q, $config ) = @_;
    return $hashref_to_assign_to_the_plug_key;
},

Mandatory. Takes either an undef, a hashref or a subref as a value. If subref is specified, its return value will be assigned to plug_preferential_order as if it was already there. If undef is specified or the sub returns one, then plugin will stop further processing. The @_ of the subref will contain $t, $q, and $config (in that order), where $t is ZofCMS Tempalate hashref, $q is query parameter hashref and $config is App::ZofCMS::Config object. Possible keys/values for the hashref are as follows:

items

plug_preferential_order => {
    items => [ # four value type variations shown here
        forum3  => '<a href="#">Forum3</a>',
        forum4  => [ 'Last forum ":)"',   \'forum-template.tmpl', ],
        forum   => [ 'First forum ":)"',  '<a href="#">Forum</a>',  ],
        forum2  => [
            'Second forum ":)"',
            sub {
                my ( $t, $q, $config ) = @_;
                return '$value_for_the_second_element_in_the_arrayref';
            },
        ],
    ],
...

plug_preferential_order => {
    items => sub {
        my ( $t, $q, $config ) = @_;
        return $items_arrayref;
    },
...

Mandatory. Takes an arrayref, a subref or undef as a value. If set to undef (i.e. not specified), plugin will not execute. If a subref is specified, its return value will be assigned to items as if it was already there. The @_ of the subref will contain $t, $q, and $config (in that order), where $t is ZofCMS Tempalate hashref, $q is query parameter hashref and $config is App::ZofCMS::Config object. This argument tells the plugin the items on the list you want the user to sort and use.

The insides of the arrayref are best to be thought as keys/values of a hashref; the reason for the arrayref is to preserve the original order. The "keys" of the arrayref must NOT contain separator (see below) and need to conform to HTML/Your-markup-language id attribute (http://xrl.us/bicips). These keys are used by the plugin to label the items in the form that the user uses to sort their lists, the labels for the actual list items when they are displayed, as well as labels stored in the SQL table for each user.

The "value" of the "key" in the arrayref can be a scalar, a scalarref, a subref, as well as an arrayref with two items, first being a scalar and the second one being either a scalar, a scalarref, or a subref.

When the value is a scalar, scalarref or subref, it will be internally converted to an arrayref with the value being the second item, and the first item being the "key" of this "value" in the arrayref. In other words, these two codes are equivalent:

items => [ foo => 'bar', ],

items => [ foo => [ 'foo', 'bar', ], ],

The first item in the inner arrayref specifies the human readable name of the HTML snippet. This will be presented to the user in the sorting form. The second item represents the actual snippet and it can be specified using one of the following three ways:

a subref

items => [
    foo => [
        bar => sub {
            my ( $t, $q, $config ) = @_;
            return 'scalar or scalarref to represent the actual snippet';
        },
    ],
],

If the second item is a subref, its @_ will contain $t, $q, and $config (in that order) where $t is ZofCMS Template hashref, $q is query parameter hashref, and $config is App::ZofCMS::Config object. The sub must return either a scalar or a scalarref that will be assigned to the "key" instead of this subref.

a scalar

items => [
    foo => [
        bar => [ bez => '<a href="#"><tmpl_var name="meow"></a>', ],
    ],
],

If the second item is a scalar, it will be interpreted as a snippet of HTML::Template template. The parameters will be set into this snippet from {t} ZofCMS Template special key.

a scalarref

items => [
    foo => [
        bar => [ bez => \'template.tmpl', ],
    ],
],

If the second item is a scalaref, its meaning and function is the same as for the scalar value, except the HTML::Template template snippet will be read from the filename specified by the scalarref. Relative paths here will be relative to index.pl file.

dsn

plug_preferential_order => {
    dsn => "DBI:mysql:database=test;host=localhost",
...

Optional, but with useless default value. The dsn key will be passed to DBI's connect_cached() method, see documentation for DBI and DBD::your_database for the correct syntax for this one. The example above uses MySQL database called test that is located on localhost. Defaults to: DBI:mysql:database=test;host=localhost

user

plug_preferential_order => {
    user => '',
...

Optional. Specifies the user name (login) for the database. This can be an empty string if, for example, you are connecting using SQLite driver. Defaults to: empty string

pass

plug_preferential_order => {
    pass => undef,
...

Optional. Same as user except specifies the password for the database. Defaults to: undef (no password)

opt

plug_preferential_order => {
    opt => { RaiseError => 1, AutoCommit => 1 },
...

Optional. Will be passed directly to DBI's connect_cached() method as "options". Defaults to: { RaiseError => 1, AutoCommit => 1 }

users_table

plug_preferential_order => {
    users_table => 'users',
...

# This is the minimal SQL table needed by the plugin:
CREATE TABLE `users` (
    `login`           TEXT,
    `plug_pref_order` TEXT
);

Optional. Takes a scalar as a value that represents the table into which to store users' sort orders. The table can be anything you want, but must at least contain two columns (see order_col and login_col below). Defaults to: users

order_col

plug_preferential_order => {
    order_col => 'plug_pref_order',
...

Optional. Takes a scalar as a value. Specifies the name of the column in the users_table table into which to store users' sort orders. The orders will be stored as strings, so the column must have appropriate type. Defaults to: plug_pref_order

login_col

plug_preferential_order => {
    login_col => 'login',
...

Optional. Takes a scalar as a value. Specifies the name of the column in the users_table table in which users' logins are stored. The plugin will use the values in this column only to look up appropriate order_col columns, thus the data type can be anything you want. Defaults to: login

order_login

plug_preferential_order => {
    order_login => sub {
        my ( $t, $q, $config ) = @_;
        return $t->{d}{user}{login};
    },
...

plug_preferential_order => {
    order_login => 'zoffix',
...

Optional. Takes a scalar, undef, or a subref as a value. If set to undef (not specified) the plugin will not run. If subref is specified, its return value will be assigned to order_login as it was already there. The @_ will contain $t, $q, and $config (in that order) where $t is ZofCMS Template hashref, $q is query parameter hashref, and $config is App::ZofCMS::Config object. The scalar value specifies the "login" of the current user; this will be used to get and store the order_col value based on the order_login present in the login_col column in the users_table table. Defaults to: sub { $_[0]->{d}{user}{login} }

separator

plug_preferential_order => {
    separator => ',',
...

Optional. Specifies the separator that will be used to join together sort order before sticking it into the database. IMPORTANT: your JS code must use the same separator to join together the sort order items when user submits the sorting form. Defaults to: , (a comma)

has_disabled

plug_preferential_order => {
    has_disabled => 1,
...

Optional. Takes either true or false values as a value. When set to a true value, the plugin will present the user with two lists, with the items movable between the two. When set to a false value, the plugin will show the user only one sortable list.

If the order was stored between the two lists, but then the second list becomes disabled, the previously disabled items will be appended to the end of the first list (both in the display list, and in the sorting form). If the second list becomes enabled before the user saves the single-list order, the divisions between the two lists will be preserved.

Originally, this was designed to have "enabled" and "disabled" groups of items, hence the naming of this and few other options; the "enabled" represents the list that is always shown, and the "disabled" represents the list that is toggleable with has_disabled argument. Defaults to: 1 (second list is enabled)

enabled_label

plug_preferential_order => {
    enabled_label => q|<p class="ppof_label">Enabled items</p>|,
...

Optional. Applies only when has_disabled is set to a true value. Takes HTML code as a value that will be shown above the "enabled" list of items inside the sorting form. Defaults to: <p class="ppof_label">Enabled items</p>

disabled_label

plug_preferential_order => {
    disabled_label => q|<p class="ppof_label">Disabled items</p>|,
...

Optional. Applies only when has_disabled is set to a true value. Takes HTML code as a value that will be shown above the "disabled" list of items inside the sorting form. Defaults to: <p class="ppof_label">Disabled items</p>

submit_button

plug_preferential_order => {
    submit_button => q|<input type="submit" class="input_submit"|
                        . q| value="Save">|,
...

Optional. Takes HTML code as a value that represents the submit button on the sorting form. This was designed with the idea to allow image button use; however, feel free to insert here any extra HTML code you require in your form. Defaults to: <input type="submit" class="input_submit" value="Save">

HTML::Template TEMPLATE VARIABLES

<tmpl_var name='plug_pref_order_form'>
<tmpl_var name='plug_pref_order_list'>
<tmpl_var name='plug_pref_order_disabled_list'>

The plugin operates through three HTML::Template variables that you can use in any combination. These are as follows:

plug_pref_order_form

<tmpl_var name='plug_pref_order_form'>

This variable contains the sorting form.

plug_pref_order_list

<tmpl_var name='plug_pref_order_list'>

This variable contains the "enabled" list. If has_disabled is turned off while the user has some items in their "disabled" list; all of them will be appended to the "enabled" list.

plug_pref_order_disabled_list

<tmpl_var name='plug_pref_order_disabled_list'>

This variable contains the "disabled" list. If has_disabled is turned off while the user has some items in their "disabled" list; all of them will be appended to the "enabled" list, and this ("disabled") list will be empty.

SAMPLE JavaScript CODE TO USED WITH THE PLUGIN

This code relies on MooTools (http://mootools.net) JS framework to operate. (Note: this code also includes non-essential bit to make the enabled and disabled lists of constant size)

window.onload = function() {
    setup_sortables();
}

function setup_sortables() {
    var els_list = $$('.ppof_list li');
    var total_height = 0;
    for ( var i = 0, l = els_list.length; i < l; i++ ) {
        total_height += els_list[i].getSize().y;
    }
    $$('.ppof_list').set({'styles': {'min-height': total_height}});

    var mySortables = new Sortables('#ppof_order, #ppof_order_disabled', {
        'constraint': true,
        'clone': true,
        'opacity': 0.3
    });

    mySortables.attach();
    $('ppof_order').zof_sortables = mySortables;
    $('plug_preferential_order_form').onsubmit = add_sorted_list_input;
}

function add_sorted_list_input() {
    var result = $('ppof_order').zof_sortables.serialize(
        0,
        function(element, index){
            return element.getProperty('id').replace('ppof_order_item_','');
        }
    ).join(',');

    var result_el = new Element ('input', {
        'type': 'hidden',
        'name': 'ppof_order',
        'value': result
    });
    result_el.inject(this);

    var result_disabled = $('ppof_order').zof_sortables.serialize(
        1,
        function(element, index){
            return element.getProperty('id').replace('ppof_order_item_','');
        }
    ).join(',');

    var result_el_disabled = new Element ('input', {
        'type': 'hidden',
        'name': 'ppof_order_disabled',
        'value': result_disabled
    });
    result_el_disabled.inject(this);
    return true;
}

SAMPLE CSS CODE USED BY THE PLUGIN

This is just a quick and ugly sample CSS code to give your lists some structure for you to quickly play with the plugin to decide if you need it:

#ppof_enabled_container,
#ppof_disabled_container {
    width: 400px;
    float: left;
}

.ppof_label {
    text-align: center;
    font-size: 90%;
    font-weight: bold;
    letter-spacing: -1px;
    padding: 0;
    margin: 0;
}

.success-message {
    color: #aa0;
    font-weight: bold;
    font-size: 90%;
}

.ppof_list {
    list-style: none;
    border: 1px solid #ccc;
    min-height: 20px;
    padding: 0;
    margin: 0 0 7px;
    background: #ffd;
}

.ppof_list li {
    padding: 10px;
    background: #ddd;
    border: 1px solid #aaa;
    position: relative;
}

#plug_preferential_order_form .input_submit {
    clear: both;
    display: block;
}

HTML CODE GENERATED BY THE PLUGIN

Sorting Form

<!-- Double list (has_disabled is set to a true value) -->
<form action="" method="POST" id="plug_preferential_order_form">
<div>
    <input type="hidden" name="page" value="/index">
    <input type="hidden" name="ppof_save_order" value="1">

    <div id="ppof_enabled_container">
        <p class="ppof_label">Enabled items</p>
        <ul id="ppof_order" class="ppof_list">
            <li id="ppof_order_item_forum4">Last forum ":)"</li>
            <li id="ppof_order_item_forum">First forum ":)"</li>
        </ul>
    </div>

    <div id="ppof_enabled_container">
        <p class="ppof_label">Disabled items</p>
        <ul id="ppof_order_disabled" class="ppof_list">
            <li id="ppof_order_item_forum2">Second forum ":)"</li>
            <li id="ppof_order_item_forum3">forum3</li>
        </ul>
    </div>

    <input type="submit" class="input_submit" value="Save">
</div>
</form>

<!-- Single list (has_disabled is set to a false value) -->
<form action="" method="POST" id="plug_preferential_order_form">
<div>
    <input type="hidden" name="page" value="/index">
    <input type="hidden" name="ppof_save_order" value="1">

    <div id="ppof_enabled_container">
        <ul id="ppof_order" class="ppof_list">
            <li id="ppof_order_item_forum4">Last forum ":)"</li>
            <li id="ppof_order_item_forum">First forum ":)"</li>
            <li id="ppof_order_item_forum2">Second forum ":)"</li>
            <li id="ppof_order_item_forum3">forum3</li>
        </ul>
    </div>

    <input type="submit" class="input_submit" value="Save">
</div>
</form>

This form shows the default arguments for enabled_label, disabled_label and submit_button. Note that id="" attributes on the list items are partially made out of the "keys" set in items argument. The value for page hidden input is derived by the plugin automagically.

"Enabled" Sorted List

<ul class="plug_list_html_template">
    <li id="ppof_order_list_item_forum4">Foo:</li>
    <li id="ppof_order_list_item_forum"><a href="#">Forum</a></li>
</ul>

The end parts of id="" attributes on the list items are derived from the "keys" in items arrayref. Note that HTML in the values are not escaped.

"Disabled" Sorted List

<ul class="plug_list_html_template_disabled">
    <li id="ppof_order_list_disabled_item_forum2">Bar</li>
    <li id="ppof_order_list_disabled_item_forum3">Meow</li>
</ul>

The end parts of id="" attributes on the list items are derived from the "keys" in items arrayref. HTML in the values (innards of <li>s) are not escaped.

REQUIRED MODULES

This plugins lives on these modules:

App::ZofCMS::Plugin::Base => 0.0106,
DBI                       => 1.607,
HTML::Template            => 2.9,

REPOSITORY

Fork this module on GitHub: https://github.com/zoffixznet/App-ZofCMS

BUGS

To report bugs or request features, please use https://github.com/zoffixznet/App-ZofCMS/issues

If you can't access GitHub, you can email your request to bug-App-ZofCMS at rt.cpan.org

AUTHOR

Zoffix Znet <zoffix at cpan.org> (http://zoffix.com/, http://haslayout.net/)

LICENSE

You can use and distribute this module under the same terms as Perl itself. See the LICENSE file included in this distribution for complete details.