From a3fe0cbde1628d25ba5f339be0e5bc50fde2d172 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 18 Sep 2016 10:25:15 +0100 Subject: [PATCH] Work towards recipe model --- modules/ORB/System/Recipe.pm | 126 +++++++++++++++++++++++++++++++---- 1 file changed, 112 insertions(+), 14 deletions(-) diff --git a/modules/ORB/System/Recipe.pm b/modules/ORB/System/Recipe.pm index 2361b71..b5ce171 100644 --- a/modules/ORB/System/Recipe.pm +++ b/modules/ORB/System/Recipe.pm @@ -39,7 +39,8 @@ # | viewed | int(10) unsigned | NO | | NULL | | # +------------+--------------------------------+------+-----+---------+----------------+ -# NOTE: ADD FIELDS: parent (int 10), metadata_id (int 10), remove update*, +# NOTE: ADD FIELDS: prev_id (int 10), metadata_id (int 10), remove update*, +# NOTE: Change source and notes fields to allow NULL # NOTE: Use status field for marking as edited? # +-----------+------------------+------+-----+---------+----------------+ @@ -92,18 +93,51 @@ sub new { # Recipe creation and deletion ## @method $ create(%args) +# Create a new recipe in the system, or edit a recipe setting the status of +# the old version to 'edited'. The args hash can contain the following, all +# fields are required unless indicated otherwise: # +# - `previd`: (optional) ID of the recipe this is an edit of. If specified, the +# old recipe has its state set to 'edited', and the metadata context +# of the new recipe is created as a child of the old recipe to ensure +# editing works as expected. +# - `name`: The name of the recipe +# - `source`: (optional) Where did the recipe come from originally? +# - `timereq`: A string describing the time required for the recipe +# - `timemins`: How long does the recipe take in minutes, in total? +# - `yield`: A string describing how much stuff the recipe creates +# - `temp`: (optional) Oven preheat temperature +# - `temptype`: The type of units used for `temp`: 'C', 'F', 'Gas mark', or 'N/A' +# - `method`: HTML text containing the recipe instructions +# - `notes`: (optional) Additional information about the recipe +# - `typeid`: The recipe type ID +# - `statusid`: The recipe status ID +# - `creatorid`: The ID of the user who created the recipe # +# @param args A hash, or reference to a hash, of values to use when creating +# the new recipe. +# @return A reference to a hash containing the new recipe ID on success, +# undef on error. sub create { my $self = shift; my $args = hash_or_hashref(@_); $self -> clear_error(); + # We need a metadata context for the recipe + my $metadataid = $self -> _create_recipe_metadata($args -> {"previd"}); + + my $newh = $self -> {"dbh"} -> prepare("INSERT INTO `".$self -> {"settings"} -> {"database"} -> {"recipes"}."` + (`metadata_id`, `prev_id`, `name`, `source`, `timereq`, `timemins`, `yield`, `temp`, `temptype`, `method`, `notes`, `type_id`, `status_id`, `creator_id`, `created`) + VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, UNIX_TIMESTAMP())"); + + } +# ============================================================================== +# Private methods ## @method private $ _add_recipe_ingredients($recipeid, $ingredients) # Add the specified ingredients to the recipe list for the specified recipe. This goes through @@ -132,7 +166,7 @@ sub _add_recipe_ingredients { # Otherwise, it's a real ingredient, so we need to do the more complex work } else { # obtain the ingredient id - my $ingid = $self -> {"ingredient"} -> get_ingred_id($ingred -> {"name"}) + my $ingid = $self -> {"ingredients"} -> get_id($ingred -> {"name"}) or return $self -> self_error("Unable to get ingreditent ID for '".$ingred -> {"name"}."': ".$self -> {"ingredient"} -> errstr()); # If we have an ID we can add the ingredient. @@ -140,7 +174,7 @@ sub _add_recipe_ingredients { or return $self -> self_error("Unable to add ingredient '".$ingred -> {"name"}."' to recipe '$recipeid': ".$self -> {"dbh"} -> errstr()); # And increase the ingredient refcount - $self -> {"ingredient"} -> increase_refcount_byid($ingid); + $self -> {"system"} -> {"ingredients"} -> increase_refcount($ingid); } } @@ -149,24 +183,36 @@ sub _add_recipe_ingredients { } -## @method $ add_recipe_tags($recipeid, $tags, $userid) +## @method $ add_recipe_tags($recipeid, $tags) # Add the specified tags to a recipe, setting the provided userid as the creator for new tags. # # @param recipeid The id of the recipe to add the tags to. -# @param tags A string containing a comma-delimited list of tags. -# @param userid The id of the user creating the recipe. +# @param tags A string containing a comma-delimited list of tags, or a reference to an +# array of tag names. +# @return true on success, undef on error sub add_recipe_tags { my $self = shift; my $recipeid = shift; my $tags = shift; - my $userid = shift; - # Bomb immediately if the tags list is empty - return undef if(!$tags || length($tags) == 0); + # exit immediately if the tags list is empty; this is not an error, as the + # recipe /can/ be untagged. + return 1 if(!$tags); - # Split the tag after removing any extraneous whitespace between tags - $tags =~ s/\s*,\s*/,/g; - my @values = split(/,/, $tags); + # If tags isn't a reference, assume it's a scalar string + if(!ref($tags)) { + return 1 unless(length($tags)); + + # Split the tag after removing any extraneous whitespace between tags + $tags =~ s/\s*,\s*/,/g; + my @values = split(/,/, $tags); + + $tags = \@values; + + # If $tags is a reference, it has to be an array! + } elsif(ref($tags) ne "ARRAY") { + return $self -> self_error("Unsupported reference passed to add_recipe_tags(). Giving up."); + } # Now we prepare the tag insert query for action my $addh = $self -> {"dbh"} -> prepare("INSERT INTO ".$self -> {"settings"} -> {"database"} -> {"recipetags"}." @@ -174,18 +220,70 @@ sub add_recipe_tags { VALUES(?, ?)"); # Go through each tag, adding it - foreach my $tag (@values) { + foreach my $tag (@{$tags}) { # Try to get the tag id - my $tagid = $self -> {"ingredient"} -> get_tag_id($tag, $userid) + my $tagid = $self -> {"system"} -> {"tags"} -> get_id($tag) or return $self -> self_error("Unable to obtain ID for tag '$tag'"); $addh -> execute($recipeid, $tagid) or return $self -> self_error("Tag association failed: ".$self -> {"dbh"} -> errstr); + + $self -> {"system"} -> {"tags"} -> increase_refcount($tagid) + or return $self -> self_error("Tag refcount change failed"); } return 1; } +# ============================================================================== +# Metadata related + +## @method $ get_recipe_metadata($recipeid) +# Given a recipe ID, fetch the ID of the metadata context associated with the +# recipe. +# +# @param recipeid The ID of the recipe to fetch the context ID for +# @return A metadata context ID on success, undef on error. +sub get_recipe_metadata { + my $self = shift; + my $recipeid = shift; + + $self -> clear_error(); + + my $metah = $self -> {"dbh"} -> prepare("SELECT `metadata_id` + FROM `".$self -> {"settings"} -> {"database"} -> {"recipes"}."` + WHERE `id` = ?"); + $metah -> execute($recipeid) + or return $self -> self_error("Unable to execute recipe metadata lookup query: ".$self -> {"dbh"} -> errstr); + + my $meta = $metah -> fetchrow_arrayref() + or return $self -> self_error("Request for non-existent recipe '$recipeid', giving up"); + + return $meta -> [0]; +} + +## @method private $ _create_recipe_metadata($previd) +# Create a metadata context for a new recipe. This will create the new context +# as a child of the metadata context for the specific previous recipe, if one +# is provided, to allow cascading permissions. +# +# @param previd Optional ID of the previous recipe. This should be set when +# editing a recipe; for new recipes, leave this as undef or 0. +# @return The ID of a new metadata context to attach the recipe to on success, +# undef on error. +sub _create_recipe_metadata { + my $self = shift; + my $previd = shift; + + if($previd) { + my $parentid = $self -> get_recipe_metadata($previd) + or return undef; + + return $self -> {"metadata"} -> create($parentid); + } + + return $self -> {"metadata"} -> create($self -> {"settings"} -> {"config"} -> {"Recipe:base_metadata"} // 1); +} 1;