From de1529138818549c80371b258f81349cc1022079 Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 28 Sep 2019 11:55:07 +0100 Subject: [PATCH] Add cloning support --- blocks/ORB/Clone.pm | 267 ++++++++++++++++++++++++++ lang/en/recipe.lang | 4 + templates/default/clone/content.tem | 149 ++++++++++++++ templates/default/clone/extrahead.tem | 1 + templates/default/clone/extrajs.tem | 8 + 5 files changed, 429 insertions(+) create mode 100644 blocks/ORB/Clone.pm create mode 100644 templates/default/clone/content.tem create mode 100644 templates/default/clone/extrahead.tem create mode 100644 templates/default/clone/extrajs.tem diff --git a/blocks/ORB/Clone.pm b/blocks/ORB/Clone.pm new file mode 100644 index 0000000..4e1c4cc --- /dev/null +++ b/blocks/ORB/Clone.pm @@ -0,0 +1,267 @@ +## @file +# This file contains the implementation of the clone 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::Clone; + +use strict; +use parent qw(ORB::Common); # This class extends the ORB common class +use experimental qw(smartmatch); +use v5.14; +use JSON; + + +## @method private $ _convert_tags($tags) +# Convert a list of tags into a string that can be shown in the recipe +# page. +# +# @param tags A reference to a list of tag names. +# @return A string containing the tag list. +sub _convert_tags { + my $self = shift; + my $tags = shift; + + my @result; + foreach my $tag (@{$tags}) { + push(@result, $tag -> {"name"}); + } + + return join(",", @result); +} + + +## @method private void _convert_ingredients($args) +# Convert the ingredients list into a form that can be shown in the +# clone form. This fixes up some differences between the field names +# used in the result of get_recipe() and the ingredient generator. +# +# @param args A reference to the recipe data hash. +sub _convert_ingredients { + my $self = shift; + my $args = shift; + + foreach my $ingred (@{$args -> {"ingredients"}}) { + if($ingred -> {"separator"}) { + $ingred -> {"name"} = $ingred -> {"separator"}; + } else { + $ingred -> {"name"} = $ingred -> {"ingredient"}; + } + + $ingred -> {"prep"} = $ingred -> {"prepmethod"}; + } +} + + +# ============================================================================ +# UI handler/dispatcher functions + +## @method $ _generate_clone($recipeid) +# Build the page containing the recipe clone form. +# +# @return An array containing the page title, content, extra header data, and +# extra javascript content. +sub _generate_clone { + my $self = shift; + my $recipeid = shift; + my ($args, $errors); + + # Recipe ID must be purely numeric for clones + return $self -> _fatal_error("{L_EDIT_FAILED_BADID}") + unless($recipeid =~ /^\d+$/); + + # Try to fetch the data. + $args = $self -> {"system"} -> {"recipe"} -> get_recipe($recipeid); + return $self -> _fatal_error("{L_EDIT_FAILED_NOTFOUND}") + unless($args -> {"id"}); + + $args -> {"tags"} = $self -> _convert_tags($args -> {"tags"}); + $self -> _convert_ingredients($args); + + # User must have recipe edit to proceed. + return $self -> _fatal_error("{L_PERMISSION_FAILED_SUMMARY}") + unless($self -> check_permission('recipe.edit', $args -> {"metadata_id"})); + + if($self -> {"cgi"} -> param("clonerecipe")) { + $self -> log("recipe.clone", "User has submitted clone for recipe $recipeid"); + + $args = {}; + + # Do all the validation, and if there's no errors then add the recipe + ($args, $errors) = $self -> _validate_recipe(); + if(!$errors) { + # No errors, try adding the recipe + $args -> {"creatorid"} = $self -> {"session"} -> get_session_userid(); + $args -> {"id"} = $self -> {"system"} -> {"recipe"} -> create($args) + or $errors = $self -> {"template"} -> load_template("error/error_item.tem", + { "%(error)s" => $self -> {"system"} -> {"recipe"} -> errstr() }); + + # Did the clone work? If so, send the user to the view page for the new recipe + return $self -> redirect($self -> build_url(block => "view", + pathinfo => [ $args -> {"id"} ], + params => "", + api => [])) + if(!$errors); + } + } + + # Wrap the errors if there are any + if($errors) { + $self -> log("recipe.clone", "Errors detected in clone: $errors"); + + my $errorlist = $self -> {"template"} -> load_template("error/error_list.tem", {"%(message)s" => "{L_CLONE_ERRORS}", + "%(errors)s" => $errors }); + $errors = $self -> {"template"} -> load_template("error/page_error.tem", { "%(message)s" => $errorlist }); + } + + # Prebuild arrays for temptypes, units, and prep methods + my $temptypes = $self -> _build_temptypes(); + my $units = $self -> _get_units(); + my $preps = $self -> _get_prepmethods(); + + # And convert them to optionlists for the later template call + my $unitopts = $self -> {"template"} -> build_optionlist($units); + my $prepopts = $self -> {"template"} -> build_optionlist($preps); + + # Build the list of ingredients + my $ingredients = $self -> _build_ingredients($args); + + # Build up the type and status data + my $typeopts = $self -> {"template"} -> build_optionlist($self -> {"system"} -> {"entities"} -> {"types"} -> as_options(1), + $args -> {"type"}); + + my $statusopts = $self -> {"template"} -> build_optionlist($self -> {"system"} -> {"entities"} -> {"states"} -> as_options(1, visible => {value => 1}), + $args -> {"status"}); + + # Convert the time fields + my ($preptime, $prepsecs) = ("", 0); + if($args -> {"preptime"}) { + $prepsecs = $args -> {"preptime"} * 60; + $preptime = $self -> _build_timereq($prepsecs); + } + + my ($cooktime, $cooksecs) = ("", 0); + if($args -> {"cooktime"}) { + $cooksecs = $args -> {"cooktime"} * 60; + $cooktime = $self -> _build_timereq($cooksecs); + } + + # Convert tags - can't use build_optionlist because all of them need to be selected. + my $taglist = ""; + if($args -> {"tags"}) { + my @tags = split(/,/, $args -> {"tags"}); + + foreach my $tag (@tags) { + $taglist .= "\n"; + } + } + + # And squirt out the page content + my $body = $self -> {"template"} -> load_template("clone/content.tem", + { + "%(errors)s" => $errors, + "%(name)s" => $args -> {"name"} // "", + "%(source)s" => $args -> {"source"} // "", + "%(yield)s" => $args -> {"yield"} // "", + "%(prepinfo)s" => $args -> {"prepinfo"} // "", + "%(preptime)s" => $preptime, + "%(prepsecs)s" => $prepsecs, + "%(cooktime)s" => $cooktime, + "%(cooksecs)s" => $cooksecs, + "%(temp)s" => $args -> {"temp"} // "", + "%(temptypes)s" => $self -> {"template"} -> build_optionlist($temptypes, $args -> {"temptype"}), + "%(types)s" => $typeopts, + "%(units)s" => $unitopts, + "%(preps)s" => $prepopts, + "%(status)s" => $statusopts, + "%(tags)s" => $taglist, + "%(ingreds)s" => $ingredients, + "%(method)s" => $args -> {"method"} // "", + "%(notes)s" => $args -> {"notes"} // "", + }); + + return ($self -> {"template"} -> replace_langvar("CLONE_TITLE"), + $body, + $self -> {"template"} -> load_template("clone/extrahead.tem"), + $self -> {"template"} -> load_template("clone/extrajs.tem")); +} + + +## @method private @ _fatal_error($error) +# Generate the tile and content for an error page. +# +# @param error A string containing the error message to display +# @return The title of the error page and an error message to place in the page. +sub _fatal_error { + my $self = shift; + my $error = shift; + + return ("{L_VIEW_ERROR_FATAL}", + $self -> {"template"} -> load_template("error/page_error.tem", + { "%(message)s" => $error, + "%(url-logout)s" => $self -> build_url(block => "login", pathinfo => ["signout"]) + }) + ); +} + + +## @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; + + my @pathinfo = $self -> {"cgi"} -> multi_param("pathinfo"); + my ($title, $body, $extrahead, $extrajs) = $self -> _generate_clone($pathinfo[0]); + + # Done generating the page content, return the filled in page template + return $self -> generate_orb_page(title => $title, + content => $body, + extrahead => $extrahead, + extrajs => $extrajs, + active => '-', + doclink => 'clone'); +} + + +# ============================================================================ +# 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/recipe.lang b/lang/en/recipe.lang index d3b476f..1491159 100644 --- a/lang/en/recipe.lang +++ b/lang/en/recipe.lang @@ -6,6 +6,10 @@ EDIT_TITLE = Edit Recipe EDIT_EDIT = Edit Recipe EDIT_ERRORS = Unable to edit recipe; the following errors have been encountered: +CLONE_TITLE = Clone Recipe +CLONE_CLONE = Clone Recipe +CLONE_ERRORS = Unable to clone recipe; the following errors have been encountered: + RECIPE_NAME = Name RECIPE_NAME_DOC = The name of the recipe RECIPE_NAME_PH = Recipe name diff --git a/templates/default/clone/content.tem b/templates/default/clone/content.tem new file mode 100644 index 0000000..5cead7a --- /dev/null +++ b/templates/default/clone/content.tem @@ -0,0 +1,149 @@ +%(errors)s +
+
+

{L_EDIT_TITLE}

+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+
    +%(ingreds)s +
+ +
+ + + + Show menu + + +
+ +
+ +
+
+ +
+ +
+ +
+
+
+ diff --git a/templates/default/clone/extrahead.tem b/templates/default/clone/extrahead.tem new file mode 100644 index 0000000..ab77799 --- /dev/null +++ b/templates/default/clone/extrahead.tem @@ -0,0 +1 @@ + diff --git a/templates/default/clone/extrajs.tem b/templates/default/clone/extrajs.tem new file mode 100644 index 0000000..46c6e28 --- /dev/null +++ b/templates/default/clone/extrajs.tem @@ -0,0 +1,8 @@ + + + + +