Compare commits
10 Commits
5fb4fe09dc
...
c73ed0d98b
Author | SHA1 | Date | |
---|---|---|---|
c73ed0d98b | |||
50b7e883f3 | |||
6b08544066 | |||
7b4db43393 | |||
3097d93e37 | |||
4ba6df6917 | |||
a362496db2 | |||
8ff8532138 | |||
c7237fce36 | |||
38e07230c1 |
24
.htaccess
24
.htaccess
@ -30,18 +30,18 @@ AddOutputFilterByType DEFLATE application/x-javascript
|
|||||||
# For extra efficiency, make sure cache expiration times are set for content.
|
# For extra efficiency, make sure cache expiration times are set for content.
|
||||||
# For example, add the following to the webapp's <VirtualHost>:
|
# For example, add the following to the webapp's <VirtualHost>:
|
||||||
#
|
#
|
||||||
# ExpiresActive On
|
ExpiresActive On
|
||||||
# ExpiresDefault "access plus 300 seconds"
|
ExpiresDefault "access plus 300 seconds"
|
||||||
#
|
#
|
||||||
# And the followin on its <Directory>:
|
# And the followin on its <Directory>:
|
||||||
#
|
#
|
||||||
# ExpiresByType text/html "access plus 30 minutes"
|
ExpiresByType text/html "access plus 30 minutes"
|
||||||
# ExpiresByType text/css "access plus 1 day"
|
ExpiresByType text/css "access plus 10 days"
|
||||||
# ExpiresByType text/javascript "access plus 1 day"
|
ExpiresByType text/javascript "access plus 10 days"
|
||||||
# ExpiresByType image/gif "access plus 1 month"
|
ExpiresByType image/gif "access plus 1 month"
|
||||||
# ExpiresByType image/jpeg "access plus 1 month"
|
ExpiresByType image/jpeg "access plus 1 month"
|
||||||
# ExpiresByType image/jpg "access plus 1 month"
|
ExpiresByType image/jpg "access plus 1 month"
|
||||||
# ExpiresByType image/png "access plus 1 month"
|
ExpiresByType image/png "access plus 1 month"
|
||||||
# ExpiresByType application/x-shockwave-flash "access plus 1 day"
|
ExpiresByType application/x-shockwave-flash "access plus 10 days"
|
||||||
# ExpiresByType application/x-javascript "access plus 1 day"
|
ExpiresByType application/x-javascript "access plus 10 days"
|
||||||
# ExpiresByType application/x-icon "access plus 1 day"
|
ExpiresByType application/x-icon "access plus 10 days"
|
||||||
|
@ -249,6 +249,9 @@ sub _dispatch_ui {
|
|||||||
sub page_display {
|
sub page_display {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
|
my $error = $self -> check_login();
|
||||||
|
return $error if($error);
|
||||||
|
|
||||||
# Is this an API call, or a normal page operation?
|
# Is this an API call, or a normal page operation?
|
||||||
my $apiop = $self -> is_api_operation();
|
my $apiop = $self -> is_api_operation();
|
||||||
if(defined($apiop)) {
|
if(defined($apiop)) {
|
||||||
|
@ -300,7 +300,7 @@ sub _validate_ingredient {
|
|||||||
$ingredient -> {"notes"} = $self -> {"template"} -> html_clean($ingdata -> {"notes"});
|
$ingredient -> {"notes"} = $self -> {"template"} -> html_clean($ingdata -> {"notes"});
|
||||||
} else {
|
} else {
|
||||||
$errors .= $self -> {"template"} -> load_template("error/error_item.tem",
|
$errors .= $self -> {"template"} -> load_template("error/error_item.tem",
|
||||||
{ "%(error)s" => "{L_ERR_BADNOTES}" });
|
{ "%(error)s" => "{L_ERR_BADNOTES}: '".$ingdata -> {"notes"}."'" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,7 +426,7 @@ sub _validate_recipe {
|
|||||||
($args -> {"cooksecs"}, $error) = $self -> validate_numeric("cooksecs", { required => 1,
|
($args -> {"cooksecs"}, $error) = $self -> validate_numeric("cooksecs", { required => 1,
|
||||||
default => 0,
|
default => 0,
|
||||||
intonly => 1,
|
intonly => 1,
|
||||||
min => 1,
|
min => 0,
|
||||||
nicename => "{L_RECIPE_COOKTIME}"
|
nicename => "{L_RECIPE_COOKTIME}"
|
||||||
});
|
});
|
||||||
$errors .= $self -> {"template"} -> load_template("error/error_item.tem", { "%(error)s" => $error })
|
$errors .= $self -> {"template"} -> load_template("error/error_item.tem", { "%(error)s" => $error })
|
||||||
@ -511,4 +511,4 @@ sub _validate_recipe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -113,6 +113,9 @@ sub _dispatch_ui {
|
|||||||
sub page_display {
|
sub page_display {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
|
my $error = $self -> check_login();
|
||||||
|
return $error if($error);
|
||||||
|
|
||||||
# Is this an API call, or a normal page operation?
|
# Is this an API call, or a normal page operation?
|
||||||
my $apiop = $self -> is_api_operation();
|
my $apiop = $self -> is_api_operation();
|
||||||
if(defined($apiop)) {
|
if(defined($apiop)) {
|
||||||
|
@ -251,6 +251,9 @@ sub _dispatch_ui {
|
|||||||
sub page_display {
|
sub page_display {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
|
my $error = $self -> check_login();
|
||||||
|
return $error if($error);
|
||||||
|
|
||||||
# Is this an API call, or a normal page operation?
|
# Is this an API call, or a normal page operation?
|
||||||
my $apiop = $self -> is_api_operation();
|
my $apiop = $self -> is_api_operation();
|
||||||
if(defined($apiop)) {
|
if(defined($apiop)) {
|
||||||
|
@ -192,6 +192,9 @@ sub _dispatch_ui {
|
|||||||
sub page_display {
|
sub page_display {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
|
my $error = $self -> check_login();
|
||||||
|
return $error if($error);
|
||||||
|
|
||||||
# Is this an API call, or a normal page operation?
|
# Is this an API call, or a normal page operation?
|
||||||
my $apiop = $self -> is_api_operation();
|
my $apiop = $self -> is_api_operation();
|
||||||
if(defined($apiop)) {
|
if(defined($apiop)) {
|
||||||
|
120
blocks/ORB/Random.pm
Normal file
120
blocks/ORB/Random.pm
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
## @file
|
||||||
|
# This file contains the implementation of the delete 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::Random;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use parent qw(ORB::Common); # This class extends the ORB common class
|
||||||
|
use experimental qw(smartmatch);
|
||||||
|
use v5.14;
|
||||||
|
use JSON;
|
||||||
|
|
||||||
|
|
||||||
|
sub _generate_random {
|
||||||
|
my $self = shift;
|
||||||
|
my $type = shift // "Mains*";
|
||||||
|
my $errors;
|
||||||
|
|
||||||
|
$type =~ s/\*/%/g;
|
||||||
|
|
||||||
|
# Try to fetch the data.
|
||||||
|
my $recipe = $self -> {"system"} -> {"recipe"} -> get_random_recipe($type);
|
||||||
|
return $self -> _fatal_error("{L_RANDOM_FAILED_NOTFOUND}")
|
||||||
|
unless($recipe -> {"id"});
|
||||||
|
|
||||||
|
# Redirect to the view page for the recipe
|
||||||
|
return $self -> redirect($self -> build_url(block => "view",
|
||||||
|
pathinfo => [ $recipe -> {"id"} ],
|
||||||
|
params => "f=".time(),
|
||||||
|
api => []))
|
||||||
|
if(!$errors);
|
||||||
|
|
||||||
|
return $self -> _fatal_error($self -> {"template"} -> load_template("error/error_list.tem",
|
||||||
|
{
|
||||||
|
"%(message)s" => "{L_RANDOM_FAILED}",
|
||||||
|
"%(errors)s" => $errors
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
## @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_RANDOM_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_random($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 => 'random');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# 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;
|
@ -100,13 +100,12 @@ sub _build_search_results {
|
|||||||
my $term = shift;
|
my $term = shift;
|
||||||
my $origonly = shift // 1;
|
my $origonly = shift // 1;
|
||||||
|
|
||||||
my $recipes = $self -> {"system"} -> {"recipe"} -> find(name => $term,
|
my $recipes = $self -> {"system"} -> {"recipe"} -> find(name => '%'.$term.'%',
|
||||||
method => $term,
|
method => '%'.$term.'%',
|
||||||
ingredients => [ '%'.$term.'%' ],
|
ingredients => [ '%'.$term.'%' ],
|
||||||
ingredmatch => 'any',
|
ingredmatch => 'any',
|
||||||
tags => [ '%'.$term.'%' ],
|
tags => [ '%'.$term.'%' ],
|
||||||
tagmatch => 'any',
|
tagmatch => 'any',
|
||||||
limit => 50,
|
|
||||||
searchmode => 'any',
|
searchmode => 'any',
|
||||||
original => $origonly);
|
original => $origonly);
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ use strict;
|
|||||||
use parent qw(ORB);
|
use parent qw(ORB);
|
||||||
use experimental qw(smartmatch);
|
use experimental qw(smartmatch);
|
||||||
use v5.14;
|
use v5.14;
|
||||||
|
use Data::Dumper;
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# Bar generation
|
# Bar generation
|
||||||
@ -47,6 +47,8 @@ sub block_display {
|
|||||||
|
|
||||||
$self -> clear_error();
|
$self -> clear_error();
|
||||||
|
|
||||||
|
my $recipe = $self -> {"system"} -> {"recipe"} -> get_random_recipe("Mains%");
|
||||||
|
|
||||||
my $urls = { "%(url-signin)s" => $self -> build_url(block => "login",
|
my $urls = { "%(url-signin)s" => $self -> build_url(block => "login",
|
||||||
fullurl => 1,
|
fullurl => 1,
|
||||||
pathinfo => [],
|
pathinfo => [],
|
||||||
@ -96,6 +98,11 @@ sub block_display {
|
|||||||
pathinfo => [ ],
|
pathinfo => [ ],
|
||||||
params => {},
|
params => {},
|
||||||
forcessl => 1),
|
forcessl => 1),
|
||||||
|
"%(url-random)s" => $self -> build_url(block => "view",
|
||||||
|
fullurl => 1,
|
||||||
|
pathinfo => [ $recipe -> {"id"} ],
|
||||||
|
params => {},
|
||||||
|
forcessl => 1),
|
||||||
"%(url-shop)s" => $self -> build_url(block => "shop",
|
"%(url-shop)s" => $self -> build_url(block => "shop",
|
||||||
fullurl => 1,
|
fullurl => 1,
|
||||||
pathinfo => [ ],
|
pathinfo => [ ],
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"schemes": [
|
"schemes": [
|
||||||
"https"
|
"https"
|
||||||
],
|
],
|
||||||
"basePath": "/orb/rest/api",
|
"basePath": "/rest/api",
|
||||||
"produces": [
|
"produces": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
@ -228,4 +228,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,6 +114,101 @@ paths:
|
|||||||
description: Unexpected error
|
description: Unexpected error
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Error'
|
$ref: '#/definitions/Error'
|
||||||
|
'/menus/{name}':
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- menus
|
||||||
|
summary: Fetch the information for the specified menu
|
||||||
|
description: |
|
||||||
|
This will return the information for the specified menu.
|
||||||
|
parameters:
|
||||||
|
- name: name
|
||||||
|
description: The name of the menu to return the data for, or 'active' to retrieve the user's currently active menu
|
||||||
|
in: path
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
default: 'active'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: The details of the menu
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Menu'
|
||||||
|
'403':
|
||||||
|
description: Permission error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
'404':
|
||||||
|
description: Not found
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
default:
|
||||||
|
description: Unexpected error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- menus
|
||||||
|
summary: Create a menu with the specified name
|
||||||
|
description: |
|
||||||
|
This will create the menu, set it as the user's active menu, and return the information for the menu.
|
||||||
|
parameters:
|
||||||
|
- name: name
|
||||||
|
description: The name of the menu to create
|
||||||
|
in: path
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: The details of the menu
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Menu'
|
||||||
|
'403':
|
||||||
|
description: Permission error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
'404':
|
||||||
|
description: Not found
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
default:
|
||||||
|
description: Unexpected error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
'/menus/{name}/stage/{identifier}':
|
||||||
|
put:
|
||||||
|
tags:
|
||||||
|
- menus
|
||||||
|
summary: Stage a recipe on the specified menu
|
||||||
|
description: This will stage the recipe identified by {identifier} on the specified menu. If the menu name is 'current', the user's current menu is used.
|
||||||
|
parameters:
|
||||||
|
- name: name
|
||||||
|
description: The name of the menu to return the data for, or 'active' to retrieve the user's currently active menu
|
||||||
|
in: path
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
default: 'active'
|
||||||
|
- name: identifier
|
||||||
|
description: The identifier for the recipe - generally the recipe ID
|
||||||
|
in: path
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: The details of the menu
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Menu'
|
||||||
|
'403':
|
||||||
|
description: Permission error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
'404':
|
||||||
|
description: Not found
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
default:
|
||||||
|
description: Unexpected error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
definitions:
|
definitions:
|
||||||
Ingredient:
|
Ingredient:
|
||||||
type: object
|
type: object
|
||||||
@ -147,6 +242,12 @@ definitions:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/Tag'
|
$ref: '#/definitions/Tag'
|
||||||
|
Menu:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
description: The name of the menu
|
||||||
Token:
|
Token:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
use utf8;
|
use utf8;
|
||||||
use v5.12;
|
use v5.12;
|
||||||
use lib qw(/var/www/webperl);
|
|
||||||
use FindBin;
|
use FindBin;
|
||||||
|
|
||||||
our ($scriptpath, $fallbackpath, $contact);
|
our ($scriptpath, $fallbackpath, $contact);
|
||||||
|
@ -65,7 +65,7 @@ ERR_NAMEFORMAT = Unsupported characters in the recipe name: only alphanumerics
|
|||||||
ERR_TAGFORMAT = Unsupported characters in tag name: only alphanumerics, _, -, and space are supported.
|
ERR_TAGFORMAT = Unsupported characters in tag name: only alphanumerics, _, -, and space are supported.
|
||||||
ERR_BADSEPNAME = Unsupported characters in separator name: only alphanumerics, _, -, comma, space, and period are supported.
|
ERR_BADSEPNAME = Unsupported characters in separator name: only alphanumerics, _, -, comma, space, and period are supported.
|
||||||
ERR_BADQUANTITY = No quantity specified for ingredient.
|
ERR_BADQUANTITY = No quantity specified for ingredient.
|
||||||
ERR_BADNOTES = Unsupported characters in notes: only alphanumerics, (, ), _, -, comma, space, and period are supported.
|
ERR_BADNOTES = Unsupported characters in ingredient notes: only alphanumerics, (, ), _, -, comma, space, and period are supported.
|
||||||
ERR_JSONFORMAT = Unknown or corrupt data in ingredient JSON
|
ERR_JSONFORMAT = Unknown or corrupt data in ingredient JSON
|
||||||
|
|
||||||
EDIT_FAILED_BADID = Unable to edit recipe: the specified recipe identifier is invalid.
|
EDIT_FAILED_BADID = Unable to edit recipe: the specified recipe identifier is invalid.
|
||||||
|
@ -74,7 +74,7 @@ sub new {
|
|||||||
|
|
||||||
# Formats of accepted types
|
# Formats of accepted types
|
||||||
$self -> {"formats"} = {
|
$self -> {"formats"} = {
|
||||||
"recipename" => '^[-\w,. ]+$',
|
"recipename" => '^[-\w,.\' ]+$',
|
||||||
"tags" => '^[-\w ]+$',
|
"tags" => '^[-\w ]+$',
|
||||||
"quantity" => '^[\d\w./]+$',
|
"quantity" => '^[\d\w./]+$',
|
||||||
"sepname" => '^[-\w,.:()&;#*\ ]{1,255}$',
|
"sepname" => '^[-\w,.:()&;#*\ ]{1,255}$',
|
||||||
|
@ -26,6 +26,7 @@ package ORB::BlockSelector;
|
|||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use parent qw(Webperl::BlockSelector);
|
use parent qw(Webperl::BlockSelector);
|
||||||
|
use URI::Escape;
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Block Selection
|
# Block Selection
|
||||||
@ -72,6 +73,8 @@ sub get_block {
|
|||||||
|
|
||||||
# If path info is present, it needs to be shoved into the cgi object
|
# If path info is present, it needs to be shoved into the cgi object
|
||||||
if($pathinfo) {
|
if($pathinfo) {
|
||||||
|
($pathinfo) = uri_unescape($pathinfo) =~ m<^((:?/\*?\w+[-\*@+., \w]*)+)>;
|
||||||
|
|
||||||
# strip off the script if it is present
|
# strip off the script if it is present
|
||||||
$pathinfo =~ s|^(/media)?/index.cgi||;
|
$pathinfo =~ s|^(/media)?/index.cgi||;
|
||||||
|
|
||||||
|
@ -327,6 +327,7 @@ sub get_recipe_list {
|
|||||||
my $mode = shift;
|
my $mode = shift;
|
||||||
my $exlstates = shift // [ $self -> {"settings"} -> {"config"} -> {"Recipe:status:edited"} // "Edited",
|
my $exlstates = shift // [ $self -> {"settings"} -> {"config"} -> {"Recipe:status:edited"} // "Edited",
|
||||||
$self -> {"settings"} -> {"config"} -> {"Recipe:status:deleted"} // "Deleted",
|
$self -> {"settings"} -> {"config"} -> {"Recipe:status:deleted"} // "Deleted",
|
||||||
|
$self -> {"settings"} -> {"config"} -> {"Recipe:status:rejected"} // "Rejected",
|
||||||
];
|
];
|
||||||
|
|
||||||
$self -> clear_error();
|
$self -> clear_error();
|
||||||
@ -429,6 +430,53 @@ sub get_recipe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
## @method $ get_random_recipe($types, $exlstates)
|
||||||
|
# Fetch a random recipe from the system.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
sub get_random_recipe {
|
||||||
|
my $self = shift;
|
||||||
|
my $type = shift // "%";
|
||||||
|
my $exlstates = shift // [ $self -> {"settings"} -> {"config"} -> {"Recipe:status:edited"} // "Edited",
|
||||||
|
$self -> {"settings"} -> {"config"} -> {"Recipe:status:deleted"} // "Deleted",
|
||||||
|
$self -> {"settings"} -> {"config"} -> {"Recipe:status:rejected"} // "Rejected",
|
||||||
|
];
|
||||||
|
|
||||||
|
$self -> clear_error();
|
||||||
|
|
||||||
|
my @params = ( $type );
|
||||||
|
my @wherefrag = ( " `t`.`name` LIKE ? ");
|
||||||
|
|
||||||
|
# Get the status IDs for excluded states
|
||||||
|
my $stateids = $self -> _convert_states($exlstates);
|
||||||
|
if(scalar(@{$stateids})) {
|
||||||
|
push(@wherefrag, " `r`.`status_id` NOT IN (?".(",?" x (scalar(@{$stateids}) - 1)).") ");
|
||||||
|
push(@params, @{$stateids});
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build the where part of the query, if needed.
|
||||||
|
my $where = "";
|
||||||
|
$where = "WHERE ".join(" AND ", @wherefrag)
|
||||||
|
if(scalar(@wherefrag));
|
||||||
|
|
||||||
|
my $recipes = $self -> {"dbh"} -> prepare("SELECT `r`.`id`
|
||||||
|
FROM `".$self -> {"settings"} -> {"database"} -> {"recipes"}."` AS `r`
|
||||||
|
LEFT JOIN `".$self -> {"settings"} -> {"database"} -> {"types"}."` AS `t`
|
||||||
|
ON `t`.`id` = `r`.`type_id`
|
||||||
|
$where
|
||||||
|
ORDER BY RAND()
|
||||||
|
LIMIT 1");
|
||||||
|
$recipes -> execute(@params)
|
||||||
|
or return $self -> self_error("Unable to perform random recipe lookup: ".$self -> {"dbh"} -> errstr);
|
||||||
|
|
||||||
|
my $recipe = $recipes -> fetchrow_hashref();
|
||||||
|
return $self -> self_error("No random recipe selected. This should not happen!")
|
||||||
|
unless($recipe && $recipe -> {"id"});
|
||||||
|
|
||||||
|
return $self -> get_recipe($recipe -> {"id"});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
## @method $ get_history($recipeid, $originalid)
|
## @method $ get_history($recipeid, $originalid)
|
||||||
# Fetch the history for the specified recipe.
|
# Fetch the history for the specified recipe.
|
||||||
#
|
#
|
||||||
@ -650,6 +698,8 @@ sub find {
|
|||||||
ORDER BY $order
|
ORDER BY $order
|
||||||
$limit";
|
$limit";
|
||||||
|
|
||||||
|
print STDERR $query."\n".Dumper(@params);
|
||||||
|
|
||||||
my $search = $self -> {"dbh"} -> prepare($query);
|
my $search = $self -> {"dbh"} -> prepare($query);
|
||||||
$search -> execute(@params)
|
$search -> execute(@params)
|
||||||
or return $self -> self_error("Unable ot perform recipe search: ".$self -> {"dbh"} -> errstr);
|
or return $self -> self_error("Unable ot perform recipe search: ".$self -> {"dbh"} -> errstr);
|
||||||
|
@ -137,3 +137,7 @@ div.top-bar-right .menu>li {
|
|||||||
div.top-bar-right .dropdown.menu>li>.button{
|
div.top-bar-right .dropdown.menu>li>.button{
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.5rem 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button.alert {
|
||||||
|
background-color: #9A3728;
|
||||||
|
}
|
@ -18,7 +18,6 @@
|
|||||||
#ingredients li button
|
#ingredients li button
|
||||||
{
|
{
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
height: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ingredients .ui-state-highlight {
|
#ingredients .ui-state-highlight {
|
||||||
|
@ -33,7 +33,7 @@ ul.meta li.type,
|
|||||||
ul.meta li.status,
|
ul.meta li.status,
|
||||||
ul.meta li.time,
|
ul.meta li.time,
|
||||||
ul.meta li.temp {
|
ul.meta li.temp {
|
||||||
display: inline-block; color: #888; font-size: 0.7rem;
|
display: inline-block; color: #444; font-size: 0.7rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul.recipes li .tags {
|
ul.recipes li .tags {
|
||||||
@ -43,7 +43,7 @@ ul.recipes li .tags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ul.recipes li .intro {
|
ul.recipes li .intro {
|
||||||
color: #888;
|
color: #444;
|
||||||
padding-right: 0.2rem;
|
padding-right: 0.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@
|
|||||||
<li class="ingred">
|
<li class="ingred">
|
||||||
<div class="grid-x">
|
<div class="grid-x">
|
||||||
<div class="small-1 cell">
|
<div class="small-1 cell">
|
||||||
<input class="quantity" type="text" placeholder="{L_RECIPE_ING_QUANT_PH}" value="%(quantity)s" />
|
<input class="quantity" type="text" placeholder="{L_RECIPE_ING_QUANT_PH}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="small-2 cell">
|
<div class="small-2 cell">
|
||||||
<select class="units">
|
<select class="units">
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<div class="column row clearfix">
|
<div class="column row clearfix">
|
||||||
<div class="small input-group nomargin">
|
<div class="small input-group nomargin">
|
||||||
<input type="search" class="input-group-field" id="listfilter" placeholder="{L_LIST_FILTER}" />
|
<input type="search" class="input-group-field" id="listfilter" placeholder="{L_LIST_FILTER}" />
|
||||||
|
<label for="listfilter" class="show-for-sr">Filter</label>
|
||||||
<div class="input-group-button button-group">
|
<div class="input-group-button button-group">
|
||||||
<button class="button" type="button" data-toggle="sort-dropdown">{L_LIST_SORT}</button>
|
<button class="button" type="button" data-toggle="sort-dropdown">{L_LIST_SORT}</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -113,7 +113,7 @@
|
|||||||
<li class="ingred">
|
<li class="ingred">
|
||||||
<div class="grid-x">
|
<div class="grid-x">
|
||||||
<div class="small-1 cell">
|
<div class="small-1 cell">
|
||||||
<input class="quantity" type="text" placeholder="{L_RECIPE_ING_QUANT_PH}" value="%(quantity)s" />
|
<input class="quantity" type="text" placeholder="{L_RECIPE_ING_QUANT_PH}" value="" />
|
||||||
</div>
|
</div>
|
||||||
<div class="small-2 cell">
|
<div class="small-2 cell">
|
||||||
<select class="units">
|
<select class="units">
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta name="Description" content="Online Recipe Book. A recipe book, except it's online.">
|
||||||
<title>%(title)s</title>
|
<title>%(title)s</title>
|
||||||
<link rel="stylesheet" href="{V_[templatepath]}3rdparty/jquery-ui/jquery-ui.min.css" />
|
<link rel="stylesheet" href="{V_[templatepath]}3rdparty/jquery-ui/jquery-ui.min.css" />
|
||||||
<link rel="stylesheet" href="{V_[templatepath]}3rdparty/foundation/css/foundation.css" />
|
<link rel="stylesheet" href="{V_[templatepath]}3rdparty/foundation/css/foundation.css" />
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<ul class="dropdown menu" data-dropdown-menu>
|
<ul class="dropdown menu" data-dropdown-menu>
|
||||||
<li><a href="%(url-new)s" class="button"><i class="fas fa-plus"></i></a></li>
|
<li><a href="%(url-random)s" class="button" title="{L_RANDOM_RECIPE}"><i class="fas fa-dice"></i></a></li>
|
||||||
<li class="image is-dropdown-submenu-parent"><a href="#" class="nopadding"><img alt="" src="https://gravatar.com/avatar/%(gravhash)s?s=32&d=mm&r=g" class="avatar avatar-32" height="32" width="32" /></a>
|
<li><a href="%(url-new)s" class="button" title="{L_NEW_CREATE}"><i class="fas fa-file"></i></a></li>
|
||||||
|
<li class="image is-dropdown-submenu-parent"><a href="#" class="nopadding" title="%(realname)s"><img alt="" src="https://gravatar.com/avatar/%(gravhash)s?s=32&d=mm&r=g" class="avatar avatar-32" height="32" width="32" /></a>
|
||||||
<ul class="menu">
|
<ul class="menu">
|
||||||
<li><a href="%(url-setpass)s">Set Password</a></li>
|
<li><a href="%(url-setpass)s">Set Password</a></li>
|
||||||
<li><a href="%(url-signout)s">Sign out</a></li>
|
<li><a href="%(url-signout)s">Sign out</a></li>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<ul class="menu">
|
<ul class="menu">
|
||||||
|
<li><a href="%(url-random)s" class="button" title="{L_RANDOM_RECIPE}"><i class="fas fa-dice"></i></a></li>
|
||||||
%(signup)s
|
%(signup)s
|
||||||
<li><a class="button" href="%(url-signin)s">Sign in</a></li>
|
<li><a class="button" href="%(url-signin)s">Sign in</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<div class="top-bar hide-for-print">
|
<div class="top-bar hide-for-print">
|
||||||
<div class="top-bar-left">
|
<div class="top-bar-left">
|
||||||
<ul class="menu">
|
<ul class="menu">
|
||||||
<li><button id="menubtn" class="menu-icon" type="button" data-open="offCanvas"></button></li>
|
<li><button id="menubtn" class="menu-icon" type="button" data-open="offCanvas" aria-label="Menu Button"></button></li>
|
||||||
<li><form action="%(url-search)s" method="POST"><input type="search" name="search" id="search" placeholder="Search"></form></li>
|
<li><form action="%(url-search)s" method="POST"><input type="search" name="search" id="search" placeholder="Search"><label for="search" class="show-for-sr">Search</label></form></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="top-bar-right show-for-medium">
|
<div class="top-bar-right show-for-medium">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user