mirror of
https://github.com/grocy/grocy.git
synced 2025-08-20 12:20:22 +00:00
Applied PHP formatting rules
This commit is contained in:
@@ -5,8 +5,70 @@ namespace Grocy\Services;
|
||||
class ApiKeyService extends BaseService
|
||||
{
|
||||
const API_KEY_TYPE_DEFAULT = 'default';
|
||||
|
||||
const API_KEY_TYPE_SPECIAL_PURPOSE_CALENDAR_ICAL = 'special-purpose-calendar-ical';
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function CreateApiKey($keyType = self::API_KEY_TYPE_DEFAULT)
|
||||
{
|
||||
$newApiKey = $this->GenerateApiKey();
|
||||
|
||||
$apiKeyRow = $this->getDatabase()->api_keys()->createRow([
|
||||
'api_key' => $newApiKey,
|
||||
'user_id' => GROCY_USER_ID,
|
||||
'expires' => '2999-12-31 23:59:59', // Default is that API keys expire never
|
||||
'key_type' => $keyType
|
||||
]);
|
||||
$apiKeyRow->save();
|
||||
|
||||
return $newApiKey;
|
||||
}
|
||||
|
||||
public function GetApiKeyId($apiKey)
|
||||
{
|
||||
$apiKey = $this->getDatabase()->api_keys()->where('api_key', $apiKey)->fetch();
|
||||
return $apiKey->id;
|
||||
}
|
||||
|
||||
// Returns any valid key for $keyType,
|
||||
// not allowed for key type "default"
|
||||
public function GetOrCreateApiKey($keyType)
|
||||
{
|
||||
if ($keyType === self::API_KEY_TYPE_DEFAULT)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
$apiKeyRow = $this->getDatabase()->api_keys()->where('key_type = :1 AND expires > :2', $keyType, date('Y-m-d H:i:s', time()))->fetch();
|
||||
|
||||
if ($apiKeyRow !== null)
|
||||
{
|
||||
return $apiKeyRow->api_key;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->CreateApiKey($keyType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function GetUserByApiKey($apiKey)
|
||||
{
|
||||
$apiKeyRow = $this->getDatabase()->api_keys()->where('api_key', $apiKey)->fetch();
|
||||
|
||||
if ($apiKeyRow !== null)
|
||||
{
|
||||
return $this->getDatabase()->users($apiKeyRow->user_id);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
@@ -19,14 +81,15 @@ class ApiKeyService extends BaseService
|
||||
else
|
||||
{
|
||||
$apiKeyRow = $this->getDatabase()->api_keys()->where('api_key = :1 AND expires > :2 AND key_type = :3', $apiKey, date('Y-m-d H:i:s', time()), $keyType)->fetch();
|
||||
|
||||
if ($apiKeyRow !== null)
|
||||
{
|
||||
// This should not change the database file modification time as this is used
|
||||
// This should not change the database file modification time as this is used
|
||||
// to determine if REALLY something has changed
|
||||
$dbModTime = $this->getDatabaseService()->GetDbChangedTime();
|
||||
$apiKeyRow->update(array(
|
||||
$apiKeyRow->update([
|
||||
'last_used' => date('Y-m-d H:i:s', time())
|
||||
));
|
||||
]);
|
||||
$this->getDatabaseService()->SetDbChangedTime($dbModTime);
|
||||
|
||||
return true;
|
||||
@@ -35,25 +98,9 @@ class ApiKeyService extends BaseService
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function CreateApiKey($keyType = self::API_KEY_TYPE_DEFAULT)
|
||||
{
|
||||
$newApiKey = $this->GenerateApiKey();
|
||||
|
||||
$apiKeyRow = $this->getDatabase()->api_keys()->createRow(array(
|
||||
'api_key' => $newApiKey,
|
||||
'user_id' => GROCY_USER_ID,
|
||||
'expires' => '2999-12-31 23:59:59', // Default is that API keys expire never
|
||||
'key_type' => $keyType
|
||||
));
|
||||
$apiKeyRow->save();
|
||||
|
||||
return $newApiKey;
|
||||
}
|
||||
|
||||
public function RemoveApiKey($apiKey)
|
||||
@@ -61,46 +108,9 @@ class ApiKeyService extends BaseService
|
||||
$this->getDatabase()->api_keys()->where('api_key', $apiKey)->delete();
|
||||
}
|
||||
|
||||
public function GetApiKeyId($apiKey)
|
||||
{
|
||||
$apiKey = $this->getDatabase()->api_keys()->where('api_key', $apiKey)->fetch();
|
||||
return $apiKey->id;
|
||||
}
|
||||
|
||||
public function GetUserByApiKey($apiKey)
|
||||
{
|
||||
$apiKeyRow = $this->getDatabase()->api_keys()->where('api_key', $apiKey)->fetch();
|
||||
if ($apiKeyRow !== null)
|
||||
{
|
||||
return $this->getDatabase()->users($apiKeyRow->user_id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Returns any valid key for $keyType,
|
||||
// not allowed for key type "default"
|
||||
public function GetOrCreateApiKey($keyType)
|
||||
{
|
||||
if ($keyType === self::API_KEY_TYPE_DEFAULT)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
$apiKeyRow = $this->getDatabase()->api_keys()->where('key_type = :1 AND expires > :2', $keyType, date('Y-m-d H:i:s', time()))->fetch();
|
||||
if ($apiKeyRow !== null)
|
||||
{
|
||||
return $apiKeyRow->api_key;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->CreateApiKey($keyType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function GenerateApiKey()
|
||||
{
|
||||
return RandomString(50);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -6,6 +6,45 @@ class ApplicationService extends BaseService
|
||||
{
|
||||
private $InstalledVersion;
|
||||
|
||||
public function GetChangelog()
|
||||
{
|
||||
$changelogItems = [];
|
||||
|
||||
foreach (glob(__DIR__ . '/../changelog/*.md') as $file)
|
||||
{
|
||||
$fileName = basename($file);
|
||||
$fileNameParts = explode('_', $fileName);
|
||||
|
||||
$fileContent = file_get_contents($file);
|
||||
$version = $fileNameParts[1];
|
||||
$releaseDate = explode('.', $fileNameParts[2])[0];
|
||||
$releaseNumber = intval($fileNameParts[0]);
|
||||
|
||||
$changelogItems[] = [
|
||||
'version' => $version,
|
||||
'release_date' => $releaseDate,
|
||||
'body' => $fileContent,
|
||||
'release_number' => $releaseNumber
|
||||
];
|
||||
}
|
||||
|
||||
// Sort changelog items to have the changelog descending by newest version
|
||||
usort($changelogItems, function ($a, $b)
|
||||
{
|
||||
if ($a['release_number'] == $b['release_number'])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ($a['release_number'] < $b['release_number']) ? 1 : -1;
|
||||
});
|
||||
|
||||
return [
|
||||
'changelog_items' => $changelogItems,
|
||||
'newest_release_number' => $changelogItems[0]['release_number']
|
||||
];
|
||||
}
|
||||
|
||||
public function GetInstalledVersion()
|
||||
{
|
||||
if ($this->InstalledVersion == null)
|
||||
@@ -20,6 +59,7 @@ class ApplicationService extends BaseService
|
||||
$this->InstalledVersion->Version = "pre-release-$commitHash";
|
||||
$this->InstalledVersion->ReleaseDate = substr($commitDate, 0, 19);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $this->InstalledVersion;
|
||||
@@ -31,48 +71,11 @@ class ApplicationService extends BaseService
|
||||
$sqliteVersion = $pdo->query('SELECT sqlite_version()')->fetch()[0];
|
||||
$pdo = null;
|
||||
|
||||
return array(
|
||||
return [
|
||||
'grocy_version' => $this->GetInstalledVersion(),
|
||||
'php_version' => phpversion(),
|
||||
'sqlite_version' => $sqliteVersion
|
||||
);
|
||||
'sqlite_version' => $sqliteVersion
|
||||
];
|
||||
}
|
||||
|
||||
public function GetChangelog()
|
||||
{
|
||||
$changelogItems = array();
|
||||
foreach(glob(__DIR__ . '/../changelog/*.md') as $file)
|
||||
{
|
||||
$fileName = basename($file);
|
||||
$fileNameParts = explode('_', $fileName);
|
||||
|
||||
$fileContent = file_get_contents($file);
|
||||
$version = $fileNameParts[1];
|
||||
$releaseDate = explode('.', $fileNameParts[2])[0];
|
||||
$releaseNumber = intval($fileNameParts[0]);
|
||||
|
||||
$changelogItems[] = array(
|
||||
'version' => $version,
|
||||
'release_date' => $releaseDate,
|
||||
'body' => $fileContent,
|
||||
'release_number' => $releaseNumber
|
||||
);
|
||||
}
|
||||
|
||||
// Sort changelog items to have the changelog descending by newest version
|
||||
usort($changelogItems, function($a, $b)
|
||||
{
|
||||
if ($a['release_number'] == $b['release_number'])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ($a['release_number'] < $b['release_number']) ? 1 : -1;
|
||||
});
|
||||
|
||||
return array(
|
||||
'changelog_items' => $changelogItems,
|
||||
'newest_release_number' => $changelogItems[0]['release_number']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -2,20 +2,19 @@
|
||||
|
||||
namespace Grocy\Services;
|
||||
|
||||
#use \Grocy\Services\DatabaseService;
|
||||
#use \Grocy\Services\LocalizationService;
|
||||
|
||||
class BaseService
|
||||
{
|
||||
public function __construct() {
|
||||
}
|
||||
private static $instances = [];
|
||||
|
||||
private static $instances = array();
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public static function getInstance()
|
||||
{
|
||||
$className = get_called_class();
|
||||
if(!isset(self::$instances[$className]))
|
||||
|
||||
if (!isset(self::$instances[$className]))
|
||||
{
|
||||
self::$instances[$className] = new $className();
|
||||
}
|
||||
@@ -23,9 +22,14 @@ class BaseService
|
||||
return self::$instances[$className];
|
||||
}
|
||||
|
||||
protected function getDatabaseService()
|
||||
protected function getBatteriesService()
|
||||
{
|
||||
return DatabaseService::getInstance();
|
||||
return BatteriesService::getInstance();
|
||||
}
|
||||
|
||||
protected function getChoresService()
|
||||
{
|
||||
return ChoresService::getInstance();
|
||||
}
|
||||
|
||||
protected function getDatabase()
|
||||
@@ -33,7 +37,12 @@ class BaseService
|
||||
return $this->getDatabaseService()->GetDbConnection();
|
||||
}
|
||||
|
||||
protected function getLocalizationService()
|
||||
protected function getDatabaseService()
|
||||
{
|
||||
return DatabaseService::getInstance();
|
||||
}
|
||||
|
||||
protected function getLocalizationService()
|
||||
{
|
||||
return LocalizationService::getInstance(GROCY_LOCALE);
|
||||
}
|
||||
@@ -48,18 +57,9 @@ class BaseService
|
||||
return TasksService::getInstance();
|
||||
}
|
||||
|
||||
protected function getChoresService()
|
||||
{
|
||||
return ChoresService::getInstance();
|
||||
}
|
||||
|
||||
protected function getBatteriesService()
|
||||
{
|
||||
return BatteriesService::getInstance();
|
||||
}
|
||||
|
||||
protected function getUsersService()
|
||||
{
|
||||
return UsersService::getInstance();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -4,12 +4,6 @@ namespace Grocy\Services;
|
||||
|
||||
class BatteriesService extends BaseService
|
||||
{
|
||||
public function GetCurrent()
|
||||
{
|
||||
$sql = 'SELECT * from batteries_current';
|
||||
return $this->getDatabaseService()->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
public function GetBatteryDetails(int $batteryId)
|
||||
{
|
||||
if (!$this->BatteryExists($batteryId))
|
||||
@@ -22,12 +16,18 @@ class BatteriesService extends BaseService
|
||||
$batteryLastChargedTime = $this->getDatabase()->battery_charge_cycles()->where('battery_id = :1 AND undone = 0', $batteryId)->max('tracked_time');
|
||||
$nextChargeTime = $this->getDatabase()->batteries_current()->where('battery_id', $batteryId)->min('next_estimated_charge_time');
|
||||
|
||||
return array(
|
||||
return [
|
||||
'battery' => $battery,
|
||||
'last_charged' => $batteryLastChargedTime,
|
||||
'charge_cycles_count' => $batteryChargeCyclesCount,
|
||||
'next_estimated_charge_time' => $nextChargeTime
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
public function GetCurrent()
|
||||
{
|
||||
$sql = 'SELECT * from batteries_current';
|
||||
return $this->getDatabaseService()->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
public function TrackChargeCycle(int $batteryId, string $trackedTime)
|
||||
@@ -37,33 +37,35 @@ class BatteriesService extends BaseService
|
||||
throw new \Exception('Battery does not exist');
|
||||
}
|
||||
|
||||
$logRow = $this->getDatabase()->battery_charge_cycles()->createRow(array(
|
||||
$logRow = $this->getDatabase()->battery_charge_cycles()->createRow([
|
||||
'battery_id' => $batteryId,
|
||||
'tracked_time' => $trackedTime
|
||||
));
|
||||
]);
|
||||
$logRow->save();
|
||||
|
||||
return $this->getDatabase()->lastInsertId();
|
||||
}
|
||||
|
||||
public function UndoChargeCycle($chargeCycleId)
|
||||
{
|
||||
$logRow = $this->getDatabase()->battery_charge_cycles()->where('id = :1 AND undone = 0', $chargeCycleId)->fetch();
|
||||
|
||||
if ($logRow == null)
|
||||
{
|
||||
throw new \Exception('Charge cycle does not exist or was already undone');
|
||||
}
|
||||
|
||||
// Update log entry
|
||||
$logRow->update([
|
||||
'undone' => 1,
|
||||
'undone_timestamp' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
}
|
||||
|
||||
private function BatteryExists($batteryId)
|
||||
{
|
||||
$batteryRow = $this->getDatabase()->batteries()->where('id = :1', $batteryId)->fetch();
|
||||
return $batteryRow !== null;
|
||||
}
|
||||
|
||||
public function UndoChargeCycle($chargeCycleId)
|
||||
{
|
||||
$logRow = $this->getDatabase()->battery_charge_cycles()->where('id = :1 AND undone = 0', $chargeCycleId)->fetch();
|
||||
if ($logRow == null)
|
||||
{
|
||||
throw new \Exception('Charge cycle does not exist or was already undone');
|
||||
}
|
||||
|
||||
// Update log entry
|
||||
$logRow->update(array(
|
||||
'undone' => 1,
|
||||
'undone_timestamp' => date('Y-m-d H:i:s')
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@@ -3,147 +3,171 @@
|
||||
namespace Grocy\Services;
|
||||
|
||||
#use \Grocy\Services\StockService;
|
||||
|
||||
#use \Grocy\Services\TasksService;
|
||||
|
||||
#use \Grocy\Services\ChoresService;
|
||||
|
||||
#use \Grocy\Services\BatteriesService;
|
||||
#use \Grocy\Services\UsersService;
|
||||
use \Grocy\Helpers\UrlManager;
|
||||
use Grocy\Helpers\UrlManager;
|
||||
|
||||
class CalendarService extends BaseService
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->UrlManager = new UrlManager(GROCY_BASE_URL);
|
||||
}
|
||||
|
||||
public function GetEvents()
|
||||
{
|
||||
$stockEvents = array();
|
||||
$stockEvents = [];
|
||||
|
||||
if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING)
|
||||
{
|
||||
$products = $this->getDatabase()->products();
|
||||
$titlePrefix = $this->getLocalizationService()->__t('Product expires') . ': ';
|
||||
foreach($this->getStockService()->GetCurrentStock() as $currentStockEntry)
|
||||
|
||||
foreach ($this->getStockService()->GetCurrentStock() as $currentStockEntry)
|
||||
{
|
||||
if ($currentStockEntry->amount > 0)
|
||||
{
|
||||
$stockEvents[] = array(
|
||||
$stockEvents[] = [
|
||||
'title' => $titlePrefix . FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name,
|
||||
'start' => $currentStockEntry->best_before_date,
|
||||
'date_format' => 'date',
|
||||
'link' => $this->UrlManager->ConstructUrl('/stockoverview')
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$taskEvents = array();
|
||||
$taskEvents = [];
|
||||
|
||||
if (GROCY_FEATURE_FLAG_TASKS)
|
||||
{
|
||||
$titlePrefix = $this->getLocalizationService()->__t('Task due') . ': ';
|
||||
foreach($this->getTasksService()->GetCurrent() as $currentTaskEntry)
|
||||
|
||||
foreach ($this->getTasksService()->GetCurrent() as $currentTaskEntry)
|
||||
{
|
||||
$taskEvents[] = array(
|
||||
$taskEvents[] = [
|
||||
'title' => $titlePrefix . $currentTaskEntry->name,
|
||||
'start' => $currentTaskEntry->due_date,
|
||||
'date_format' => 'date',
|
||||
'link' => $this->UrlManager->ConstructUrl('/tasks')
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$choreEvents = array();
|
||||
$choreEvents = [];
|
||||
|
||||
if (GROCY_FEATURE_FLAG_CHORES)
|
||||
{
|
||||
$users = $this->getUsersService()->GetUsersAsDto();
|
||||
|
||||
$chores = $this->getDatabase()->chores();
|
||||
$titlePrefix = $this->getLocalizationService()->__t('Chore due') . ': ';
|
||||
foreach($this->getChoresService()->GetCurrent() as $currentChoreEntry)
|
||||
|
||||
foreach ($this->getChoresService()->GetCurrent() as $currentChoreEntry)
|
||||
{
|
||||
$chore = FindObjectInArrayByPropertyValue($chores, 'id', $currentChoreEntry->chore_id);
|
||||
|
||||
$assignedToText = '';
|
||||
|
||||
if (!empty($currentChoreEntry->next_execution_assigned_to_user_id))
|
||||
{
|
||||
$assignedToText = ' (' . $this->getLocalizationService()->__t('assigned to %s', FindObjectInArrayByPropertyValue($users, 'id', $currentChoreEntry->next_execution_assigned_to_user_id)->display_name) . ')';
|
||||
}
|
||||
|
||||
$choreEvents[] = array(
|
||||
$choreEvents[] = [
|
||||
'title' => $titlePrefix . $chore->name . $assignedToText,
|
||||
'start' => $currentChoreEntry->next_estimated_execution_time,
|
||||
'date_format' => 'datetime',
|
||||
'link' => $this->UrlManager->ConstructUrl('/choresoverview'),
|
||||
'allDay' => $chore->track_date_only == 1
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$batteryEvents = array();
|
||||
$batteryEvents = [];
|
||||
|
||||
if (GROCY_FEATURE_FLAG_BATTERIES)
|
||||
{
|
||||
$batteries = $this->getDatabase()->batteries();
|
||||
$titlePrefix = $this->getLocalizationService()->__t('Battery charge cycle due') . ': ';
|
||||
foreach($this->getBatteriesService()->GetCurrent() as $currentBatteryEntry)
|
||||
|
||||
foreach ($this->getBatteriesService()->GetCurrent() as $currentBatteryEntry)
|
||||
{
|
||||
$batteryEvents[] = array(
|
||||
$batteryEvents[] = [
|
||||
'title' => $titlePrefix . FindObjectInArrayByPropertyValue($batteries, 'id', $currentBatteryEntry->battery_id)->name,
|
||||
'start' => $currentBatteryEntry->next_estimated_charge_time,
|
||||
'date_format' => 'datetime',
|
||||
'link' => $this->UrlManager->ConstructUrl('/batteriesoverview')
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$mealPlanRecipeEvents = array();
|
||||
$mealPlanRecipeEvents = [];
|
||||
|
||||
if (GROCY_FEATURE_FLAG_RECIPES)
|
||||
{
|
||||
$recipes = $this->getDatabase()->recipes();
|
||||
$mealPlanDayRecipes = $this->getDatabase()->recipes()->where('type', 'mealplan-day');
|
||||
$titlePrefix = $this->getLocalizationService()->__t('Meal plan recipe') . ': ';
|
||||
|
||||
foreach($mealPlanDayRecipes as $mealPlanDayRecipe)
|
||||
foreach ($mealPlanDayRecipes as $mealPlanDayRecipe)
|
||||
{
|
||||
$recipesOfCurrentDay = $this->getDatabase()->recipes_nestings_resolved()->where('recipe_id = :1 AND includes_recipe_id != :1', $mealPlanDayRecipe->id);
|
||||
|
||||
foreach ($recipesOfCurrentDay as $recipeOfCurrentDay)
|
||||
{
|
||||
$mealPlanRecipeEvents[] = array(
|
||||
$mealPlanRecipeEvents[] = [
|
||||
'title' => $titlePrefix . FindObjectInArrayByPropertyValue($recipes, 'id', $recipeOfCurrentDay->includes_recipe_id)->name,
|
||||
'start' => FindObjectInArrayByPropertyValue($recipes, 'id', $recipeOfCurrentDay->recipe_id)->name,
|
||||
'date_format' => 'date',
|
||||
'description' => $this->UrlManager->ConstructUrl('/mealplan' . '?week=' . FindObjectInArrayByPropertyValue($recipes, 'id', $recipeOfCurrentDay->recipe_id)->name),
|
||||
'link' => $this->UrlManager->ConstructUrl('/recipes' . '?recipe=' . $recipeOfCurrentDay->includes_recipe_id . "#fullscreen")
|
||||
);
|
||||
'link' => $this->UrlManager->ConstructUrl('/recipes' . '?recipe=' . $recipeOfCurrentDay->includes_recipe_id . '#fullscreen')
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$mealPlanDayNotes = $this->getDatabase()->meal_plan()->where('type', 'note');
|
||||
$titlePrefix = $this->getLocalizationService()->__t('Meal plan note') . ': ';
|
||||
$mealPlanNotesEvents = array();
|
||||
foreach($mealPlanDayNotes as $mealPlanDayNote)
|
||||
$mealPlanNotesEvents = [];
|
||||
|
||||
foreach ($mealPlanDayNotes as $mealPlanDayNote)
|
||||
{
|
||||
$mealPlanNotesEvents[] = array(
|
||||
$mealPlanNotesEvents[] = [
|
||||
'title' => $titlePrefix . $mealPlanDayNote->note,
|
||||
'start' => $mealPlanDayNote->day,
|
||||
'date_format' => 'date'
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
$products = $this->getDatabase()->products();
|
||||
$mealPlanDayProducts = $this->getDatabase()->meal_plan()->where('type', 'product');
|
||||
$titlePrefix = $this->getLocalizationService()->__t('Meal plan product') . ': ';
|
||||
$mealPlanProductEvents = array();
|
||||
foreach($mealPlanDayProducts as $mealPlanDayProduct)
|
||||
$mealPlanProductEvents = [];
|
||||
|
||||
foreach ($mealPlanDayProducts as $mealPlanDayProduct)
|
||||
{
|
||||
$mealPlanProductEvents[] = array(
|
||||
$mealPlanProductEvents[] = [
|
||||
'title' => $titlePrefix . FindObjectInArrayByPropertyValue($products, 'id', $mealPlanDayProduct->product_id)->name,
|
||||
'start' => $mealPlanDayProduct->day,
|
||||
'date_format' => 'date'
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return array_merge($stockEvents, $taskEvents, $choreEvents, $batteryEvents, $mealPlanRecipeEvents, $mealPlanNotesEvents, $mealPlanProductEvents);
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->UrlManager = new UrlManager(GROCY_BASE_URL);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -2,31 +2,115 @@
|
||||
|
||||
namespace Grocy\Services;
|
||||
|
||||
#use \Grocy\Services\StockService;
|
||||
|
||||
class ChoresService extends BaseService
|
||||
{
|
||||
const CHORE_PERIOD_TYPE_MANUALLY = 'manually';
|
||||
const CHORE_PERIOD_TYPE_DYNAMIC_REGULAR = 'dynamic-regular';
|
||||
const CHORE_PERIOD_TYPE_DAILY = 'daily';
|
||||
const CHORE_PERIOD_TYPE_WEEKLY = 'weekly';
|
||||
const CHORE_PERIOD_TYPE_MONTHLY = 'monthly';
|
||||
const CHORE_PERIOD_TYPE_YEARLY = 'yearly';
|
||||
|
||||
const CHORE_ASSIGNMENT_TYPE_NO_ASSIGNMENT = 'no-assignment';
|
||||
const CHORE_ASSIGNMENT_TYPE_WHO_LEAST_DID_FIRST = 'who-least-did-first';
|
||||
const CHORE_ASSIGNMENT_TYPE_RANDOM = 'random';
|
||||
const CHORE_ASSIGNMENT_TYPE_IN_ALPHABETICAL_ORDER = 'in-alphabetical-order';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
const CHORE_ASSIGNMENT_TYPE_NO_ASSIGNMENT = 'no-assignment';
|
||||
|
||||
public function GetCurrent()
|
||||
const CHORE_ASSIGNMENT_TYPE_RANDOM = 'random';
|
||||
|
||||
const CHORE_ASSIGNMENT_TYPE_WHO_LEAST_DID_FIRST = 'who-least-did-first';
|
||||
|
||||
const CHORE_PERIOD_TYPE_DAILY = 'daily';
|
||||
|
||||
const CHORE_PERIOD_TYPE_DYNAMIC_REGULAR = 'dynamic-regular';
|
||||
|
||||
const CHORE_PERIOD_TYPE_MANUALLY = 'manually';
|
||||
|
||||
const CHORE_PERIOD_TYPE_MONTHLY = 'monthly';
|
||||
|
||||
const CHORE_PERIOD_TYPE_WEEKLY = 'weekly';
|
||||
|
||||
const CHORE_PERIOD_TYPE_YEARLY = 'yearly';
|
||||
|
||||
public function CalculateNextExecutionAssignment($choreId)
|
||||
{
|
||||
$sql = 'SELECT chores_current.*, chores.name AS chore_name from chores_current join chores on chores_current.chore_id = chores.id';
|
||||
return $this->getDatabaseService()->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ);
|
||||
if (!$this->ChoreExists($choreId))
|
||||
{
|
||||
throw new \Exception('Chore does not exist');
|
||||
}
|
||||
|
||||
$chore = $this->getDatabase()->chores($choreId);
|
||||
$choreLastTrackedTime = $this->getDatabase()->chores_log()->where('chore_id = :1 AND undone = 0', $choreId)->max('tracked_time');
|
||||
$lastChoreLogRow = $this->getDatabase()->chores_log()->where('chore_id = :1 AND tracked_time = :2 AND undone = 0', $choreId, $choreLastTrackedTime)->orderBy('row_created_timestamp', 'DESC')->fetch();
|
||||
$lastDoneByUserId = $lastChoreLogRow->done_by_user_id;
|
||||
|
||||
$users = $this->getUsersService()->GetUsersAsDto();
|
||||
$assignedUsers = [];
|
||||
|
||||
foreach ($users as $user)
|
||||
{
|
||||
if (in_array($user->id, explode(',', $chore->assignment_config)))
|
||||
{
|
||||
$assignedUsers[] = $user;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$nextExecutionUserId = null;
|
||||
|
||||
if ($chore->assignment_type == self::CHORE_ASSIGNMENT_TYPE_RANDOM)
|
||||
{
|
||||
// Random assignment and only 1 user in the group? Well, ok - will be hard to guess the next one...
|
||||
if (count($assignedUsers) == 1)
|
||||
{
|
||||
$nextExecutionUserId = array_shift($assignedUsers)->id;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Randomness in small groups will likely often result in the same user, so try it as long as this is the case
|
||||
while ($nextExecutionUserId == null || $nextExecutionUserId == $lastDoneByUserId)
|
||||
{
|
||||
$nextExecutionUserId = $assignedUsers[array_rand($assignedUsers)]->id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else if ($chore->assignment_type == self::CHORE_ASSIGNMENT_TYPE_IN_ALPHABETICAL_ORDER)
|
||||
{
|
||||
usort($assignedUsers, function ($a, $b)
|
||||
{
|
||||
return strcmp($a->display_name, $b->display_name);
|
||||
});
|
||||
|
||||
$nextRoundMatches = false;
|
||||
foreach ($assignedUsers as $user)
|
||||
{
|
||||
if ($nextRoundMatches)
|
||||
{
|
||||
$nextExecutionUserId = $user->id;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($user->id == $lastDoneByUserId)
|
||||
{
|
||||
$nextRoundMatches = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If nothing has matched, probably it was the last user in the sorted list -> the first one is the next one
|
||||
if ($nextExecutionUserId == null)
|
||||
{
|
||||
$nextExecutionUserId = array_shift($assignedUsers)->id;
|
||||
}
|
||||
|
||||
}
|
||||
else if ($chore->assignment_type == self::CHORE_ASSIGNMENT_TYPE_WHO_LEAST_DID_FIRST)
|
||||
{
|
||||
$row = $this->getDatabase()->chores_execution_users_statistics()->where('chore_id = :1', $choreId)->orderBy('execution_count')->limit(1)->fetch();
|
||||
if ($row != null)
|
||||
{
|
||||
$nextExecutionUserId = $row->user_id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$chore->update([
|
||||
'next_execution_assigned_to_user_id' => $nextExecutionUserId
|
||||
]);
|
||||
}
|
||||
|
||||
public function GetChoreDetails(int $choreId)
|
||||
@@ -36,14 +120,14 @@ class ChoresService extends BaseService
|
||||
throw new \Exception('Chore does not exist');
|
||||
}
|
||||
|
||||
$users = $this->getUsersService()->GetUsersAsDto();
|
||||
$users = $this->getUsersService()->GetUsersAsDto();
|
||||
|
||||
$chore = $this->getDatabase()->chores($choreId);
|
||||
$choreTrackedCount = $this->getDatabase()->chores_log()->where('chore_id = :1 AND undone = 0', $choreId)->count();
|
||||
$choreLastTrackedTime = $this->getDatabase()->chores_log()->where('chore_id = :1 AND undone = 0', $choreId)->max('tracked_time');
|
||||
$nextExecutionTime = $this->getDatabase()->chores_current()->where('chore_id', $choreId)->min('next_estimated_execution_time');
|
||||
|
||||
$lastChoreLogRow = $this->getDatabase()->chores_log()->where('chore_id = :1 AND tracked_time = :2 AND undone = 0', $choreId, $choreLastTrackedTime)->fetch();
|
||||
$lastChoreLogRow = $this->getDatabase()->chores_log()->where('chore_id = :1 AND tracked_time = :2 AND undone = 0', $choreId, $choreLastTrackedTime)->fetch();
|
||||
$lastDoneByUser = null;
|
||||
if ($lastChoreLogRow !== null && !empty($lastChoreLogRow))
|
||||
{
|
||||
@@ -56,14 +140,20 @@ class ChoresService extends BaseService
|
||||
$nextExecutionAssignedUser = FindObjectInArrayByPropertyValue($users, 'id', $chore->next_execution_assigned_to_user_id);
|
||||
}
|
||||
|
||||
return array(
|
||||
return [
|
||||
'chore' => $chore,
|
||||
'last_tracked' => $choreLastTrackedTime,
|
||||
'tracked_count' => $choreTrackedCount,
|
||||
'last_done_by' => $lastDoneByUser,
|
||||
'next_estimated_execution_time' => $nextExecutionTime,
|
||||
'next_execution_assigned_user' => $nextExecutionAssignedUser
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
public function GetCurrent()
|
||||
{
|
||||
$sql = 'SELECT chores_current.*, chores.name AS chore_name from chores_current join chores on chores_current.chore_id = chores.id';
|
||||
return $this->getDatabaseService()->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
public function TrackChore(int $choreId, string $trackedTime, $doneBy = GROCY_USER_ID)
|
||||
@@ -85,11 +175,11 @@ class ChoresService extends BaseService
|
||||
$trackedTime = substr($trackedTime, 0, 10) . ' 00:00:00';
|
||||
}
|
||||
|
||||
$logRow = $this->getDatabase()->chores_log()->createRow(array(
|
||||
$logRow = $this->getDatabase()->chores_log()->createRow([
|
||||
'chore_id' => $choreId,
|
||||
'tracked_time' => $trackedTime,
|
||||
'done_by_user_id' => $doneBy
|
||||
));
|
||||
]);
|
||||
$logRow->save();
|
||||
$lastInsertId = $this->getDatabase()->lastInsertId();
|
||||
|
||||
@@ -103,12 +193,6 @@ class ChoresService extends BaseService
|
||||
return $lastInsertId;
|
||||
}
|
||||
|
||||
private function ChoreExists($choreId)
|
||||
{
|
||||
$choreRow = $this->getDatabase()->chores()->where('id = :1', $choreId)->fetch();
|
||||
return $choreRow !== null;
|
||||
}
|
||||
|
||||
public function UndoChoreExecution($executionId)
|
||||
{
|
||||
$logRow = $this->getDatabase()->chores_log()->where('id = :1 AND undone = 0', $executionId)->fetch();
|
||||
@@ -118,90 +202,21 @@ class ChoresService extends BaseService
|
||||
}
|
||||
|
||||
// Update log entry
|
||||
$logRow->update(array(
|
||||
$logRow->update([
|
||||
'undone' => 1,
|
||||
'undone_timestamp' => date('Y-m-d H:i:s')
|
||||
));
|
||||
]);
|
||||
}
|
||||
|
||||
public function CalculateNextExecutionAssignment($choreId)
|
||||
public function __construct()
|
||||
{
|
||||
if (!$this->ChoreExists($choreId))
|
||||
{
|
||||
throw new \Exception('Chore does not exist');
|
||||
}
|
||||
|
||||
$chore = $this->getDatabase()->chores($choreId);
|
||||
$choreLastTrackedTime = $this->getDatabase()->chores_log()->where('chore_id = :1 AND undone = 0', $choreId)->max('tracked_time');
|
||||
$lastChoreLogRow = $this->getDatabase()->chores_log()->where('chore_id = :1 AND tracked_time = :2 AND undone = 0', $choreId, $choreLastTrackedTime)->orderBy('row_created_timestamp', 'DESC')->fetch();
|
||||
$lastDoneByUserId = $lastChoreLogRow->done_by_user_id;
|
||||
|
||||
$users = $this->getUsersService()->GetUsersAsDto();
|
||||
$assignedUsers = array();
|
||||
foreach ($users as $user)
|
||||
{
|
||||
if (in_array($user->id, explode(',', $chore->assignment_config)))
|
||||
{
|
||||
$assignedUsers[] = $user;
|
||||
}
|
||||
}
|
||||
|
||||
$nextExecutionUserId = null;
|
||||
if ($chore->assignment_type == self::CHORE_ASSIGNMENT_TYPE_RANDOM)
|
||||
{
|
||||
// Random assignment and only 1 user in the group? Well, ok - will be hard to guess the next one...
|
||||
if (count($assignedUsers) == 1)
|
||||
{
|
||||
$nextExecutionUserId = array_shift($assignedUsers)->id;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Randomness in small groups will likely often result in the same user, so try it as long as this is the case
|
||||
while ($nextExecutionUserId == null || $nextExecutionUserId == $lastDoneByUserId)
|
||||
{
|
||||
$nextExecutionUserId = $assignedUsers[array_rand($assignedUsers)]->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ($chore->assignment_type == self::CHORE_ASSIGNMENT_TYPE_IN_ALPHABETICAL_ORDER)
|
||||
{
|
||||
usort($assignedUsers, function($a, $b)
|
||||
{
|
||||
return strcmp($a->display_name, $b->display_name);
|
||||
});
|
||||
|
||||
$nextRoundMatches = false;
|
||||
foreach ($assignedUsers as $user)
|
||||
{
|
||||
if ($nextRoundMatches)
|
||||
{
|
||||
$nextExecutionUserId = $user->id;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($user->id == $lastDoneByUserId)
|
||||
{
|
||||
$nextRoundMatches = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing has matched, probably it was the last user in the sorted list -> the first one is the next one
|
||||
if ($nextExecutionUserId == null)
|
||||
{
|
||||
$nextExecutionUserId = array_shift($assignedUsers)->id;
|
||||
}
|
||||
}
|
||||
else if ($chore->assignment_type == self::CHORE_ASSIGNMENT_TYPE_WHO_LEAST_DID_FIRST)
|
||||
{
|
||||
$row = $this->getDatabase()->chores_execution_users_statistics()->where('chore_id = :1', $choreId)->orderBy('execution_count')->limit(1)->fetch();
|
||||
if ($row != null)
|
||||
{
|
||||
$nextExecutionUserId = $row->user_id;
|
||||
}
|
||||
}
|
||||
|
||||
$chore->update(array(
|
||||
'next_execution_assigned_to_user_id' => $nextExecutionUserId
|
||||
));
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
private function ChoreExists($choreId)
|
||||
{
|
||||
$choreRow = $this->getDatabase()->chores()->where('id = :1', $choreId)->fetch();
|
||||
return $choreRow !== null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -6,47 +6,58 @@ class DatabaseMigrationService extends BaseService
|
||||
{
|
||||
public function MigrateDatabase()
|
||||
{
|
||||
$this->getDatabaseService()->ExecuteDbStatement("CREATE TABLE IF NOT EXISTS migrations (migration INTEGER NOT NULL PRIMARY KEY UNIQUE, execution_time_timestamp DATETIME DEFAULT (datetime('now', 'localtime')))");
|
||||
$this->getDatabaseService()->ExecuteDbStatement("CREATE TABLE IF NOT EXISTS migrations (migration INTEGER NOT NULL PRIMARY KEY UNIQUE, execution_time_timestamp DATETIME DEFAULT (datetime('now', 'localtime')))");
|
||||
|
||||
$migrationFiles = [];
|
||||
|
||||
$migrationFiles = array();
|
||||
foreach (new \FilesystemIterator(__DIR__ . '/../migrations') as $file)
|
||||
{
|
||||
$migrationFiles[$file->getBasename()] = $file;
|
||||
$migrationFiles[$file->getBasename()] = $file;
|
||||
}
|
||||
|
||||
ksort($migrationFiles);
|
||||
foreach($migrationFiles as $migrationKey => $migrationFile)
|
||||
{
|
||||
if($migrationFile->getExtension() === 'php')
|
||||
{
|
||||
$migrationNumber = ltrim($migrationFile->getBasename('.php'), '0');
|
||||
$this->ExecutePhpMigrationWhenNeeded($migrationNumber, $migrationFile->getPathname());
|
||||
}
|
||||
else if($migrationFile->getExtension() === 'sql')
|
||||
{
|
||||
$migrationNumber = ltrim($migrationFile->getBasename('.sql'), '0');
|
||||
$this->ExecuteSqlMigrationWhenNeeded($migrationNumber, file_get_contents($migrationFile->getPathname()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private function ExecuteSqlMigrationWhenNeeded(int $migrationId, string $sql)
|
||||
{
|
||||
$rowCount = $this->getDatabaseService()->ExecuteDbQuery('SELECT COUNT(*) FROM migrations WHERE migration = ' . $migrationId)->fetchColumn();
|
||||
if (intval($rowCount) === 0)
|
||||
foreach ($migrationFiles as $migrationKey => $migrationFile)
|
||||
{
|
||||
$this->getDatabaseService()->ExecuteDbStatement($sql);
|
||||
$this->getDatabaseService()->ExecuteDbStatement('INSERT INTO migrations (migration) VALUES (' . $migrationId . ')');
|
||||
if ($migrationFile->getExtension() === 'php')
|
||||
{
|
||||
$migrationNumber = ltrim($migrationFile->getBasename('.php'), '0');
|
||||
$this->ExecutePhpMigrationWhenNeeded($migrationNumber, $migrationFile->getPathname());
|
||||
}
|
||||
else
|
||||
|
||||
if ($migrationFile->getExtension() === 'sql')
|
||||
{
|
||||
$migrationNumber = ltrim($migrationFile->getBasename('.sql'), '0');
|
||||
$this->ExecuteSqlMigrationWhenNeeded($migrationNumber, file_get_contents($migrationFile->getPathname()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function ExecutePhpMigrationWhenNeeded(int $migrationId, string $phpFile)
|
||||
{
|
||||
$rowCount = $this->getDatabaseService()->ExecuteDbQuery('SELECT COUNT(*) FROM migrations WHERE migration = ' . $migrationId)->fetchColumn();
|
||||
|
||||
if (intval($rowCount) === 0)
|
||||
{
|
||||
include $phpFile;
|
||||
$this->getDatabaseService()->ExecuteDbStatement('INSERT INTO migrations (migration) VALUES (' . $migrationId . ')');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function ExecuteSqlMigrationWhenNeeded(int $migrationId, string $sql)
|
||||
{
|
||||
$rowCount = $this->getDatabaseService()->ExecuteDbQuery('SELECT COUNT(*) FROM migrations WHERE migration = ' . $migrationId)->fetchColumn();
|
||||
|
||||
if (intval($rowCount) === 0)
|
||||
{
|
||||
$this->getDatabaseService()->ExecuteDbStatement($sql);
|
||||
$this->getDatabaseService()->ExecuteDbStatement('INSERT INTO migrations (migration) VALUES (' . $migrationId . ')');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -6,8 +6,80 @@ namespace Grocy\Services;
|
||||
|
||||
class DatabaseService
|
||||
{
|
||||
private static $DbConnection = null;
|
||||
|
||||
private static $DbConnectionRaw = null;
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* @return boolean|\PDOStatement
|
||||
*/
|
||||
public function ExecuteDbQuery(string $sql)
|
||||
{
|
||||
$pdo = $this->GetDbConnectionRaw();
|
||||
|
||||
if ($this->ExecuteDbStatement($sql) === true)
|
||||
{
|
||||
return $pdo->query($sql);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function ExecuteDbStatement(string $sql)
|
||||
{
|
||||
$pdo = $this->GetDbConnectionRaw();
|
||||
|
||||
if ($pdo->exec($sql) === false)
|
||||
{
|
||||
throw new Exception($pdo->errorInfo());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetDbChangedTime()
|
||||
{
|
||||
return date('Y-m-d H:i:s', filemtime($this->GetDbFilePath()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \LessQL\Database
|
||||
*/
|
||||
public function GetDbConnection()
|
||||
{
|
||||
if (self::$DbConnection == null)
|
||||
{
|
||||
self::$DbConnection = new \LessQL\Database($this->GetDbConnectionRaw());
|
||||
}
|
||||
|
||||
return self::$DbConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \PDO
|
||||
*/
|
||||
public function GetDbConnectionRaw()
|
||||
{
|
||||
if (self::$DbConnectionRaw == null)
|
||||
{
|
||||
$pdo = new \PDO('sqlite:' . $this->GetDbFilePath());
|
||||
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
self::$DbConnectionRaw = $pdo;
|
||||
}
|
||||
|
||||
return self::$DbConnectionRaw;
|
||||
}
|
||||
|
||||
public function SetDbChangedTime($dateTime)
|
||||
{
|
||||
touch($this->GetDbFilePath(), strtotime($dateTime));
|
||||
}
|
||||
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance == null)
|
||||
@@ -28,71 +100,4 @@ class DatabaseService
|
||||
return GROCY_DATAPATH . '/grocy.db';
|
||||
}
|
||||
|
||||
private static $DbConnectionRaw = null;
|
||||
/**
|
||||
* @return \PDO
|
||||
*/
|
||||
public function GetDbConnectionRaw()
|
||||
{
|
||||
if (self::$DbConnectionRaw == null)
|
||||
{
|
||||
$pdo = new \PDO('sqlite:' . $this->GetDbFilePath());
|
||||
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
self::$DbConnectionRaw = $pdo;
|
||||
}
|
||||
|
||||
return self::$DbConnectionRaw;
|
||||
}
|
||||
|
||||
private static $DbConnection = null;
|
||||
/**
|
||||
* @return \LessQL\Database
|
||||
*/
|
||||
public function GetDbConnection()
|
||||
{
|
||||
if (self::$DbConnection == null)
|
||||
{
|
||||
self::$DbConnection = new \LessQL\Database($this->GetDbConnectionRaw());
|
||||
}
|
||||
|
||||
return self::$DbConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function ExecuteDbStatement(string $sql)
|
||||
{
|
||||
$pdo = $this->GetDbConnectionRaw();
|
||||
if ($pdo->exec($sql) === false)
|
||||
{
|
||||
throw new Exception($pdo->errorInfo());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean|\PDOStatement
|
||||
*/
|
||||
public function ExecuteDbQuery(string $sql)
|
||||
{
|
||||
$pdo = $this->GetDbConnectionRaw();
|
||||
if ($this->ExecuteDbStatement($sql) === true)
|
||||
{
|
||||
return $pdo->query($sql);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetDbChangedTime()
|
||||
{
|
||||
return date('Y-m-d H:i:s', filemtime($this->GetDbFilePath()));
|
||||
}
|
||||
|
||||
public function SetDbChangedTime($dateTime)
|
||||
{
|
||||
touch($this->GetDbFilePath(), strtotime($dateTime));
|
||||
}
|
||||
}
|
||||
|
@@ -6,17 +6,14 @@ namespace Grocy\Services;
|
||||
|
||||
class DemoDataGeneratorService extends BaseService
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->LocalizationService = new LocalizationService(GROCY_DEFAULT_LOCALE);
|
||||
}
|
||||
|
||||
protected $LocalizationService;
|
||||
|
||||
private $LastSupermarketId = 1;
|
||||
|
||||
public function PopulateDemoData()
|
||||
{
|
||||
$rowCount = $this->getDatabaseService()->ExecuteDbQuery('SELECT COUNT(*) FROM migrations WHERE migration = -1')->fetchColumn();
|
||||
|
||||
if (intval($rowCount) === 0)
|
||||
{
|
||||
$loremIpsum = 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.';
|
||||
@@ -314,14 +311,29 @@ class DemoDataGeneratorService extends BaseService
|
||||
$this->DownloadFileIfNotAlreadyExists('https://releases.grocy.info/demoresources/chocolate_sauce.jpg', "$recipePicturesFolder/chocolate_sauce.jpg");
|
||||
$this->DownloadFileIfNotAlreadyExists('https://releases.grocy.info/demoresources/pancakes_chocolate_sauce.jpg', "$recipePicturesFolder/pancakes_chocolate_sauce.jpg");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function RandomPrice()
|
||||
public function __construct()
|
||||
{
|
||||
return mt_rand(2 * 100, 25 * 100) / 100;
|
||||
parent::__construct();
|
||||
$this->LocalizationService = new LocalizationService(GROCY_DEFAULT_LOCALE);
|
||||
}
|
||||
|
||||
private function DownloadFileIfNotAlreadyExists($sourceUrl, $destinationPath)
|
||||
{
|
||||
if (!file_exists($destinationPath))
|
||||
{
|
||||
file_put_contents($destinationPath, file_get_contents($sourceUrl, false, stream_context_create([
|
||||
'ssl' => [
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false
|
||||
]
|
||||
])));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private $LastSupermarketId = 1;
|
||||
private function NextSupermarketId()
|
||||
{
|
||||
$returnValue = $this->LastSupermarketId;
|
||||
@@ -338,10 +350,9 @@ class DemoDataGeneratorService extends BaseService
|
||||
return $returnValue;
|
||||
}
|
||||
|
||||
private function __t_sql(string $text)
|
||||
private function RandomPrice()
|
||||
{
|
||||
$localizedText = $this->getLocalizationService()->__t($text, null);
|
||||
return str_replace("'", "''", $localizedText);
|
||||
return mt_rand(2 * 100, 25 * 100) / 100;
|
||||
}
|
||||
|
||||
private function __n_sql($number, string $singularForm, string $pluralForm)
|
||||
@@ -350,16 +361,10 @@ class DemoDataGeneratorService extends BaseService
|
||||
return str_replace("'", "''", $localizedText);
|
||||
}
|
||||
|
||||
private function DownloadFileIfNotAlreadyExists($sourceUrl, $destinationPath)
|
||||
private function __t_sql(string $text)
|
||||
{
|
||||
if (!file_exists($destinationPath))
|
||||
{
|
||||
file_put_contents($destinationPath, file_get_contents($sourceUrl, false, stream_context_create(array(
|
||||
'ssl' => array(
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false,
|
||||
),
|
||||
))));
|
||||
}
|
||||
$localizedText = $this->getLocalizationService()->__t($text, null);
|
||||
return str_replace("'", "''", $localizedText);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -2,37 +2,14 @@
|
||||
|
||||
namespace Grocy\Services;
|
||||
|
||||
use \Gumlet\ImageResize;
|
||||
use Gumlet\ImageResize;
|
||||
|
||||
class FilesService extends BaseService
|
||||
{
|
||||
const FILE_SERVE_TYPE_PICTURE = 'picture';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->StoragePath = GROCY_DATAPATH . '/storage';
|
||||
|
||||
if (!file_exists($this->StoragePath))
|
||||
{
|
||||
mkdir($this->StoragePath);
|
||||
}
|
||||
}
|
||||
|
||||
private $StoragePath;
|
||||
|
||||
public function GetFilePath($group, $fileName)
|
||||
{
|
||||
$groupFolderPath = $this->StoragePath . '/' . $group;
|
||||
if (!file_exists($groupFolderPath))
|
||||
{
|
||||
mkdir($groupFolderPath);
|
||||
}
|
||||
|
||||
return $groupFolderPath . '/' . $fileName;
|
||||
}
|
||||
|
||||
public function DownscaleImage($group, $fileName, $bestFitHeight = null, $bestFitWidth = null)
|
||||
{
|
||||
$filePath = $this->GetFilePath($group, $fileName);
|
||||
@@ -52,20 +29,27 @@ class FilesService extends BaseService
|
||||
if (!file_exists($filePathDownscaled))
|
||||
{
|
||||
$image = new ImageResize($filePath);
|
||||
|
||||
if ($bestFitHeight !== null && $bestFitHeight !== null)
|
||||
{
|
||||
$image->resizeToBestFit($bestFitWidth, $bestFitHeight);
|
||||
}
|
||||
else if ($bestFitHeight !== null)
|
||||
else
|
||||
|
||||
if ($bestFitHeight !== null)
|
||||
{
|
||||
$image->resizeToHeight($bestFitHeight);
|
||||
}
|
||||
else if ($bestFitWidth !== null)
|
||||
else
|
||||
|
||||
if ($bestFitWidth !== null)
|
||||
{
|
||||
$image->resizeToWidth($bestFitWidth);
|
||||
}
|
||||
|
||||
$image->save($filePathDownscaled);
|
||||
}
|
||||
|
||||
}
|
||||
catch (ImageResizeException $ex)
|
||||
{
|
||||
@@ -74,4 +58,30 @@ class FilesService extends BaseService
|
||||
|
||||
return $filePathDownscaled;
|
||||
}
|
||||
|
||||
public function GetFilePath($group, $fileName)
|
||||
{
|
||||
$groupFolderPath = $this->StoragePath . '/' . $group;
|
||||
|
||||
if (!file_exists($groupFolderPath))
|
||||
{
|
||||
mkdir($groupFolderPath);
|
||||
}
|
||||
|
||||
return $groupFolderPath . '/' . $fileName;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->StoragePath = GROCY_DATAPATH . '/storage';
|
||||
|
||||
if (!file_exists($this->StoragePath))
|
||||
{
|
||||
mkdir($this->StoragePath);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -3,14 +3,69 @@
|
||||
namespace Grocy\Services;
|
||||
|
||||
#use \Grocy\Services\DatabaseService;
|
||||
use \Gettext\Translation;
|
||||
use \Gettext\Translations;
|
||||
use \Gettext\Translator;
|
||||
use Gettext\Translation;
|
||||
use Gettext\Translations;
|
||||
use Gettext\Translator;
|
||||
|
||||
class LocalizationService
|
||||
{
|
||||
protected $Po;
|
||||
|
||||
private static $instanceMap = array();
|
||||
protected $PoUserStrings;
|
||||
|
||||
protected $Pot;
|
||||
|
||||
protected $PotMain;
|
||||
|
||||
protected $Translator;
|
||||
|
||||
private static $instanceMap = [];
|
||||
|
||||
public function CheckAndAddMissingTranslationToPot($text)
|
||||
{
|
||||
if (GROCY_MODE === 'dev')
|
||||
{
|
||||
if ($this->Pot->find('', $text) === false && $this->PoUserStrings->find('', $text) === false && empty($text) === false)
|
||||
{
|
||||
$translation = new Translation('', $text);
|
||||
$this->PotMain[] = $translation;
|
||||
$this->PotMain->toPoFile(__DIR__ . '/../localization/strings.pot');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function GetPluralCount()
|
||||
{
|
||||
if ($this->Po->getHeader(Translations::HEADER_PLURAL) !== null)
|
||||
{
|
||||
return $this->Po->getPluralForms()[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function GetPluralDefinition()
|
||||
{
|
||||
if ($this->Po->getHeader(Translations::HEADER_PLURAL) !== null)
|
||||
{
|
||||
return $this->Po->getPluralForms()[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
return '(n != 1)';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function GetPoAsJsonString()
|
||||
{
|
||||
return $this->Po->toJsonString();
|
||||
}
|
||||
|
||||
public function __construct(string $culture)
|
||||
{
|
||||
@@ -19,15 +74,26 @@ class LocalizationService
|
||||
$this->LoadLocalizations($culture);
|
||||
}
|
||||
|
||||
|
||||
protected function getDatabaseService()
|
||||
public function __n($number, $singularForm, $pluralForm)
|
||||
{
|
||||
return DatabaseService::getInstance();
|
||||
$this->CheckAndAddMissingTranslationToPot($singularForm);
|
||||
|
||||
return sprintf($this->Translator->ngettext($singularForm, $pluralForm, $number), $number);
|
||||
}
|
||||
|
||||
protected function getdatabase()
|
||||
public function __t($text, ...$placeholderValues)
|
||||
{
|
||||
return $this->getDatabaseService()->GetDbConnection();
|
||||
$this->CheckAndAddMissingTranslationToPot($text);
|
||||
|
||||
if (func_num_args() === 1)
|
||||
{
|
||||
return $this->Translator->gettext($text);
|
||||
}
|
||||
else
|
||||
{
|
||||
return vsprintf($this->Translator->gettext($text), ...$placeholderValues);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function getInstance(string $culture)
|
||||
@@ -40,11 +106,15 @@ class LocalizationService
|
||||
return self::$instanceMap[$culture];
|
||||
}
|
||||
|
||||
protected $Pot;
|
||||
protected $PotMain;
|
||||
protected $Po;
|
||||
protected $PoUserStrings;
|
||||
protected $Translator;
|
||||
protected function getDatabaseService()
|
||||
{
|
||||
return DatabaseService::getInstance();
|
||||
}
|
||||
|
||||
protected function getdatabase()
|
||||
{
|
||||
return $this->getDatabaseService()->GetDbConnection();
|
||||
}
|
||||
|
||||
private function LoadLocalizations()
|
||||
{
|
||||
@@ -63,45 +133,53 @@ class LocalizationService
|
||||
$this->Pot = $this->Pot->mergeWith(Translations::fromPoFile(__DIR__ . '/../localization/permissions.pot'));
|
||||
$this->Pot = $this->Pot->mergeWith(Translations::fromPoFile(__DIR__ . '/../localization/locales.pot'));
|
||||
|
||||
|
||||
if (GROCY_MODE !== 'production')
|
||||
{
|
||||
$this->Pot = $this->Pot->mergeWith(Translations::fromPoFile(__DIR__ . '/../localization/demo_data.pot'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->PoUserStrings = new Translations();
|
||||
$this->PoUserStrings->setDomain('grocy/userstrings');
|
||||
|
||||
$this->Po = Translations::fromPoFile(__DIR__ . "/../localization/$culture/strings.po");
|
||||
|
||||
if (file_exists(__DIR__ . "/../localization/$culture/chore_assignment_types.po"))
|
||||
{
|
||||
$this->Po = $this->Po->mergeWith(Translations::fromPoFile(__DIR__ . "/../localization/$culture/chore_assignment_types.po"));
|
||||
}
|
||||
|
||||
if (file_exists(__DIR__ . "/../localization/$culture/component_translations.po"))
|
||||
{
|
||||
$this->Po = $this->Po->mergeWith(Translations::fromPoFile(__DIR__ . "/../localization/$culture/component_translations.po"));
|
||||
}
|
||||
|
||||
if (file_exists(__DIR__ . "/../localization/$culture/stock_transaction_types.po"))
|
||||
{
|
||||
$this->Po = $this->Po->mergeWith(Translations::fromPoFile(__DIR__ . "/../localization/$culture/stock_transaction_types.po"));
|
||||
}
|
||||
|
||||
if (file_exists(__DIR__ . "/../localization/$culture/chore_period_types.po"))
|
||||
{
|
||||
$this->Po = $this->Po->mergeWith(Translations::fromPoFile(__DIR__ . "/../localization/$culture/chore_period_types.po"));
|
||||
}
|
||||
|
||||
if (file_exists(__DIR__ . "/../localization/$culture/userfield_types.po"))
|
||||
{
|
||||
$this->Po = $this->Po->mergeWith(Translations::fromPoFile(__DIR__ . "/../localization/$culture/userfield_types.po"));
|
||||
}
|
||||
|
||||
if (file_exists(__DIR__ . "/../localization/$culture/permissions.po"))
|
||||
{
|
||||
$this->Po = $this->Po->mergeWith(Translations::fromPoFile(__DIR__ . "/../localization/$culture/permissions.po"));
|
||||
}
|
||||
|
||||
if (file_exists(__DIR__ . "/../localization/$culture/locales.po"))
|
||||
{
|
||||
$this->Po = $this->Po->mergeWith(Translations::fromPoFile(__DIR__ . "/../localization/$culture/locales.po"));
|
||||
}
|
||||
|
||||
if (GROCY_MODE !== 'production' && file_exists(__DIR__ . "/../localization/$culture/demo_data.po"))
|
||||
{
|
||||
$this->Po = $this->Po->mergeWith(Translations::fromPoFile(__DIR__ . "/../localization/$culture/demo_data.po"));
|
||||
@@ -128,6 +206,7 @@ class LocalizationService
|
||||
|
||||
$this->PoUserStrings[] = $translation;
|
||||
}
|
||||
|
||||
$this->Po = $this->Po->mergeWith($this->PoUserStrings);
|
||||
}
|
||||
|
||||
@@ -135,66 +214,4 @@ class LocalizationService
|
||||
$this->Translator->loadTranslations($this->Po);
|
||||
}
|
||||
|
||||
public function GetPoAsJsonString()
|
||||
{
|
||||
return $this->Po->toJsonString();
|
||||
}
|
||||
|
||||
public function GetPluralCount()
|
||||
{
|
||||
if ($this->Po->getHeader(Translations::HEADER_PLURAL) !== null)
|
||||
{
|
||||
return $this->Po->getPluralForms()[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetPluralDefinition()
|
||||
{
|
||||
if ($this->Po->getHeader(Translations::HEADER_PLURAL) !== null)
|
||||
{
|
||||
return $this->Po->getPluralForms()[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
return '(n != 1)';
|
||||
}
|
||||
}
|
||||
|
||||
public function __t($text, ...$placeholderValues)
|
||||
{
|
||||
$this->CheckAndAddMissingTranslationToPot($text);
|
||||
|
||||
if (func_num_args() === 1)
|
||||
{
|
||||
return $this->Translator->gettext($text);
|
||||
}
|
||||
else
|
||||
{
|
||||
return vsprintf($this->Translator->gettext($text), ...$placeholderValues);
|
||||
}
|
||||
}
|
||||
|
||||
public function __n($number, $singularForm, $pluralForm)
|
||||
{
|
||||
$this->CheckAndAddMissingTranslationToPot($singularForm);
|
||||
|
||||
return sprintf($this->Translator->ngettext($singularForm, $pluralForm, $number), $number);
|
||||
}
|
||||
|
||||
public function CheckAndAddMissingTranslationToPot($text)
|
||||
{
|
||||
if (GROCY_MODE === 'dev')
|
||||
{
|
||||
if ($this->Pot->find('', $text) === false && $this->PoUserStrings->find('', $text) === false && empty($text) === false)
|
||||
{
|
||||
$translation = new Translation('', $text);
|
||||
$this->PotMain[] = $translation;
|
||||
$this->PotMain->toPoFile(__DIR__ . '/../localization/strings.pot');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,13 +6,74 @@ namespace Grocy\Services;
|
||||
|
||||
class RecipesService extends BaseService
|
||||
{
|
||||
const RECIPE_TYPE_NORMAL = 'normal';
|
||||
const RECIPE_TYPE_MEALPLAN_DAY = 'mealplan-day';
|
||||
|
||||
const RECIPE_TYPE_MEALPLAN_WEEK = 'mealplan-week';
|
||||
|
||||
public function __construct()
|
||||
const RECIPE_TYPE_NORMAL = 'normal';
|
||||
|
||||
public function AddNotFulfilledProductsToShoppingList($recipeId, $excludedProductIds = null)
|
||||
{
|
||||
parent::__construct();
|
||||
$recipe = $this->getDataBase()->recipes($recipeId);
|
||||
|
||||
$recipePositions = $this->GetRecipesPosResolved();
|
||||
|
||||
foreach ($recipePositions as $recipePosition)
|
||||
{
|
||||
if ($recipePosition->recipe_id == $recipeId && !in_array($recipePosition->product_id, $excludedProductIds))
|
||||
{
|
||||
$product = $this->getDataBase()->products($recipePosition->product_id);
|
||||
|
||||
$toOrderAmount = round(($recipePosition->missing_amount - $recipePosition->amount_on_shopping_list) / $product->qu_factor_purchase_to_stock, 2);
|
||||
|
||||
if ($recipe->not_check_shoppinglist == 1)
|
||||
{
|
||||
$toOrderAmount = round($recipePosition->missing_amount / $product->qu_factor_purchase_to_stock, 2);
|
||||
}
|
||||
|
||||
if ($toOrderAmount > 0)
|
||||
{
|
||||
$shoppinglistRow = $this->getDataBase()->shopping_list()->createRow([
|
||||
'product_id' => $recipePosition->product_id,
|
||||
'amount' => $toOrderAmount,
|
||||
'note' => $this->getLocalizationService()->__t('Added for recipe %s', $recipe->name)
|
||||
]);
|
||||
$shoppinglistRow->save();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function ConsumeRecipe($recipeId)
|
||||
{
|
||||
if (!$this->RecipeExists($recipeId))
|
||||
{
|
||||
throw new \Exception('Recipe does not exist');
|
||||
}
|
||||
|
||||
$transactionId = uniqid();
|
||||
$recipePositions = $this->getDatabase()->recipes_pos_resolved()->where('recipe_id', $recipeId)->fetchAll();
|
||||
|
||||
foreach ($recipePositions as $recipePosition)
|
||||
{
|
||||
if ($recipePosition->only_check_single_unit_in_stock == 0)
|
||||
{
|
||||
$this->getStockService()->ConsumeProduct($recipePosition->product_id, $recipePosition->recipe_amount, false, StockService::TRANSACTION_TYPE_CONSUME, 'default', $recipeId, null, $transactionId, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$recipeRow = $this->getDatabase()->recipes()->where('id = :1', $recipeId)->fetch();
|
||||
|
||||
if (!empty($recipeRow->product_id))
|
||||
{
|
||||
$recipeResolvedRow = $this->getDatabase()->recipes_resolved()->where('recipe_id = :1', $recipeId)->fetch();
|
||||
$this->getStockService()->AddProduct($recipeRow->product_id, floatval($recipeRow->desired_servings), null, StockService::TRANSACTION_TYPE_SELF_PRODUCTION, date('Y-m-d'), floatval($recipeResolvedRow->costs));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function GetRecipesPosResolved()
|
||||
@@ -27,59 +88,9 @@ class RecipesService extends BaseService
|
||||
return $this->getDataBaseService()->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
public function AddNotFulfilledProductsToShoppingList($recipeId, $excludedProductIds = null)
|
||||
public function __construct()
|
||||
{
|
||||
$recipe = $this->getDataBase()->recipes($recipeId);
|
||||
|
||||
$recipePositions = $this->GetRecipesPosResolved();
|
||||
foreach ($recipePositions as $recipePosition)
|
||||
{
|
||||
if($recipePosition->recipe_id == $recipeId && !in_array($recipePosition->product_id, $excludedProductIds))
|
||||
{
|
||||
$product = $this->getDataBase()->products($recipePosition->product_id);
|
||||
|
||||
$toOrderAmount = round(($recipePosition->missing_amount - $recipePosition->amount_on_shopping_list) / $product->qu_factor_purchase_to_stock, 2);
|
||||
if ($recipe->not_check_shoppinglist == 1)
|
||||
{
|
||||
$toOrderAmount = round($recipePosition->missing_amount / $product->qu_factor_purchase_to_stock, 2);
|
||||
}
|
||||
|
||||
if($toOrderAmount > 0)
|
||||
{
|
||||
$shoppinglistRow = $this->getDataBase()->shopping_list()->createRow(array(
|
||||
'product_id' => $recipePosition->product_id,
|
||||
'amount' => $toOrderAmount,
|
||||
'note' => $this->getLocalizationService()->__t('Added for recipe %s', $recipe->name)
|
||||
));
|
||||
$shoppinglistRow->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function ConsumeRecipe($recipeId)
|
||||
{
|
||||
if (!$this->RecipeExists($recipeId))
|
||||
{
|
||||
throw new \Exception('Recipe does not exist');
|
||||
}
|
||||
|
||||
$transactionId = uniqid();
|
||||
$recipePositions = $this->getDatabase()->recipes_pos_resolved()->where('recipe_id', $recipeId)->fetchAll();
|
||||
foreach ($recipePositions as $recipePosition)
|
||||
{
|
||||
if ($recipePosition->only_check_single_unit_in_stock == 0)
|
||||
{
|
||||
$this->getStockService()->ConsumeProduct($recipePosition->product_id, $recipePosition->recipe_amount, false, StockService::TRANSACTION_TYPE_CONSUME, 'default', $recipeId, null, $transactionId, true);
|
||||
}
|
||||
}
|
||||
|
||||
$recipeRow = $this->getDatabase()->recipes()->where('id = :1', $recipeId)->fetch();
|
||||
if (!empty($recipeRow->product_id))
|
||||
{
|
||||
$recipeResolvedRow = $this->getDatabase()->recipes_resolved()->where('recipe_id = :1', $recipeId)->fetch();
|
||||
$this->getStockService()->AddProduct($recipeRow->product_id, floatval($recipeRow->desired_servings), null, StockService::TRANSACTION_TYPE_SELF_PRODUCTION, date('Y-m-d'), floatval($recipeResolvedRow->costs));
|
||||
}
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
private function RecipeExists($recipeId)
|
||||
@@ -87,4 +98,5 @@ class RecipesService extends BaseService
|
||||
$recipeRow = $this->getDataBase()->recipes()->where('id = :1', $recipeId)->fetch();
|
||||
return $recipeRow !== null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -4,6 +4,47 @@ namespace Grocy\Services;
|
||||
|
||||
class SessionService extends BaseService
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function CreateSession($userId, $stayLoggedInPermanently = false)
|
||||
{
|
||||
$newSessionKey = $this->GenerateSessionKey();
|
||||
|
||||
$expires = date('Y-m-d H:i:s', intval(time() + 2592000));
|
||||
|
||||
// Default is that sessions expire in 30 days
|
||||
if ($stayLoggedInPermanently === true)
|
||||
{
|
||||
$expires = date('Y-m-d H:i:s', PHP_INT_SIZE == 4 ? PHP_INT_MAX : PHP_INT_MAX >> 32); // Never
|
||||
}
|
||||
|
||||
$sessionRow = $this->getDatabase()->sessions()->createRow([
|
||||
'user_id' => $userId,
|
||||
'session_key' => $newSessionKey,
|
||||
'expires' => $expires
|
||||
]);
|
||||
$sessionRow->save();
|
||||
|
||||
return $newSessionKey;
|
||||
}
|
||||
|
||||
public function GetDefaultUser()
|
||||
{
|
||||
return $this->getDatabase()->users(1);
|
||||
}
|
||||
|
||||
public function GetUserBySessionKey($sessionKey)
|
||||
{
|
||||
$sessionRow = $this->getDatabase()->sessions()->where('session_key', $sessionKey)->fetch();
|
||||
|
||||
if ($sessionRow !== null)
|
||||
{
|
||||
return $this->getDatabase()->users($sessionRow->user_id);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
@@ -17,14 +58,15 @@ class SessionService extends BaseService
|
||||
else
|
||||
{
|
||||
$sessionRow = $this->getDatabase()->sessions()->where('session_key = :1 AND expires > :2', $sessionKey, date('Y-m-d H:i:s', time()))->fetch();
|
||||
|
||||
if ($sessionRow !== null)
|
||||
{
|
||||
// This should not change the database file modification time as this is used
|
||||
// This should not change the database file modification time as this is used
|
||||
// to determine if REALLY something has changed
|
||||
$dbModTime = $this->getDatabaseService()->GetDbChangedTime();
|
||||
$sessionRow->update(array(
|
||||
$sessionRow->update([
|
||||
'last_used' => date('Y-m-d H:i:s', time())
|
||||
));
|
||||
]);
|
||||
$this->getDatabaseService()->SetDbChangedTime($dbModTime);
|
||||
|
||||
return true;
|
||||
@@ -33,30 +75,9 @@ class SessionService extends BaseService
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function CreateSession($userId, $stayLoggedInPermanently = false)
|
||||
{
|
||||
$newSessionKey = $this->GenerateSessionKey();
|
||||
|
||||
$expires = date('Y-m-d H:i:s', intval(time() + 2592000)); // Default is that sessions expire in 30 days
|
||||
if ($stayLoggedInPermanently === true)
|
||||
{
|
||||
$expires = date('Y-m-d H:i:s', PHP_INT_SIZE == 4 ? PHP_INT_MAX : PHP_INT_MAX>>32); // Never
|
||||
}
|
||||
|
||||
$sessionRow = $this->getDatabase()->sessions()->createRow(array(
|
||||
'user_id' => $userId,
|
||||
'session_key' => $newSessionKey,
|
||||
'expires' => $expires
|
||||
));
|
||||
$sessionRow->save();
|
||||
|
||||
return $newSessionKey;
|
||||
}
|
||||
|
||||
public function RemoveSession($sessionKey)
|
||||
@@ -64,23 +85,9 @@ class SessionService extends BaseService
|
||||
$this->getDatabase()->sessions()->where('session_key', $sessionKey)->delete();
|
||||
}
|
||||
|
||||
public function GetUserBySessionKey($sessionKey)
|
||||
{
|
||||
$sessionRow = $this->getDatabase()->sessions()->where('session_key', $sessionKey)->fetch();
|
||||
if ($sessionRow !== null)
|
||||
{
|
||||
return $this->getDatabase()->users($sessionRow->user_id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetDefaultUser()
|
||||
{
|
||||
return $this->getDatabase()->users(1);
|
||||
}
|
||||
|
||||
private function GenerateSessionKey()
|
||||
{
|
||||
return RandomString(50);
|
||||
}
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -18,10 +18,10 @@ class TasksService extends BaseService
|
||||
}
|
||||
|
||||
$taskRow = $this->getDatabase()->tasks()->where('id = :1', $taskId)->fetch();
|
||||
$taskRow->update(array(
|
||||
$taskRow->update([
|
||||
'done' => 1,
|
||||
'done_timestamp' => $doneTime
|
||||
));
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -34,10 +34,10 @@ class TasksService extends BaseService
|
||||
}
|
||||
|
||||
$taskRow = $this->getDatabase()->tasks()->where('id = :1', $taskId)->fetch();
|
||||
$taskRow->update(array(
|
||||
$taskRow->update([
|
||||
'done' => 0,
|
||||
'done_timestamp' => null
|
||||
));
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -47,4 +47,5 @@ class TasksService extends BaseService
|
||||
$taskRow = $this->getDatabase()->tasks()->where('id = :1', $taskId)->fetch();
|
||||
return $taskRow !== null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -4,33 +4,69 @@ namespace Grocy\Services;
|
||||
|
||||
class UserfieldsService extends BaseService
|
||||
{
|
||||
const USERFIELD_TYPE_SINGLE_LINE_TEXT = 'text-single-line';
|
||||
const USERFIELD_TYPE_SINGLE_MULTILINE_TEXT = 'text-multi-line';
|
||||
const USERFIELD_TYPE_INTEGRAL_NUMBER = 'number-integral';
|
||||
const USERFIELD_TYPE_DECIMAL_NUMBER = 'number-decimal';
|
||||
const USERFIELD_TYPE_DATE = 'date';
|
||||
const USERFIELD_TYPE_DATETIME = 'datetime';
|
||||
const USERFIELD_TYPE_CHECKBOX = 'checkbox';
|
||||
const USERFIELD_TYPE_PRESET_LIST = 'preset-list';
|
||||
const USERFIELD_TYPE_PRESET_CHECKLIST = 'preset-checklist';
|
||||
const USERFIELD_TYPE_LINK = 'link';
|
||||
const USERFIELD_TYPE_FILE = 'file';
|
||||
const USERFIELD_TYPE_IMAGE = 'image';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
const USERFIELD_TYPE_DATE = 'date';
|
||||
|
||||
const USERFIELD_TYPE_DATETIME = 'datetime';
|
||||
|
||||
const USERFIELD_TYPE_DECIMAL_NUMBER = 'number-decimal';
|
||||
|
||||
const USERFIELD_TYPE_FILE = 'file';
|
||||
|
||||
const USERFIELD_TYPE_IMAGE = 'image';
|
||||
|
||||
const USERFIELD_TYPE_INTEGRAL_NUMBER = 'number-integral';
|
||||
|
||||
const USERFIELD_TYPE_LINK = 'link';
|
||||
|
||||
const USERFIELD_TYPE_PRESET_CHECKLIST = 'preset-checklist';
|
||||
|
||||
const USERFIELD_TYPE_PRESET_LIST = 'preset-list';
|
||||
|
||||
const USERFIELD_TYPE_SINGLE_LINE_TEXT = 'text-single-line';
|
||||
|
||||
const USERFIELD_TYPE_SINGLE_MULTILINE_TEXT = 'text-multi-line';
|
||||
|
||||
protected $OpenApiSpec = null;
|
||||
|
||||
protected function getOpenApispec()
|
||||
public function GetAllFields()
|
||||
{
|
||||
if($this->OpenApiSpec == null)
|
||||
return $this->getDatabase()->userfields()->orderBy('name')->fetchAll();
|
||||
}
|
||||
|
||||
public function GetAllValues($entity)
|
||||
{
|
||||
if (!$this->IsValidEntity($entity))
|
||||
{
|
||||
$this->OpenApiSpec = json_decode(file_get_contents(__DIR__ . '/../grocy.openapi.json'));
|
||||
throw new \Exception('Entity does not exist or is not exposed');
|
||||
}
|
||||
return $this->OpenApiSpec;
|
||||
|
||||
return $this->getDatabase()->userfield_values_resolved()->where('entity', $entity)->orderBy('name')->fetchAll();
|
||||
}
|
||||
|
||||
public function GetEntities()
|
||||
{
|
||||
$exposedDefaultEntities = $this->getOpenApiSpec()->components->internalSchemas->ExposedEntity->enum;
|
||||
|
||||
$userentities = [];
|
||||
|
||||
foreach ($this->getDatabase()->userentities()->orderBy('name') as $userentity)
|
||||
{
|
||||
$userentities[] = 'userentity-' . $userentity->name;
|
||||
}
|
||||
|
||||
return array_merge($exposedDefaultEntities, $userentities);
|
||||
}
|
||||
|
||||
public function GetField($fieldId)
|
||||
{
|
||||
return $this->getDatabase()->userfields($fieldId);
|
||||
}
|
||||
|
||||
public function GetFieldTypes()
|
||||
{
|
||||
return GetClassConstants('\Grocy\Services\UserfieldsService');
|
||||
}
|
||||
|
||||
public function GetFields($entity)
|
||||
@@ -43,16 +79,6 @@ class UserfieldsService extends BaseService
|
||||
return $this->getDatabase()->userfields()->where('entity', $entity)->orderBy('name')->fetchAll();
|
||||
}
|
||||
|
||||
public function GetField($fieldId)
|
||||
{
|
||||
return $this->getDatabase()->userfields($fieldId);
|
||||
}
|
||||
|
||||
public function GetAllFields()
|
||||
{
|
||||
return $this->getDatabase()->userfields()->orderBy('name')->fetchAll();
|
||||
}
|
||||
|
||||
public function GetValues($entity, $objectId)
|
||||
{
|
||||
if (!$this->IsValidEntity($entity))
|
||||
@@ -61,7 +87,8 @@ class UserfieldsService extends BaseService
|
||||
}
|
||||
|
||||
$userfields = $this->getDatabase()->userfield_values_resolved()->where('entity = :1 AND object_id = :2', $entity, $objectId)->orderBy('name')->fetchAll();
|
||||
$userfieldKeyValuePairs = array();
|
||||
$userfieldKeyValuePairs = [];
|
||||
|
||||
foreach ($userfields as $userfield)
|
||||
{
|
||||
$userfieldKeyValuePairs[$userfield->name] = $userfield->value;
|
||||
@@ -70,16 +97,6 @@ class UserfieldsService extends BaseService
|
||||
return $userfieldKeyValuePairs;
|
||||
}
|
||||
|
||||
public function GetAllValues($entity)
|
||||
{
|
||||
if (!$this->IsValidEntity($entity))
|
||||
{
|
||||
throw new \Exception('Entity does not exist or is not exposed');
|
||||
}
|
||||
|
||||
return $this->getDatabase()->userfield_values_resolved()->where('entity', $entity)->orderBy('name')->fetchAll();
|
||||
}
|
||||
|
||||
public function SetValues($entity, $objectId, $userfields)
|
||||
{
|
||||
if (!$this->IsValidEntity($entity))
|
||||
@@ -99,44 +116,45 @@ class UserfieldsService extends BaseService
|
||||
$fieldId = $fieldRow->id;
|
||||
|
||||
$alreadyExistingEntry = $this->getDatabase()->userfield_values()->where('field_id = :1 AND object_id = :2', $fieldId, $objectId)->fetch();
|
||||
|
||||
if ($alreadyExistingEntry) // Update
|
||||
{
|
||||
$alreadyExistingEntry->update(array(
|
||||
$alreadyExistingEntry->update([
|
||||
'value' => $value
|
||||
));
|
||||
]);
|
||||
}
|
||||
else // Insert
|
||||
{
|
||||
$newRow = $this->getDatabase()->userfield_values()->createRow(array(
|
||||
$newRow = $this->getDatabase()->userfield_values()->createRow([
|
||||
'field_id' => $fieldId,
|
||||
'object_id' => $objectId,
|
||||
'value' => $value
|
||||
));
|
||||
]);
|
||||
$newRow->save();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function GetEntities()
|
||||
public function __construct()
|
||||
{
|
||||
$exposedDefaultEntities = $this->getOpenApiSpec()->components->internalSchemas->ExposedEntity->enum;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
$userentities = array();
|
||||
foreach ($this->getDatabase()->userentities()->orderBy('name') as $userentity)
|
||||
protected function getOpenApispec()
|
||||
{
|
||||
if ($this->OpenApiSpec == null)
|
||||
{
|
||||
$userentities[] = 'userentity-' . $userentity->name;
|
||||
$this->OpenApiSpec = json_decode(file_get_contents(__DIR__ . '/../grocy.openapi.json'));
|
||||
}
|
||||
|
||||
return array_merge($exposedDefaultEntities, $userentities);
|
||||
}
|
||||
|
||||
public function GetFieldTypes()
|
||||
{
|
||||
return GetClassConstants('\Grocy\Services\UserfieldsService');
|
||||
return $this->OpenApiSpec;
|
||||
}
|
||||
|
||||
private function IsValidEntity($entity)
|
||||
{
|
||||
return in_array($entity, $this->GetEntities());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -6,25 +6,34 @@ class UsersService extends BaseService
|
||||
{
|
||||
public function CreateUser(string $username, string $firstName, string $lastName, string $password)
|
||||
{
|
||||
$newUserRow = $this->getDatabase()->users()->createRow(array(
|
||||
$newUserRow = $this->getDatabase()->users()->createRow([
|
||||
'username' => $username,
|
||||
'first_name' => $firstName,
|
||||
'last_name' => $lastName,
|
||||
'password' => password_hash($password, PASSWORD_DEFAULT)
|
||||
));
|
||||
]);
|
||||
$newUserRow = $newUserRow->save();
|
||||
$permList = array();
|
||||
foreach ($this->getDatabase()->permission_hierarchy()->where('name', GROCY_DEFAULT_PERMISSIONS)->fetchAll() as $perm) {
|
||||
$permList[] = array(
|
||||
$permList = [];
|
||||
|
||||
foreach ($this->getDatabase()->permission_hierarchy()->where('name', GROCY_DEFAULT_PERMISSIONS)->fetchAll() as $perm)
|
||||
{
|
||||
$permList[] = [
|
||||
'user_id' => $newUserRow->id,
|
||||
'permission_id' => $perm->id
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
$this->getDatabase()->user_permissions()->insert($permList);
|
||||
|
||||
return $newUserRow;
|
||||
}
|
||||
|
||||
public function DeleteUser($userId)
|
||||
{
|
||||
$row = $this->getDatabase()->users($userId);
|
||||
$row->delete();
|
||||
}
|
||||
|
||||
public function EditUser(int $userId, string $username, string $firstName, string $lastName, string $password)
|
||||
{
|
||||
if (!$this->UserExists($userId))
|
||||
@@ -33,36 +42,18 @@ class UsersService extends BaseService
|
||||
}
|
||||
|
||||
$user = $this->getDatabase()->users($userId);
|
||||
$user->update(array(
|
||||
$user->update([
|
||||
'username' => $username,
|
||||
'first_name' => $firstName,
|
||||
'last_name' => $lastName,
|
||||
'password' => password_hash($password, PASSWORD_DEFAULT)
|
||||
));
|
||||
}
|
||||
|
||||
public function DeleteUser($userId)
|
||||
{
|
||||
$row = $this->getDatabase()->users($userId);
|
||||
$row->delete();
|
||||
}
|
||||
|
||||
public function GetUsersAsDto()
|
||||
{
|
||||
$users = $this->getDatabase()->users();
|
||||
$returnUsers = array();
|
||||
foreach ($users as $user)
|
||||
{
|
||||
unset($user->password);
|
||||
$user->display_name = GetUserDisplayName($user);
|
||||
$returnUsers[] = $user;
|
||||
}
|
||||
return $returnUsers;
|
||||
]);
|
||||
}
|
||||
|
||||
public function GetUserSetting($userId, $settingKey)
|
||||
{
|
||||
$settingRow = $this->getDatabase()->user_settings()->where('user_id = :1 AND key = :2', $userId, $settingKey)->fetch();
|
||||
|
||||
if ($settingRow !== null)
|
||||
{
|
||||
return $settingRow->value;
|
||||
@@ -71,13 +62,15 @@ class UsersService extends BaseService
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function GetUserSettings($userId)
|
||||
{
|
||||
$settings = array();
|
||||
$settings = [];
|
||||
|
||||
$settingRows = $this->getDatabase()->user_settings()->where('user_id = :1', $userId)->fetchAll();
|
||||
|
||||
foreach ($settingRows as $settingRow)
|
||||
{
|
||||
$settings[$settingRow->key] = $settingRow->value;
|
||||
@@ -88,25 +81,42 @@ class UsersService extends BaseService
|
||||
return array_merge($GROCY_DEFAULT_USER_SETTINGS, $settings);
|
||||
}
|
||||
|
||||
public function GetUsersAsDto()
|
||||
{
|
||||
$users = $this->getDatabase()->users();
|
||||
$returnUsers = [];
|
||||
|
||||
foreach ($users as $user)
|
||||
{
|
||||
unset($user->password);
|
||||
$user->display_name = GetUserDisplayName($user);
|
||||
$returnUsers[] = $user;
|
||||
}
|
||||
|
||||
return $returnUsers;
|
||||
}
|
||||
|
||||
public function SetUserSetting($userId, $settingKey, $settingValue)
|
||||
{
|
||||
$settingRow = $this->getDatabase()->user_settings()->where('user_id = :1 AND key = :2', $userId, $settingKey)->fetch();
|
||||
|
||||
if ($settingRow !== null)
|
||||
{
|
||||
$settingRow->update(array(
|
||||
$settingRow->update([
|
||||
'value' => $settingValue,
|
||||
'row_updated_timestamp' => date('Y-m-d H:i:s')
|
||||
));
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$settingRow = $this->getDatabase()->user_settings()->createRow(array(
|
||||
$settingRow = $this->getDatabase()->user_settings()->createRow([
|
||||
'user_id' => $userId,
|
||||
'key' => $settingKey,
|
||||
'value' => $settingValue
|
||||
));
|
||||
]);
|
||||
$settingRow->save();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function UserExists($userId)
|
||||
@@ -114,4 +124,5 @@ class UsersService extends BaseService
|
||||
$userRow = $this->getDatabase()->users()->where('id = :1', $userId)->fetch();
|
||||
return $userRow !== null;
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user