diff --git a/blocks/ORB/List.pm b/blocks/ORB/List.pm new file mode 100644 index 0000000..893e394 --- /dev/null +++ b/blocks/ORB/List.pm @@ -0,0 +1,159 @@ +## @file +# This file contains the implementation of the list page. +# +# @author Chris Page <chris@starforge.co.uk> +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + +## @class +package ORB::List; + +use strict; +use parent qw(ORB); # This class extends the ORB block class +use experimental qw(smartmatch); +use v5.14; +use Data::Dumper; +## @method private % _build_tag($tag) +# Given a reference to a hash containing tag data, generate HTML to +# represent the tag +# +# @param tag A reference to a tag hash +# @return A string representing the tag +sub _build_tag { + my $self = shift; + my $tag = shift; + + return $self -> {"template"} -> load_template("list/tag.tem", { "%(name)s" => $tag -> {"name"}, + "%(color)s" => $tag -> {"color"}, + "%(bgcol)s" => $tag -> {"background"}, + "%(faicon)s" => $tag -> {"fa-icon"} + }); +} + + +## @method private $ _generate_list($mode) +# Generate the list page. This will create a page containing a list +# of recipies based on the specified mode. +# +# @return An array of two values containing the page title and content. +sub _generate_list { + my $self = shift; + my $mode = shift; + + my $recipes = $self -> {"system"} -> {"recipe"} -> get_recipe_list($mode) + or $self -> generate_errorbox(message => $self -> {"system"} -> {"recipe"} -> errstr()); + + my @list; + foreach my $recipe (@{$recipes}) { + my $temp = ""; + + if($recipe -> {"temp"} && $recipe -> {"temptype"} ne "N/A") { + $temp = $self -> {"template"} -> load_template("list/temp.tem", { "%(temp)s" => $recipe -> {"temp"}, + "%(temptype)s" => $recipe -> {"temptype"} + }); + } + + my $controls = ""; + if($self -> check_permission("recipe.edit", $recipe -> {"metadata_id"})) { + $controls .= $self -> {"template"} -> load_template("list/recipe.tem", + { "%(url-edit)s" => $self -> build_url(block => "edit", pathinfo => [ $recipe -> {"id"} ]), + "%(url-edit)s" => $self -> build_url(block => "edit", pathinfo => [ "clone", $recipe -> {"id"} ]), + "%(url-edit)s" => $self -> build_url(block => "edit", pathinfo => [ "delete", $recipe -> {"id"}]), + }); + } + + push(@list, $self -> {"template"} -> load_template("list/recipe.tem", + { "%(id)s" => $recipe -> {"id"}, + "%(url-view)s" => $self -> build_url(block => "view", + pathinfo => [ $recipe -> {"id"} ]), + "%(name)s" => $recipe -> {"name"}, + "%(type)s" => $recipe -> {"type"}, + "%(status)s" => $recipe -> {"status"}, + "%(time)s" => $self -> {"template"} -> humanise_seconds($recipe -> {"timemins"} * 60, 1), + "%(temp)s" => $temp, + "%(tags)s" => join("", map { $self -> _build_tag($_) } @{$recipe -> {"tags"}}), + "%(controls)s" => $controls, + })); + } + + return ($self -> {"template"} -> replace_langvar("LIST_TITLE", { "%(page)s" => uc($mode // "All") }), + $self -> {"template"} -> load_template("list/content.tem", {"%(pagemenu)s" => $self -> pagemenu($mode), + "%(page)s" => uc($mode // "All"), + "%(recipes)s" => join("", @list), + }), + $self -> {"template"} -> load_template("list/extrahead.tem"), + $self -> {"template"} -> load_template("list/extrajs.tem"), + ); +} + + +# ============================================================================ +# UI handler/dispatcher functions + +## @method private $ _dispatch_ui() +# Implements the core behaviour dispatcher for non-api functions. This will +# inspect the state of the pathinfo and invoke the appropriate handler +# function to generate content for the user. +# +# @return A string containing the page HTML. +sub _dispatch_ui { + my $self = shift; + + # We need to determine what the page title should be, and the content to shove in it... + my ($title, $body, $extrahead, $extrajs) = ("", "", "", ""); + my @pathinfo = $self -> {"cgi"} -> multi_param("pathinfo"); + + print STDERR "Mode: ".$pathinfo[0]."\n"; + if(defined($pathinfo[0]) && $pathinfo[0] =~ /^[0a-zA-Z\$]$/) { + ($title, $body, $extrahead, $extrajs) = $self -> _generate_list($pathinfo[0]); + } elsif($pathinfo[0] && lc($pathinfo[0]) eq "all") { + ($title, $body, $extrahead, $extrajs) = $self -> _generate_list(); + } else { + ($title, $body, $extrahead, $extrajs) = $self -> _generate_list('A'); + } + + # Done generating the page content, return the filled in page template + return $self -> generate_orb_page(title => $title, + content => $body, + extrahead => $extrahead, + extrajs => $extrajs, + doclink => 'list'); +} + + +# ============================================================================ +# Module interface functions + +## @method $ page_display() +# Generate the page content for this module. +sub page_display { + my $self = shift; + + # Is this an API call, or a normal page operation? + my $apiop = $self -> is_api_operation(); + if(defined($apiop)) { + # API call - dispatch to appropriate handler. + given($apiop) { + default { + return $self -> api_response($self -> api_errorhash('bad_op', + $self -> {"template"} -> replace_langvar("API_BAD_OP"))) + } + } + } else { + return $self -> _dispatch_ui(); + } +} + + +1; \ No newline at end of file diff --git a/lang/en/recipelist.lang b/lang/en/recipelist.lang new file mode 100644 index 0000000..c237263 --- /dev/null +++ b/lang/en/recipelist.lang @@ -0,0 +1,10 @@ +LIST_TITLE = Recipes - Page %(page)s +LIST_SUMMARIES = Summaries +LIST_FILTER = Filter +LIST_SORT = Sort + +LIST_NAME = Name +LIST_TYPE = Type +LIST_STATUS = Status + +LIST_OKAY = Okay diff --git a/templates/default/css/recipelist.css b/templates/default/css/recipelist.css new file mode 100644 index 0000000..f13f14b --- /dev/null +++ b/templates/default/css/recipelist.css @@ -0,0 +1,56 @@ +li.recipe { + padding: 3px; + border-bottom: 1px solid #e7e7e7; +} + +ul.recipes { + margin-left: 0; +} + +ul.recipes li { + list-style: none; +} + + +ul.recipes li .controls { + margin-bottom: 0.5rem; + margin-top: 0.5rem; +} + +ul.recipes li:nth-child(even) { + background-color: #fafafa; +} + +ul.recipes li .name { font-weight: bold; word-wrap: break-word; line-height: 1.1; margin-bottom: 4px; } +ul.recipes li ul.meta { + list-style: none; + margin: 0 0 0.2rem 0.5rem; + padding: 0; + line-height: 1.1; +} + +ul.meta li.type, +ul.meta li.status, +ul.meta li.time, +ul.meta li.temp { + display: inline-block; color: #888; font-size: 0.7rem; +} + +ul.recipes li .tags { + font-size: .75rem; + line-height: 1; + margin-left: 0.5rem; +} + +ul.recipes li .intro { + color: #888; + padding-right: 0.2rem; +} + +span.ljs-tag { + display: inline-block; + padding: .33333rem .5rem; + margin-right: .3rem; + border-radius: 0; + white-space: nowrap; +} \ No newline at end of file diff --git a/templates/default/js/delaysearch.js b/templates/default/js/delaysearch.js new file mode 100644 index 0000000..90de2ef --- /dev/null +++ b/templates/default/js/delaysearch.js @@ -0,0 +1,23 @@ +(function ($, undefined) { + $.fn.delaysearch = function (list) { + var $this = this; + + var searchTimer; + var delayLimit = 3; + var delayTime = 1500; + + $this.bind('keyup', function(e) { + var target = e.target || e.srcElement; // IE have srcElement + clearTimeout(searchTimer); + + var value = $this.val(); + if(value.length == 0 || value.length >= delayLimit) { + list.search(value); + } else { + searchTimer = setTimeout(list.search, delayTime, value); + } + }); + + return $this; + }; +})(jQuery); diff --git a/templates/default/js/recipe_list.js b/templates/default/js/recipe_list.js new file mode 100644 index 0000000..4808066 --- /dev/null +++ b/templates/default/js/recipe_list.js @@ -0,0 +1,10 @@ +$(function() { + var options = { + valueNames: [ 'ljs-name', 'ljs-type', 'ljs-status', 'ljs-time', 'tags' ], + plugins: [ ListFuzzySearch() ] + }; + + var recipeList = new List('recipelist', options); + + $('#listfilter').delaysearch(recipeList); +}); \ No newline at end of file diff --git a/templates/default/list/content.tem b/templates/default/list/content.tem new file mode 100644 index 0000000..e660586 --- /dev/null +++ b/templates/default/list/content.tem @@ -0,0 +1,31 @@ +%(pagemenu)s +
+ +
+
+ +
+ +
+
+
+ + +
+
+
error
+
+
\ No newline at end of file diff --git a/templates/default/list/controls.tem b/templates/default/list/controls.tem new file mode 100644 index 0000000..49d5c58 --- /dev/null +++ b/templates/default/list/controls.tem @@ -0,0 +1,5 @@ +
+ + + +
diff --git a/templates/default/list/extrahead.tem b/templates/default/list/extrahead.tem new file mode 100644 index 0000000..1442a48 --- /dev/null +++ b/templates/default/list/extrahead.tem @@ -0,0 +1 @@ + diff --git a/templates/default/list/extrajs.tem b/templates/default/list/extrajs.tem new file mode 100644 index 0000000..5f095ee --- /dev/null +++ b/templates/default/list/extrajs.tem @@ -0,0 +1,6 @@ + + + + + +