mirror of
https://github.com/grocy/grocy.git
synced 2026-04-07 05:16:15 +02:00
use singletons to reduce need to recreate the same objects
This commit is contained in:
parent
e493abf784
commit
fca0bd73ad
37
Docker-compose.yaml
Normal file
37
Docker-compose.yaml
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Usage:
|
||||||
|
# docker-compose build && docker-compose up
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
grocy-nginx:
|
||||||
|
image: grocy/grocy-docker:nginx
|
||||||
|
# build:
|
||||||
|
# context: .
|
||||||
|
# dockerfile: Dockerfile-grocy-nginx
|
||||||
|
depends_on:
|
||||||
|
- grocy
|
||||||
|
ports:
|
||||||
|
- '80:80'
|
||||||
|
- '443:443'
|
||||||
|
volumes_from:
|
||||||
|
- grocy
|
||||||
|
container_name: grocy-nginx
|
||||||
|
|
||||||
|
grocy:
|
||||||
|
image: grocy/grocy-docker:grocy
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
expose:
|
||||||
|
- 9000
|
||||||
|
volumes:
|
||||||
|
- ./data_store:/www/data
|
||||||
|
environment:
|
||||||
|
PHP_MEMORY_LIMIT: 512M
|
||||||
|
MAX_UPLOAD: 50M
|
||||||
|
PHP_MAX_FILE_UPLOAD: 200
|
||||||
|
PHP_MAX_POST: 100M
|
||||||
|
GROCY_CULTURE: en
|
||||||
|
container_name: grocy
|
||||||
|
#volumes:
|
||||||
|
# database:
|
||||||
78
Dockerfile
Normal file
78
Dockerfile
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
FROM php:7.2-fpm-alpine
|
||||||
|
LABEL maintainer="Talmai Oliveira <to@talm.ai>"
|
||||||
|
|
||||||
|
RUN docker-php-ext-install opcache
|
||||||
|
|
||||||
|
#COPY grocy.tar.gz .
|
||||||
|
RUN echo $PWD
|
||||||
|
RUN mkdir /var/www/html/grocy-dev
|
||||||
|
ADD ./ /var/www/html/grocy-dev/
|
||||||
|
RUN apk update && \
|
||||||
|
apk upgrade && \
|
||||||
|
apk add --no-cache --update yarn git python py-pip wget freetype libpng libjpeg-turbo freetype-dev libpng-dev libjpeg-turbo-dev && \
|
||||||
|
docker-php-ext-configure gd \
|
||||||
|
--with-gd \
|
||||||
|
--with-freetype-dir=/usr/include/ \
|
||||||
|
--with-png-dir=/usr/include/ \
|
||||||
|
--with-jpeg-dir=/usr/include/ && \
|
||||||
|
NPROC=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || 1) && \
|
||||||
|
docker-php-ext-install -j${NPROC} gd && \
|
||||||
|
mkdir -p /www && \
|
||||||
|
# Set environments
|
||||||
|
sed -i "s|;*daemonize\s*=\s*yes|daemonize = no|g" /usr/local/etc/php-fpm.conf && \
|
||||||
|
sed -i "s|;*listen\s*=\s*127.0.0.1:9000|listen = 9000|g" /usr/local/etc/php-fpm.conf && \
|
||||||
|
sed -i "s|;*listen\s*=\s*/||g" /usr/local/etc/php-fpm.conf && \
|
||||||
|
sed -i "s|;*chdir\s*=\s*/var/www|chdir = /www|g" /usr/local/etc/php-fpm.d/www.conf && \
|
||||||
|
#echo "[opcache]" >> /usr/local/etc/php-fpm.d/www.conf && \
|
||||||
|
#echo "zend_extension=opcache.so" >> /usr/local/etc/php-fpm.conf && \
|
||||||
|
#echo "opcache.enable=1" >> /usr/local/etc/php-fpm.d/www.conf && \
|
||||||
|
#echo "opcache.memory_consumption=128" >> /usr/local/etc/php-fpm.d/www.conf && \
|
||||||
|
#echo "opcache.interned_strings_buffer=8" >> /usr/local/etc/php-fpm.d/www.conf && \
|
||||||
|
wget https://getcomposer.org/installer -O - -q | php -- --quiet && \
|
||||||
|
#pip install lastversion==0.2.4 && \
|
||||||
|
#mkdir -p /tmp/download && \
|
||||||
|
#wget -t 3 -T 30 -nv -O "grocy.tar.gz" $(lastversion --source grocy/grocy) && \
|
||||||
|
#tar xzf grocy.tar.gz && \
|
||||||
|
#rm -f grocy.tar.gz && \
|
||||||
|
cd grocy-dev && \
|
||||||
|
echo $PWD && \
|
||||||
|
ls -lrt . && \
|
||||||
|
mv public /www/public && \
|
||||||
|
mv controllers /www/controllers && \
|
||||||
|
mv data /www/data && \
|
||||||
|
mv helpers /www/helpers && \
|
||||||
|
mv localization/ /www/localization && \
|
||||||
|
mv middleware/ /www/middleware && \
|
||||||
|
mv migrations/ /www/migrations && \
|
||||||
|
mv publication_assets/ /www/publication_assets && \
|
||||||
|
mv services/ /www/services && \
|
||||||
|
mv views/ /www/views && \
|
||||||
|
mv .yarnrc /www/ && \
|
||||||
|
mv *.php /www/ && \
|
||||||
|
mv *.json /www/ && \
|
||||||
|
mv composer.* /root/.composer/ && \
|
||||||
|
mv *yarn* /www/ && \
|
||||||
|
mv *.sh /www/ && \
|
||||||
|
# Cleaning up
|
||||||
|
rm -rf /tmp/download && \
|
||||||
|
rm -rf /var/cache/apk/*
|
||||||
|
|
||||||
|
|
||||||
|
# run php composer.phar with -vvv for extra debug information
|
||||||
|
RUN cd /var/www/html && \
|
||||||
|
php composer.phar --working-dir=/www/ -n install && \
|
||||||
|
cp /www/config-dist.php /www/data/config.php && \
|
||||||
|
cd /www && \
|
||||||
|
yarn install && \
|
||||||
|
if [ -d /www/data/viewcache ]; then rm -rf /www/data/viewcache; fi && \
|
||||||
|
mkdir /www/data/viewcache && \
|
||||||
|
chown www-data:www-data -R /www/
|
||||||
|
|
||||||
|
# Set Workdir
|
||||||
|
WORKDIR /www/public
|
||||||
|
|
||||||
|
# Expose volumes
|
||||||
|
#VOLUME ["/www"]
|
||||||
|
|
||||||
|
# Expose ports
|
||||||
|
EXPOSE 9000
|
||||||
8
app.php
8
app.php
|
|
@ -74,6 +74,14 @@ $appContainer = new \Slim\Container([
|
||||||
]);
|
]);
|
||||||
$app = new \Slim\App($appContainer);
|
$app = new \Slim\App($appContainer);
|
||||||
|
|
||||||
|
$fp = fopen('/www/data/sql.log', 'a');
|
||||||
|
fwrite($fp, "!!!Starting up loading app\n");
|
||||||
|
fwrite($fp, "!!!".print_r(ini_get_all(),True)."\n");
|
||||||
|
#fwrite($fp, "!!!".print_r(opcache_get_status(),True)."\n");
|
||||||
|
fclose($fp);
|
||||||
|
|
||||||
|
phpinfo();
|
||||||
|
|
||||||
// Load routes from separate file
|
// Load routes from separate file
|
||||||
require_once __DIR__ . '/routes.php';
|
require_once __DIR__ . '/routes.php';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@ use \Grocy\Services\UsersService;
|
||||||
class BaseController
|
class BaseController
|
||||||
{
|
{
|
||||||
public function __construct(\Slim\Container $container) {
|
public function __construct(\Slim\Container $container) {
|
||||||
$databaseService = new DatabaseService();
|
$databaseService = DatabaseService::getInstance();
|
||||||
$this->Database = $databaseService->GetDbConnection();
|
$this->Database = $databaseService->GetDbConnection();
|
||||||
|
|
||||||
$localizationService = new LocalizationService(GROCY_CULTURE);
|
$localizationService = new LocalizationService(GROCY_CULTURE);
|
||||||
$this->LocalizationService = $localizationService;
|
$this->LocalizationService = $localizationService;
|
||||||
|
|
||||||
$applicationService = new ApplicationService();
|
$applicationService = ApplicationService::getInstance();
|
||||||
$versionInfo = $applicationService->GetInstalledVersion();
|
$versionInfo = $applicationService->GetInstalledVersion();
|
||||||
$container->view->set('version', $versionInfo->Version);
|
$container->view->set('version', $versionInfo->Version);
|
||||||
$container->view->set('releaseDate', $versionInfo->ReleaseDate);
|
$container->view->set('releaseDate', $versionInfo->ReleaseDate);
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class OpenApiController extends BaseApiController
|
||||||
|
|
||||||
public function DocumentationSpec(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
public function DocumentationSpec(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||||
{
|
{
|
||||||
$applicationService = new ApplicationService();
|
$applicationService = ApplicationService::getInstance();
|
||||||
|
|
||||||
$versionInfo = $applicationService->GetInstalledVersion();
|
$versionInfo = $applicationService->GetInstalledVersion();
|
||||||
$this->OpenApiSpec->info->version = $versionInfo->Version;
|
$this->OpenApiSpec->info->version = $versionInfo->Version;
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,12 @@ class StockApiController extends BaseApiController
|
||||||
{
|
{
|
||||||
$requestBody = $request->getParsedBody();
|
$requestBody = $request->getParsedBody();
|
||||||
|
|
||||||
|
$result = null;
|
||||||
|
|
||||||
|
$fp = fopen('/www/data/sql.log', 'a');
|
||||||
|
fwrite($fp, "???executing api consume product");
|
||||||
|
$time_start = microtime(true);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if ($requestBody === null)
|
if ($requestBody === null)
|
||||||
|
|
@ -154,12 +160,15 @@ class StockApiController extends BaseApiController
|
||||||
}
|
}
|
||||||
|
|
||||||
$bookingId = $this->StockService->ConsumeProduct($args['productId'], $requestBody['amount'], $spoiled, $transactionType, $specificStockEntryId, $recipeId);
|
$bookingId = $this->StockService->ConsumeProduct($args['productId'], $requestBody['amount'], $spoiled, $transactionType, $specificStockEntryId, $recipeId);
|
||||||
return $this->ApiResponse($this->Database->stock_log($bookingId));
|
$result = $this->ApiResponse($this->Database->stock_log($bookingId));
|
||||||
}
|
}
|
||||||
catch (\Exception $ex)
|
catch (\Exception $ex)
|
||||||
{
|
{
|
||||||
return $this->GenericErrorResponse($response, $ex->getMessage());
|
$result = $this->GenericErrorResponse($response, $ex->getMessage());
|
||||||
}
|
}
|
||||||
|
fwrite($fp, "???API Consume product - Total execution time in seconds: " . round((microtime(true) - $time_start),6) . "\n");
|
||||||
|
fclose($fp);
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ConsumeProductByBarcode(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
public function ConsumeProductByBarcode(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ class StockController extends BaseController
|
||||||
|
|
||||||
public function __construct(\Slim\Container $container)
|
public function __construct(\Slim\Container $container)
|
||||||
{
|
{
|
||||||
|
$fp = fopen('/www/data/sql.log', 'a');
|
||||||
|
fwrite($fp, "!!!constructing StockController\n");
|
||||||
|
fclose($fp);
|
||||||
parent::__construct($container);
|
parent::__construct($container);
|
||||||
$this->StockService = new StockService();
|
$this->StockService = new StockService();
|
||||||
$this->UserfieldsService = new UserfieldsService();
|
$this->UserfieldsService = new UserfieldsService();
|
||||||
|
|
@ -48,10 +51,15 @@ class StockController extends BaseController
|
||||||
|
|
||||||
public function Consume(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
public function Consume(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||||
{
|
{
|
||||||
return $this->AppContainer->view->render($response, 'consume', [
|
$fp = fopen('/www/data/sql.log', 'a');
|
||||||
|
fwrite($fp, "???executing consume stock");
|
||||||
|
$time_start = microtime(true);
|
||||||
|
$result = $this->AppContainer->view->render($response, 'consume', [
|
||||||
'products' => $this->Database->products()->orderBy('name'),
|
'products' => $this->Database->products()->orderBy('name'),
|
||||||
'recipes' => $this->Database->recipes()->orderBy('name')
|
'recipes' => $this->Database->recipes()->orderBy('name')
|
||||||
]);
|
]);
|
||||||
|
fwrite($fp, "???Total execution time in seconds: " . round((microtime(true) - $time_start),6) . "\n");
|
||||||
|
fclose($fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Inventory(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
public function Inventory(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ class SystemApiController extends BaseApiController
|
||||||
public function __construct(\Slim\Container $container)
|
public function __construct(\Slim\Container $container)
|
||||||
{
|
{
|
||||||
parent::__construct($container);
|
parent::__construct($container);
|
||||||
$this->DatabaseService = new DatabaseService();
|
$this->DatabaseService = DatabaseService::getInstance();
|
||||||
$this->ApplicationService = new ApplicationService();
|
$this->ApplicationService = ApplicationService::getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $DatabaseService;
|
protected $DatabaseService;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ class SystemController extends BaseController
|
||||||
public function __construct(\Slim\Container $container)
|
public function __construct(\Slim\Container $container)
|
||||||
{
|
{
|
||||||
parent::__construct($container);
|
parent::__construct($container);
|
||||||
$this->ApplicationService = new ApplicationService();
|
$this->ApplicationService = ApplicationService::getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Root(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
public function Root(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ class BaseMiddleware
|
||||||
public function __construct(\Slim\Container $container)
|
public function __construct(\Slim\Container $container)
|
||||||
{
|
{
|
||||||
$this->AppContainer = $container;
|
$this->AppContainer = $container;
|
||||||
$this->ApplicationService = new ApplicationService();
|
$this->ApplicationService = ApplicationService::getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $AppContainer;
|
protected $AppContainer;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,22 @@ namespace Grocy\Services;
|
||||||
class ApplicationService extends BaseService
|
class ApplicationService extends BaseService
|
||||||
{
|
{
|
||||||
private $InstalledVersion;
|
private $InstalledVersion;
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
public static function getInstance()
|
||||||
|
{
|
||||||
|
if (self::$instance == null)
|
||||||
|
{
|
||||||
|
self::$instance = new $self();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
public function GetInstalledVersion()
|
public function GetInstalledVersion()
|
||||||
{
|
{
|
||||||
if ($this->InstalledVersion == null)
|
if ($this->InstalledVersion == null)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use \Grocy\Services\LocalizationService;
|
||||||
class BaseService
|
class BaseService
|
||||||
{
|
{
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->DatabaseService = new DatabaseService();
|
$this->DatabaseService = DatabaseService::getInstance();
|
||||||
$this->Database = $this->DatabaseService->GetDbConnection();
|
$this->Database = $this->DatabaseService->GetDbConnection();
|
||||||
|
|
||||||
$localizationService = new LocalizationService(GROCY_CULTURE);
|
$localizationService = new LocalizationService(GROCY_CULTURE);
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,59 @@ namespace Grocy\Services;
|
||||||
|
|
||||||
use \Grocy\Services\ApplicationService;
|
use \Grocy\Services\ApplicationService;
|
||||||
|
|
||||||
|
class PDOWrap
|
||||||
|
{
|
||||||
|
private $instance = null;
|
||||||
|
public function __construct(){
|
||||||
|
$pars = func_get_args();
|
||||||
|
$this->instance = is_object($obj='PDO')?$obj:new $obj(
|
||||||
|
$pars[0],
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
array(\PDO::ATTR_PERSISTENT => true)
|
||||||
|
);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call($name,$pars){
|
||||||
|
$result = null;
|
||||||
|
|
||||||
|
$fp = fopen('/www/data/sql.log', 'a');
|
||||||
|
fwrite($fp, "PDO::".$name." called with arguments:- ".implode( ", ", $pars)."\n");
|
||||||
|
$time_start = microtime(true);
|
||||||
|
if(in_array($name, array("exec","query")))
|
||||||
|
{
|
||||||
|
fwrite($fp, array_values($pars)[0] . "\n");
|
||||||
|
$result = call_user_func_array([$this->instance,$name],$pars);
|
||||||
|
}else{
|
||||||
|
$result = call_user_func_array([$this->instance,$name],$pars);
|
||||||
|
}
|
||||||
|
fwrite($fp, "Total execution time in seconds: " . round((microtime(true) - $time_start),6) . "\n");
|
||||||
|
fclose($fp);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class DatabaseService
|
class DatabaseService
|
||||||
{
|
{
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
public static function getInstance()
|
||||||
|
{
|
||||||
|
if (self::$instance == null)
|
||||||
|
{
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private function GetDbFilePath()
|
private function GetDbFilePath()
|
||||||
{
|
{
|
||||||
if (GROCY_MODE === 'demo' || GROCY_MODE === 'prerelease')
|
if (GROCY_MODE === 'demo' || GROCY_MODE === 'prerelease')
|
||||||
|
|
@ -16,34 +67,54 @@ class DatabaseService
|
||||||
return GROCY_DATAPATH . '/grocy.db';
|
return GROCY_DATAPATH . '/grocy.db';
|
||||||
}
|
}
|
||||||
|
|
||||||
private $DbConnectionRaw;
|
#private $DbConnectionRaw;
|
||||||
|
private static $DbConnectionRaw = null;
|
||||||
/**
|
/**
|
||||||
* @return \PDO
|
* @return \PDO
|
||||||
*/
|
*/
|
||||||
public function GetDbConnectionRaw()
|
public function GetDbConnectionRaw()
|
||||||
{
|
{
|
||||||
if ($this->DbConnectionRaw == null)
|
if (self::$DbConnectionRaw == null)
|
||||||
|
#if ($this->DbConnectionRaw == null)
|
||||||
{
|
{
|
||||||
$pdo = new \PDO('sqlite:' . $this->GetDbFilePath());
|
$fp = fopen('/www/data/sql.log', 'a');
|
||||||
|
fwrite($fp, "+++Creating new PDO object\n");
|
||||||
|
$time_start = microtime(true);
|
||||||
|
$pdo = new PDOWrap('sqlite:' . $this->GetDbFilePath());
|
||||||
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||||
$this->DbConnectionRaw = $pdo;
|
self::$DbConnectionRaw = $pdo;
|
||||||
|
#$this->DbConnectionRaw = $pdo;
|
||||||
|
fwrite($fp, "+++Total execution time in seconds: " . round((microtime(true) - $time_start),6) . "\n");
|
||||||
|
fwrite($fp, "+++object created\n");
|
||||||
|
fclose($fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->DbConnectionRaw;
|
return self::$DbConnectionRaw;
|
||||||
|
#return $this->DbConnectionRaw;
|
||||||
}
|
}
|
||||||
|
|
||||||
private $DbConnection;
|
#private $DbConnection;
|
||||||
|
private static $DbConnection = null;
|
||||||
/**
|
/**
|
||||||
* @return \LessQL\Database
|
* @return \LessQL\Database
|
||||||
*/
|
*/
|
||||||
public function GetDbConnection()
|
public function GetDbConnection()
|
||||||
{
|
{
|
||||||
if ($this->DbConnection == null)
|
if (self::$DbConnection == null)
|
||||||
|
#if ($this->DbConnection == null)
|
||||||
{
|
{
|
||||||
$this->DbConnection = new \LessQL\Database($this->GetDbConnectionRaw());
|
$fp = fopen('/www/data/sql.log', 'a');
|
||||||
|
fwrite($fp, "---creating new LessQL::Database object\n");
|
||||||
|
$time_start = microtime(true);
|
||||||
|
self::$DbConnection = new \LessQL\Database($this->GetDbConnectionRaw());
|
||||||
|
#$this->DbConnection = new \LessQL\Database($this->GetDbConnectionRaw());
|
||||||
|
fwrite($fp, "---Total execution time in seconds: " . round((microtime(true) - $time_start),6) . "\n");
|
||||||
|
fwrite($fp, "---object created\n");
|
||||||
|
fclose($fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->DbConnection;
|
return self::$DbConnection;
|
||||||
|
#return $this->DbConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ class LocalizationService
|
||||||
public function __construct(string $culture)
|
public function __construct(string $culture)
|
||||||
{
|
{
|
||||||
$this->Culture = $culture;
|
$this->Culture = $culture;
|
||||||
$this->DatabaseService = new DatabaseService();
|
$this->DatabaseService = DatabaseService::getInstance();
|
||||||
$this->Database = $this->DatabaseService->GetDbConnection();
|
$this->Database = $this->DatabaseService->GetDbConnection();
|
||||||
|
|
||||||
$this->LoadLocalizations($culture);
|
$this->LoadLocalizations($culture);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user