зеркало из https://github.com/nextcloud/cookbook.git
Basic refactoring and starting to bring everything up to php 7.0
e.g. type hinting whenever possible, camel case instead of snake case...
This commit is contained in:
Родитель
1a0b16913f
Коммит
fc35e1b2b0
|
@ -2,6 +2,9 @@
|
|||
|
||||
namespace OCA\Cookbook\Service;
|
||||
|
||||
use Exception;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Files\NotPermittedException;
|
||||
use OCP\Image;
|
||||
use OCP\IConfig;
|
||||
use OCP\Files\IRootFolder;
|
||||
|
@ -9,16 +12,23 @@ use OCP\Files\FileInfo;
|
|||
use OCP\Files\File;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
use OCA\Cookbook\Db\RecipeDb;
|
||||
use OCP\PreConditionNotMetException;
|
||||
|
||||
class RecipeService {
|
||||
/**
|
||||
* Main service class for the cookbook app.
|
||||
*
|
||||
* @package OCA\Cookbook\Service
|
||||
*/
|
||||
class RecipeService
|
||||
{
|
||||
private $root;
|
||||
private $userId;
|
||||
private $db;
|
||||
private $config;
|
||||
|
||||
public function __construct($UserId, IRootFolder $root, RecipeDb $db, IConfig $config) {
|
||||
public function __construct(string $UserId, IRootFolder $root, RecipeDb $db, IConfig $config)
|
||||
{
|
||||
$this->userId = $UserId;
|
||||
$this->root = $root;
|
||||
$this->db = $db;
|
||||
|
@ -26,14 +36,19 @@ class RecipeService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get a recipe by its folder id.
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return array
|
||||
* @return array|null
|
||||
*/
|
||||
public function getRecipeById($id) {
|
||||
public function getRecipeById(int $id)
|
||||
{
|
||||
$file = $this->getRecipeFileByFolderId($id);
|
||||
|
||||
if(!$file) { return null; }
|
||||
if (!$file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->parseRecipeFile($file);
|
||||
}
|
||||
|
@ -43,20 +58,27 @@ class RecipeService {
|
|||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return \OCP\Files\File
|
||||
* @return File|null
|
||||
*/
|
||||
public function getRecipeFileByFolderId($id) {
|
||||
$user_folder = $this->getFolderForUser();
|
||||
$recipe_folder = $user_folder->getById($id);
|
||||
public function getRecipeFileByFolderId(int $id)
|
||||
{
|
||||
$userFolder = $this->getFolderForUser();
|
||||
$recipeFolder = $userFolder->getById($id);
|
||||
|
||||
if(count($recipe_folder) <= 0) { return null; }
|
||||
if (count($recipeFolder) <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$recipe_folder = $recipe_folder[0];
|
||||
$recipeFolder = $recipeFolder[0];
|
||||
|
||||
if($recipe_folder instanceof Folder === false) { return null; }
|
||||
if ($recipeFolder instanceof Folder === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach($recipe_folder->getDirectoryListing() as $file) {
|
||||
if($this->isRecipeFile($file)) { return $file; }
|
||||
foreach ($recipeFolder->getDirectoryListing() as $file) {
|
||||
if ($this->isRecipeFile($file)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -69,13 +91,14 @@ class RecipeService {
|
|||
* @param array $json
|
||||
* @param string $key
|
||||
*/
|
||||
private function validateDuration($json, $key) {
|
||||
private function validateDuration($json, $key)
|
||||
{
|
||||
// Make sure we have a string and valid DateInterval
|
||||
// regex validation from here: https://stackoverflow.com/a/32045167
|
||||
$interval_regex = "/^P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?$/";
|
||||
if(isset($json[$key]) && is_string($json[$key])) {
|
||||
if (isset($json[$key]) && is_string($json[$key])) {
|
||||
$time_string = $this->cleanUpString($json[$key]);
|
||||
if(preg_match_all($interval_regex, $time_string)) {
|
||||
if (preg_match_all($interval_regex, $time_string)) {
|
||||
$json[$key] = $time_string;
|
||||
} else {
|
||||
$json[$key] = "";
|
||||
|
@ -92,9 +115,14 @@ class RecipeService {
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
public function checkRecipe($json) {
|
||||
if(!$json) { throw new \Exception('Recipe array was null'); }
|
||||
if(!isset($json['name']) || !$json['name']) { throw new \Exception('Field "name" is required'); }
|
||||
public function checkRecipe($json)
|
||||
{
|
||||
if (!$json) {
|
||||
throw new Exception('Recipe array was null');
|
||||
}
|
||||
if (!isset($json['name']) || !$json['name']) {
|
||||
throw new Exception('Field "name" is required');
|
||||
}
|
||||
|
||||
// Make sure the schema.org fields are present
|
||||
$json['@context'] = 'http://schema.org';
|
||||
|
@ -104,19 +132,19 @@ class RecipeService {
|
|||
$json['name'] = $this->cleanUpString($json['name']);
|
||||
|
||||
// Make sure that "image" is a string of the highest resolution image available
|
||||
if(isset($json['image']) && $json['image']) {
|
||||
if(is_array($json['image'])) {
|
||||
if (isset($json['image']) && $json['image']) {
|
||||
if (is_array($json['image'])) {
|
||||
// Get the image from a subproperty "url"
|
||||
if(isset($json['image']['url'])) {
|
||||
if (isset($json['image']['url'])) {
|
||||
$json['image'] = $json['image']['url'];
|
||||
|
||||
// Try to get the image with the highest resolution by adding together all numbers in the url
|
||||
// Try to get the image with the highest resolution by adding together all numbers in the url
|
||||
} else {
|
||||
$images = $json['image'];
|
||||
$image_size = 0;
|
||||
|
||||
foreach($images as $img) {
|
||||
if(is_array($img) && isset($img['url'])) {
|
||||
foreach ($images as $img) {
|
||||
if (is_array($img) && isset($img['url'])) {
|
||||
$img = $img['url'];
|
||||
}
|
||||
|
||||
|
@ -126,16 +154,16 @@ class RecipeService {
|
|||
|
||||
$this_image_size = 0;
|
||||
|
||||
foreach($image_matches as $image_match) {
|
||||
$this_image_size += (int) $image_match;
|
||||
foreach ($image_matches as $image_match) {
|
||||
$this_image_size += (int)$image_match;
|
||||
}
|
||||
|
||||
if($image_size === 0 || $this_image_size > $image_size) {
|
||||
if ($image_size === 0 || $this_image_size > $image_size) {
|
||||
$json['image'] = $img;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(!is_string($json['image'])) {
|
||||
} else if (!is_string($json['image'])) {
|
||||
$json['image'] = '';
|
||||
}
|
||||
} else {
|
||||
|
@ -146,11 +174,11 @@ class RecipeService {
|
|||
$json['image'] = stripslashes($json['image']);
|
||||
|
||||
// Make sure that "recipeYield" is an integer which is at least 1
|
||||
if(isset($json['recipeYield']) && $json['recipeYield']) {
|
||||
if (isset($json['recipeYield']) && $json['recipeYield']) {
|
||||
$yield = filter_var($json['recipeYield'], FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
if($yield && $yield > 0) {
|
||||
$json['recipeYield'] = (int) $yield;
|
||||
if ($yield && $yield > 0) {
|
||||
$json['recipeYield'] = (int)$yield;
|
||||
} else {
|
||||
$json['recipeYield'] = 1;
|
||||
}
|
||||
|
@ -159,7 +187,7 @@ class RecipeService {
|
|||
}
|
||||
|
||||
// Make sure that "keywords" is an array of unique strings
|
||||
if(isset($json['keywords']) && is_string($json['keywords'])) {
|
||||
if (isset($json['keywords']) && is_string($json['keywords'])) {
|
||||
$keywords = trim($json['keywords']);
|
||||
$keywords = trim($keywords, ',');
|
||||
$keywords = trim($keywords);
|
||||
|
@ -176,13 +204,15 @@ class RecipeService {
|
|||
}
|
||||
|
||||
// Make sure that "recipeIngredient" is an array of strings
|
||||
if(isset($json['recipeIngredient']) && is_array($json['recipeIngredient'])) {
|
||||
if (isset($json['recipeIngredient']) && is_array($json['recipeIngredient'])) {
|
||||
$ingredients = [];
|
||||
|
||||
foreach($json['recipeIngredient'] as $i => $ingredient) {
|
||||
foreach ($json['recipeIngredient'] as $i => $ingredient) {
|
||||
$ingredient = $this->cleanUpString($ingredient);
|
||||
|
||||
if(!$ingredient) { continue; }
|
||||
if (!$ingredient) {
|
||||
continue;
|
||||
}
|
||||
|
||||
array_push($ingredients, $ingredient);
|
||||
}
|
||||
|
@ -193,19 +223,19 @@ class RecipeService {
|
|||
$json['recipeIngredient'] = array_filter($json['recipeIngredient']);
|
||||
|
||||
// Make sure that "recipeInstructions" is an array of strings
|
||||
if(isset($json['recipeInstructions'])) {
|
||||
if(is_array($json['recipeInstructions'])) {
|
||||
foreach($json['recipeInstructions'] as $i => $step) {
|
||||
if(is_string($step)) {
|
||||
if (isset($json['recipeInstructions'])) {
|
||||
if (is_array($json['recipeInstructions'])) {
|
||||
foreach ($json['recipeInstructions'] as $i => $step) {
|
||||
if (is_string($step)) {
|
||||
$json['recipeInstructions'][$i] = $this->cleanUpString($step, true);
|
||||
} else if(is_array($step) && isset($step['text'])) {
|
||||
} else if (is_array($step) && isset($step['text'])) {
|
||||
$json['recipeInstructions'][$i] = $this->cleanUpString($step['text'], true);
|
||||
} else {
|
||||
$json['recipeInstructions'][$i] = '';
|
||||
}
|
||||
}
|
||||
|
||||
} else if(is_string($json['recipeInstructions'])) {
|
||||
} else if (is_string($json['recipeInstructions'])) {
|
||||
$json['recipeInstructions'] = html_entity_decode($json['recipeInstructions']);
|
||||
|
||||
$regex_matches = [];
|
||||
|
@ -213,17 +243,21 @@ class RecipeService {
|
|||
|
||||
$instructions = [];
|
||||
|
||||
foreach($regex_matches as $regex_match) {
|
||||
if(!$regex_match || !isset($regex_match[2])) { continue; }
|
||||
foreach ($regex_matches as $regex_match) {
|
||||
if (!$regex_match || !isset($regex_match[2])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$step = $this->cleanUpString($regex_match[2]);
|
||||
|
||||
if(!$step) { continue; }
|
||||
if (!$step) {
|
||||
continue;
|
||||
}
|
||||
|
||||
array_push($instructions, $step);
|
||||
}
|
||||
|
||||
if(sizeof($instructions) > 0) {
|
||||
if (sizeof($instructions) > 0) {
|
||||
$json['recipeInstructions'] = $instructions;
|
||||
} else {
|
||||
$json['recipeInstructions'] = explode(PHP_EOL, $json['recipeInstructions']);
|
||||
|
@ -235,19 +269,19 @@ class RecipeService {
|
|||
$json['recipeInstructions'] = [];
|
||||
}
|
||||
|
||||
$json['recipeInstructions'] = array_filter($json['recipeInstructions'], function($v) {
|
||||
$json['recipeInstructions'] = array_filter($json['recipeInstructions'], function ($v) {
|
||||
return !empty($v) && $v !== "\n" && $v !== "\r";
|
||||
});
|
||||
|
||||
// Make sure the 'description' is a string
|
||||
if(isset($json['description']) && is_string($json['description'])) {
|
||||
$json['description'] = $this->cleanUpString($json['description']);
|
||||
} else {
|
||||
$json['description'] = "";
|
||||
}
|
||||
// Make sure the 'description' is a string
|
||||
if (isset($json['description']) && is_string($json['description'])) {
|
||||
$json['description'] = $this->cleanUpString($json['description']);
|
||||
} else {
|
||||
$json['description'] = "";
|
||||
}
|
||||
|
||||
// Make sure the 'url' is a URL, or blank
|
||||
if(isset($json['url']) && $json['url']) {
|
||||
// Make sure the 'url' is a URL, or blank
|
||||
if (isset($json['url']) && $json['url']) {
|
||||
$url = filter_var($json['url'], FILTER_SANITIZE_URL);
|
||||
if (filter_var($url, FILTER_VALIDATE_URL) == false) {
|
||||
$url = "";
|
||||
|
@ -269,34 +303,45 @@ class RecipeService {
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
private function parseRecipeHtml($html) {
|
||||
if(!$html) { return null; }
|
||||
private function parseRecipeHtml($html)
|
||||
{
|
||||
if (!$html) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//$html = str_replace(["\r", "\n", "\t"], '', $html);
|
||||
$json_matches = [];
|
||||
|
||||
// Parse JSON
|
||||
preg_match_all('/<script type=["|\']application\/ld\+json["|\'][^>]*>([\s\S]*?)<\/script>/', $html, $json_matches, PREG_SET_ORDER);
|
||||
foreach($json_matches as $json_match) {
|
||||
if(!$json_match || !isset($json_match[1])) { continue; }
|
||||
foreach ($json_matches as $json_match) {
|
||||
if (!$json_match || !isset($json_match[1])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$string = $json_match[1];
|
||||
|
||||
if(!$string) { continue; }
|
||||
if (!$string) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$json = json_decode($string, true);
|
||||
|
||||
// Look through @graph field for recipe
|
||||
if($json && isset($json['@graph']) && is_array($json['@graph'])) {
|
||||
foreach($json['@graph'] as $graph_item) {
|
||||
if(!isset($graph_item['@type']) || $graph_item['@type'] !== 'Recipe') { continue; }
|
||||
if ($json && isset($json['@graph']) && is_array($json['@graph'])) {
|
||||
foreach ($json['@graph'] as $graph_item) {
|
||||
if (!isset($graph_item['@type']) || $graph_item['@type'] !== 'Recipe') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$json = $graph_item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$json || !isset($json['@type']) || $json['@type'] !== 'Recipe') { continue; }
|
||||
if (!$json || !isset($json['@type']) || $json['@type'] !== 'Recipe') {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $this->checkRecipe($json);
|
||||
}
|
||||
|
@ -306,8 +351,8 @@ class RecipeService {
|
|||
$article_matches = [];
|
||||
preg_match_all('/<article.*itemtype=".*Recipe".*>([\s\S]*?)<\/article>/', $html, $article_matches);
|
||||
|
||||
if(!isset($article_matches[1][0])) {
|
||||
throw new \Exception('Could not find article element');
|
||||
if (!isset($article_matches[1][0])) {
|
||||
throw new Exception('Could not find article element');
|
||||
}
|
||||
|
||||
$article_html = $article_matches[1][0];
|
||||
|
@ -323,59 +368,79 @@ class RecipeService {
|
|||
|
||||
$prop_matches = [];
|
||||
|
||||
foreach($props as $prop) {
|
||||
foreach ($props as $prop) {
|
||||
preg_match_all('/itemprop="' . $prop . '".*>(.*?)<\//', $article_html, $prop_matches, PREG_SET_ORDER);
|
||||
|
||||
foreach($prop_matches as $prop_match) {
|
||||
if(!$prop_match || !isset($prop_match[1])) { continue; }
|
||||
foreach ($prop_matches as $prop_match) {
|
||||
if (!$prop_match || !isset($prop_match[1])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = $prop_match[1];
|
||||
|
||||
switch($prop) {
|
||||
case 'image': case 'images': case 'thumbnail':
|
||||
$prop = 'image';
|
||||
$src_matches = [];
|
||||
preg_match('/="http([^"]+)"/', $prop_match[0], $src_matches);
|
||||
switch ($prop) {
|
||||
case 'image':
|
||||
case 'images':
|
||||
case 'thumbnail':
|
||||
$prop = 'image';
|
||||
$src_matches = [];
|
||||
preg_match('/="http([^"]+)"/', $prop_match[0], $src_matches);
|
||||
|
||||
if(!isset($src_matches[1])) { break; }
|
||||
if (!isset($src_matches[1])) {
|
||||
break;
|
||||
}
|
||||
|
||||
$src = 'http' . $src_matches[1];
|
||||
$src = 'http' . $src_matches[1];
|
||||
|
||||
if(isset($json[$prop]) && strlen($json[$prop]) < strlen($src)) { break; }
|
||||
if (isset($json[$prop]) && strlen($json[$prop]) < strlen($src)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$json[$prop] = $src;
|
||||
break;
|
||||
$json[$prop] = $src;
|
||||
break;
|
||||
|
||||
case 'recipeIngredient': case 'ingredients':
|
||||
$prop = 'recipeIngredient';
|
||||
if(!$json[$prop]) { $json[$prop] = []; }
|
||||
case 'recipeIngredient':
|
||||
case 'ingredients':
|
||||
$prop = 'recipeIngredient';
|
||||
if (!$json[$prop]) {
|
||||
$json[$prop] = [];
|
||||
}
|
||||
|
||||
array_push($json[$prop], $value);
|
||||
break;
|
||||
array_push($json[$prop], $value);
|
||||
break;
|
||||
|
||||
case 'recipeInstructions': case 'instructions': case 'steps': case 'guide':
|
||||
$prop = 'recipeInstructions';
|
||||
if(!$json[$prop]) { $json[$prop] = []; }
|
||||
case 'recipeInstructions':
|
||||
case 'instructions':
|
||||
case 'steps':
|
||||
case 'guide':
|
||||
$prop = 'recipeInstructions';
|
||||
if (!$json[$prop]) {
|
||||
$json[$prop] = [];
|
||||
}
|
||||
|
||||
array_push($json[$prop], $value);
|
||||
break;
|
||||
array_push($json[$prop], $value);
|
||||
break;
|
||||
|
||||
default:
|
||||
if(isset($json[$prop]) && $json[$prop]) { break; }
|
||||
default:
|
||||
if (isset($json[$prop]) && $json[$prop]) {
|
||||
break;
|
||||
}
|
||||
|
||||
$json[$prop] = $value;
|
||||
$json[$prop] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make one final desparate attempt at getting the instructions
|
||||
if(!isset($json['recipeInstructions']) || !$json['recipeInstructions'] || sizeof($json['recipeInstructions']) < 1) {
|
||||
if (!isset($json['recipeInstructions']) || !$json['recipeInstructions'] || sizeof($json['recipeInstructions']) < 1) {
|
||||
$step_matches = [];
|
||||
$json['recipeInstructions'] = [];
|
||||
preg_match_all('/<p.*>(.*?)<\/p>/', $article_html, $step_matches, PREG_SET_ORDER);
|
||||
|
||||
foreach($step_matches as $step_match) {
|
||||
if(!$step_match || !isset($step_match[1])) { continue; }
|
||||
foreach ($step_matches as $step_match) {
|
||||
if (!$step_match || !isset($step_match[1])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = $step_match[1];
|
||||
|
||||
|
@ -384,17 +449,19 @@ class RecipeService {
|
|||
}
|
||||
|
||||
// If no keywords were found, use the ingredients
|
||||
if(!isset($json['keywords']) || !$json['keywords']) {
|
||||
if (!isset($json['keywords']) || !$json['keywords']) {
|
||||
$json['keywords'] = '';
|
||||
|
||||
if(isset($json['recipeIngredient'])) {
|
||||
foreach($json['recipeIngredient'] as $ingredient) {
|
||||
if (isset($json['recipeIngredient'])) {
|
||||
foreach ($json['recipeIngredient'] as $ingredient) {
|
||||
$keyword = strip_tags($ingredient);
|
||||
$keyword = strtolower($keyword);
|
||||
$parts = array_filter(explode(' ', $keyword));
|
||||
$keyword = array_pop($parts);
|
||||
|
||||
if($json['keywords']) { $json['keywords'] .= ','; }
|
||||
if ($json['keywords']) {
|
||||
$json['keywords'] .= ',';
|
||||
}
|
||||
|
||||
$json['keywords'] .= $keyword;
|
||||
}
|
||||
|
@ -407,11 +474,12 @@ class RecipeService {
|
|||
/**
|
||||
* @param int $id
|
||||
*/
|
||||
public function deleteRecipe(int $id) {
|
||||
public function deleteRecipe(int $id)
|
||||
{
|
||||
$user_folder = $this->getFolderForUser();
|
||||
$recipe_folder = $user_folder->getById($id);
|
||||
|
||||
if($recipe_folder && sizeof($recipe_folder) > 0) {
|
||||
if ($recipe_folder && sizeof($recipe_folder) > 0) {
|
||||
$recipe_folder[0]->delete();
|
||||
}
|
||||
|
||||
|
@ -421,10 +489,13 @@ class RecipeService {
|
|||
/**
|
||||
* @param array $json
|
||||
*
|
||||
* @return \OCP\Files\File
|
||||
* @return File
|
||||
*/
|
||||
public function addRecipe($json) {
|
||||
if(!$json || !isset($json['name']) || !$json['name']) { throw new \Exception('Recipe name not found'); }
|
||||
public function addRecipe($json)
|
||||
{
|
||||
if (!$json || !isset($json['name']) || !$json['name']) {
|
||||
throw new Exception('Recipe name not found');
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
$json = $this->checkRecipe($json);
|
||||
|
@ -435,13 +506,13 @@ class RecipeService {
|
|||
$recipe_file = null;
|
||||
|
||||
try {
|
||||
if(isset($json['id']) && $json['id']) {
|
||||
if (isset($json['id']) && $json['id']) {
|
||||
$recipe_folder = $user_folder->getById($json['id'])[0];
|
||||
|
||||
$old_path = $recipe_folder->getPath();
|
||||
$new_path = dirname($old_path) . '/' . $json['name'];
|
||||
|
||||
if($old_path !== $new_path) {
|
||||
if ($old_path !== $new_path) {
|
||||
$recipe_folder->move($new_path);
|
||||
}
|
||||
|
||||
|
@ -449,13 +520,13 @@ class RecipeService {
|
|||
$recipe_folder = $user_folder->get($json['name']);
|
||||
}
|
||||
|
||||
} catch(\OCP\Files\NotFoundException $e) {
|
||||
} catch (NotFoundException $e) {
|
||||
$recipe_folder = $user_folder->newFolder($json['name']);
|
||||
}
|
||||
|
||||
$recipe_file = $this->getRecipeFileByFolderId($recipe_folder->getId());
|
||||
|
||||
if(!$recipe_file) {
|
||||
if (!$recipe_file) {
|
||||
$recipe_file = $recipe_folder->newFile($json['name'] . '.json');
|
||||
}
|
||||
|
||||
|
@ -467,8 +538,8 @@ class RecipeService {
|
|||
// Download image and generate thumbnail
|
||||
$full_image_data = null;
|
||||
|
||||
if(isset($json['image']) && $json['image']) {
|
||||
if(strpos($json['image'], 'http') === 0) {
|
||||
if (isset($json['image']) && $json['image']) {
|
||||
if (strpos($json['image'], 'http') === 0) {
|
||||
$json['image'] = str_replace(' ', '%20', $json['image']);
|
||||
$full_image_data = file_get_contents($json['image']);
|
||||
} else {
|
||||
|
@ -478,11 +549,11 @@ class RecipeService {
|
|||
}
|
||||
|
||||
// If image data was fetched, write it to disk
|
||||
if($full_image_data) {
|
||||
if ($full_image_data) {
|
||||
// Write the full image
|
||||
try {
|
||||
$full_image_file = $recipe_folder->get('full.jpg');
|
||||
} catch(\OCP\Files\NotFoundException $e) {
|
||||
} catch (NotFoundException $e) {
|
||||
$full_image_file = $recipe_folder->newFile('full.jpg');
|
||||
}
|
||||
|
||||
|
@ -496,7 +567,7 @@ class RecipeService {
|
|||
|
||||
try {
|
||||
$thumb_image_file = $recipe_folder->get('thumb.jpg');
|
||||
} catch(\OCP\Files\NotFoundException $e) {
|
||||
} catch (NotFoundException $e) {
|
||||
$thumb_image_file = $recipe_folder->newFile('thumb.jpg');
|
||||
}
|
||||
|
||||
|
@ -509,28 +580,35 @@ class RecipeService {
|
|||
/**
|
||||
* @param string $url
|
||||
*
|
||||
* @return \OCP\Files\File
|
||||
* @return File
|
||||
*/
|
||||
public function downloadRecipe($url) {
|
||||
public function downloadRecipe($url)
|
||||
{
|
||||
$host = parse_url($url);
|
||||
|
||||
if(!$host) { throw new \Exception('Could not parse URL'); }
|
||||
if (!$host) {
|
||||
throw new Exception('Could not parse URL');
|
||||
}
|
||||
|
||||
$opts = [
|
||||
"http" => [
|
||||
"method" => "GET",
|
||||
"header" => "User-Agent: Nextcloud Cookbook App"
|
||||
]
|
||||
];
|
||||
$context = stream_context_create($opts);
|
||||
"http" => [
|
||||
"method" => "GET",
|
||||
"header" => "User-Agent: Nextcloud Cookbook App"
|
||||
]
|
||||
];
|
||||
$context = stream_context_create($opts);
|
||||
|
||||
$html = file_get_contents($url, false, $context);
|
||||
|
||||
if(!$html) { throw new \Exception('Could not fetch site ' . $url); }
|
||||
if (!$html) {
|
||||
throw new Exception('Could not fetch site ' . $url);
|
||||
}
|
||||
|
||||
$json = $this->parseRecipeHtml($html);
|
||||
|
||||
if(!$json) { throw new \Exception('No recipe data found'); }
|
||||
if (!$json) {
|
||||
throw new Exception('No recipe data found');
|
||||
}
|
||||
|
||||
$json['url'] = $url;
|
||||
|
||||
|
@ -540,15 +618,18 @@ class RecipeService {
|
|||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRecipeFiles() {
|
||||
public function getRecipeFiles()
|
||||
{
|
||||
$user_folder = $this->getFolderForUser();
|
||||
$recipe_folders = $user_folder->getDirectoryListing();
|
||||
$recipe_files = [];
|
||||
|
||||
foreach($recipe_folders as $recipe_folder) {
|
||||
foreach ($recipe_folders as $recipe_folder) {
|
||||
$recipe_file = $this->getRecipeFileByFolderId($recipe_folder->getId());
|
||||
|
||||
if(!$recipe_file) { continue; }
|
||||
if (!$recipe_file) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$recipe_files[] = $recipe_file;
|
||||
}
|
||||
|
@ -559,7 +640,8 @@ class RecipeService {
|
|||
/**
|
||||
* Rebuilds the search index
|
||||
*/
|
||||
public function rebuildSearchIndex() {
|
||||
public function rebuildSearchIndex()
|
||||
{
|
||||
// Clear the database
|
||||
$this->db->emptySearchIndex($this->userId);
|
||||
|
||||
|
@ -570,20 +652,21 @@ class RecipeService {
|
|||
/**
|
||||
* Updates the search index
|
||||
*/
|
||||
public function updateSearchIndex() {
|
||||
public function updateSearchIndex()
|
||||
{
|
||||
// Remove old cache folder if needed
|
||||
$legacy_cache_path = '/cookbook/cache';
|
||||
|
||||
if($this->root->nodeExists($legacy_cache_path)) {
|
||||
if ($this->root->nodeExists($legacy_cache_path)) {
|
||||
$this->root->get($legacy_cache_path)->delete();
|
||||
}
|
||||
|
||||
// Restructure files if needed
|
||||
$user_folder = $this->getFolderForUser();
|
||||
|
||||
foreach($user_folder->getDirectoryListing() as $node) {
|
||||
foreach ($user_folder->getDirectoryListing() as $node) {
|
||||
// Move JSON files from the user directory into its own folder
|
||||
if($this->isRecipeFile($node)) {
|
||||
if ($this->isRecipeFile($node)) {
|
||||
$recipe_name = str_replace('.json', '', $node->getName());
|
||||
|
||||
$node->move($node->getPath() . '_tmp');
|
||||
|
@ -592,15 +675,15 @@ class RecipeService {
|
|||
|
||||
$node->move($recipe_folder->getPath() . '/' . $recipe_name . '.json');
|
||||
|
||||
// Rename folders with .json extensions (this was likely caused by a migration bug)
|
||||
} else if($node instanceof Folder && strpos($node->getName(), '.json')) {
|
||||
// Rename folders with .json extensions (this was likely caused by a migration bug)
|
||||
} else if ($node instanceof Folder && strpos($node->getName(), '.json')) {
|
||||
$node->move(str_replace('.json', '', $node->getPath()));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Re-index recipe files
|
||||
foreach($this->getRecipeFiles() as $file) {
|
||||
foreach ($this->getRecipeFiles() as $file) {
|
||||
$this->db->indexRecipeFile($file, $this->userId);
|
||||
}
|
||||
|
||||
|
@ -611,11 +694,12 @@ class RecipeService {
|
|||
/**
|
||||
* Checks if a search index update is needed and performs it
|
||||
*/
|
||||
private function checkSearchIndexUpdate() {
|
||||
private function checkSearchIndexUpdate()
|
||||
{
|
||||
$last_index_update = $this->getSearchIndexLastUpdateTime();
|
||||
$interval = $this->getSearchIndexUpdateInterval();
|
||||
|
||||
if($last_index_update < 1 || time() > $last_index_update + ($interval * 60)) {
|
||||
if ($last_index_update < 1 || time() > $last_index_update + ($interval * 60)) {
|
||||
$this->updateSearchIndex();
|
||||
}
|
||||
}
|
||||
|
@ -623,8 +707,9 @@ class RecipeService {
|
|||
/**
|
||||
* Gets the last time the search index was updated
|
||||
*/
|
||||
public function getSearchIndexLastUpdateTime() {
|
||||
return (int) $this->config->getUserValue($this->userId, 'cookbook', 'last_index_update');
|
||||
public function getSearchIndexLastUpdateTime()
|
||||
{
|
||||
return (int)$this->config->getUserValue($this->userId, 'cookbook', 'last_index_update');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -632,7 +717,8 @@ class RecipeService {
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAllKeywordsInSearchIndex() {
|
||||
public function getAllKeywordsInSearchIndex()
|
||||
{
|
||||
$this->checkSearchIndexUpdate();
|
||||
|
||||
return $this->db->findAllKeywords($this->userId);
|
||||
|
@ -643,7 +729,8 @@ class RecipeService {
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAllRecipesInSearchIndex() {
|
||||
public function getAllRecipesInSearchIndex()
|
||||
{
|
||||
$this->checkSearchIndexUpdate();
|
||||
|
||||
return $this->db->findAllRecipes($this->userId);
|
||||
|
@ -656,14 +743,15 @@ class RecipeService {
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
public function findRecipesInSearchIndex($keywords_string) {
|
||||
public function findRecipesInSearchIndex($keywords_string)
|
||||
{
|
||||
$this->checkSearchIndexUpdate();
|
||||
|
||||
$keywords_string = strtolower($keywords_string);
|
||||
$keywords_array = [];
|
||||
preg_match_all('/[^ ,]+/', $keywords_string, $keywords_array);
|
||||
|
||||
if(sizeof($keywords_array) > 0) {
|
||||
if (sizeof($keywords_array) > 0) {
|
||||
$keywords_array = $keywords_array[0];
|
||||
}
|
||||
|
||||
|
@ -673,43 +761,53 @@ class RecipeService {
|
|||
/**
|
||||
* @param string $path
|
||||
*/
|
||||
public function setUserFolderPath(string $path) {
|
||||
public function setUserFolderPath(string $path)
|
||||
{
|
||||
$this->config->setUserValue($this->userId, 'cookbook', 'folder', $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUserFolderPath() {
|
||||
public function getUserFolderPath()
|
||||
{
|
||||
$path = $this->config->getUserValue($this->userId, 'cookbook', 'folder');
|
||||
|
||||
if(!$path) { $path = '/Recipes'; }
|
||||
if (!$path) {
|
||||
$path = '/Recipes';
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $interval
|
||||
* @throws PreConditionNotMetException
|
||||
*/
|
||||
public function setSearchIndexUpdateInterval(int $interval) {
|
||||
public function setSearchIndexUpdateInterval(int $interval)
|
||||
{
|
||||
$this->config->setUserValue($this->userId, 'cookbook', 'update_interval', $interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSearchIndexUpdateInterval() {
|
||||
$interval = (int) $this->config->getUserValue($this->userId, 'cookbook', 'update_interval');
|
||||
public function getSearchIndexUpdateInterval(): int
|
||||
{
|
||||
$interval = (int)$this->config->getUserValue($this->userId, 'cookbook', 'update_interval');
|
||||
|
||||
if($interval < 1) { $interval = 5; }
|
||||
if ($interval < 1) {
|
||||
$interval = 5;
|
||||
}
|
||||
|
||||
return $interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \OCP\Files\Folder
|
||||
* @return Folder
|
||||
*/
|
||||
public function getFolderForUser() {
|
||||
public function getFolderForUser()
|
||||
{
|
||||
$path = '/' . $this->userId . '/files/' . $this->getUserFolderPath();
|
||||
$path = str_replace('//', '/', $path);
|
||||
|
||||
|
@ -720,9 +818,13 @@ class RecipeService {
|
|||
* Finds a folder and creates it if non-existent
|
||||
* @param string $path path to the folder
|
||||
*
|
||||
* @return \OCP\Files\Folder
|
||||
* @return Folder
|
||||
*
|
||||
* @throws NotFoundException
|
||||
* @throws NotPermittedException
|
||||
*/
|
||||
private function getOrCreateFolder($path) {
|
||||
private function getOrCreateFolder($path)
|
||||
{
|
||||
if ($this->root->nodeExists($path)) {
|
||||
$folder = $this->root->get($path);
|
||||
} else {
|
||||
|
@ -734,16 +836,21 @@ class RecipeService {
|
|||
/**
|
||||
* Get recipe file contents as an array
|
||||
*
|
||||
* @param \OCP\Files\File $file
|
||||
* @param File $file
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function parseRecipeFile($file) {
|
||||
if(!$file) { return null; }
|
||||
public function parseRecipeFile($file)
|
||||
{
|
||||
if (!$file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$json = json_decode($file->getContent(), true);
|
||||
|
||||
if(!$json) { return null; }
|
||||
if (!$json) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$json['id'] = $file->getParent()->getId();
|
||||
|
||||
|
@ -756,17 +863,22 @@ class RecipeService {
|
|||
* @param int $id
|
||||
* @param string $size
|
||||
*
|
||||
* @return \OCP\Files\File
|
||||
* @return File
|
||||
*/
|
||||
public function getRecipeImageFileByFolderId($id, $size = 'thumb') {
|
||||
if(!$size) { $size = 'thumb'; }
|
||||
if($size !== 'full' && $size !== 'thumb') {
|
||||
throw new \Exception('Image size "' . $size . '" not recognised');
|
||||
public function getRecipeImageFileByFolderId($id, $size = 'thumb')
|
||||
{
|
||||
if (!$size) {
|
||||
$size = 'thumb';
|
||||
}
|
||||
if ($size !== 'full' && $size !== 'thumb') {
|
||||
throw new Exception('Image size "' . $size . '" not recognised');
|
||||
}
|
||||
|
||||
$recipe_folder = $this->root->getById($id);
|
||||
|
||||
if(sizeof($recipe_folder) < 1) { throw new \Exception('Recipe ' . $id . ' not found'); }
|
||||
if (count($recipe_folder) < 1) {
|
||||
throw new Exception('Recipe ' . $id . ' not found');
|
||||
}
|
||||
|
||||
$recipe_folder = $recipe_folder[0];
|
||||
|
||||
|
@ -775,24 +887,29 @@ class RecipeService {
|
|||
|
||||
$image_file = $recipe_folder->get($image_filename);
|
||||
|
||||
if($image_file && $this->isImage($image_file)) { return $image_file; }
|
||||
if ($image_file && $this->isImage($image_file)) {
|
||||
return $image_file;
|
||||
}
|
||||
|
||||
throw new \Exception('Image file not recognised');
|
||||
throw new Exception('Image file not recognised');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if file is an image
|
||||
*
|
||||
* @param \OCP\Files\File $file
|
||||
* @param File $file
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isImage($file) {
|
||||
private function isImage($file)
|
||||
{
|
||||
$allowedExtensions = ['jpg', 'jpeg', 'png'];
|
||||
if($file->getType() !== 'file') return false;
|
||||
if ($file->getType() !== 'file') {
|
||||
return false;
|
||||
}
|
||||
$ext = pathinfo($file->getName(), PATHINFO_EXTENSION);
|
||||
$iext = strtolower($ext);
|
||||
if(!in_array($iext, $allowedExtensions)) {
|
||||
if (!in_array($iext, $allowedExtensions)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -801,16 +918,19 @@ class RecipeService {
|
|||
/**
|
||||
* Test if file is a recipe
|
||||
*
|
||||
* @param \OCP\Files\File $file
|
||||
* @param File $file
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isRecipeFile($file) {
|
||||
private function isRecipeFile($file)
|
||||
{
|
||||
$allowedExtensions = ['json'];
|
||||
if($file->getType() !== 'file') return false;
|
||||
if ($file->getType() !== 'file') {
|
||||
return false;
|
||||
}
|
||||
$ext = pathinfo($file->getName(), PATHINFO_EXTENSION);
|
||||
$iext = strtolower($ext);
|
||||
if(!in_array($iext, $allowedExtensions)) {
|
||||
if (!in_array($iext, $allowedExtensions)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -821,12 +941,15 @@ class RecipeService {
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
private function cleanUpString($str, $preserve_newlines = false) {
|
||||
if(!$str) { return ''; }
|
||||
private function cleanUpString($str, $preserve_newlines = false)
|
||||
{
|
||||
if (!$str) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$str = strip_tags($str);
|
||||
|
||||
if(!$preserve_newlines) {
|
||||
if (!$preserve_newlines) {
|
||||
$str = str_replace(["\r", "\n"], '', $str);
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче