languages + close button + import fix + total rebuild
This commit is contained in:
parent
8de9a0b158
commit
5fa4abba5f
|
@ -35,11 +35,15 @@ final class catalog extends core
|
||||||
*/
|
*/
|
||||||
public function index(array $parameters = []): ?string
|
public function index(array $parameters = []): ?string
|
||||||
{
|
{
|
||||||
if (!empty($parameters['category'])) {
|
// Initializing identifier of a category
|
||||||
|
preg_match('/[\d]+/', $parameters['identifier'] ?? '', $matches);
|
||||||
|
$identifier = $matches[0] ?? null;
|
||||||
|
|
||||||
|
if (!empty($parameters['identifier'])) {
|
||||||
// Передана категория (идентификатор)
|
// Передана категория (идентификатор)
|
||||||
|
|
||||||
// Инициализация актуальной категории
|
// Инициализация актуальной категории
|
||||||
$category = new category(document: category::_read('d.identifier == @identifier', parameters: ['identifier' => $parameters['category']]));
|
$category = category::_read('d.identifier == @identifier', parameters: ['identifier' => (int) $identifier], errors: $this->errors['catalog']);
|
||||||
|
|
||||||
if ($category instanceof category) {
|
if ($category instanceof category) {
|
||||||
// Found the category
|
// Found the category
|
||||||
|
@ -84,7 +88,10 @@ final class catalog extends core
|
||||||
|
|
||||||
// Generating filters
|
// Generating filters
|
||||||
$this->view->filters = [
|
$this->view->filters = [
|
||||||
'brands' => product::collect('d.brand.ru', $this->errors['catalog'])
|
'brands' => product::collect(
|
||||||
|
'd.brand.' . $this->language->name,
|
||||||
|
errors: $this->errors['catalog']
|
||||||
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
@ -110,7 +117,7 @@ final class catalog extends core
|
||||||
'html' => [
|
'html' => [
|
||||||
'categories' => $this->view->render('catalog/elements/categories.html'),
|
'categories' => $this->view->render('catalog/elements/categories.html'),
|
||||||
'products' => $this->view->render('catalog/elements/products/2columns.html'),
|
'products' => $this->view->render('catalog/elements/products/2columns.html'),
|
||||||
'filters' => $this->view->render('catalog/elemments/filters.html')
|
'filters' => $this->view->render('catalog/elements/filters.html')
|
||||||
],
|
],
|
||||||
'errors' => $this->errors
|
'errors' => $this->errors
|
||||||
]
|
]
|
||||||
|
@ -151,6 +158,7 @@ final class catalog extends core
|
||||||
filter: 'd.deleted != true && d.hidden != true',
|
filter: 'd.deleted != true && d.hidden != true',
|
||||||
sort: 'd.position ASC, d.name ASC, d.created DESC',
|
sort: 'd.position ASC, d.name ASC, d.created DESC',
|
||||||
amount: 30,
|
amount: 30,
|
||||||
|
language: $this->language,
|
||||||
errors: $this->errors['catalog']
|
errors: $this->errors['catalog']
|
||||||
) : [];
|
) : [];
|
||||||
|
|
||||||
|
@ -167,7 +175,7 @@ final class catalog extends core
|
||||||
[
|
[
|
||||||
'title' => $title ?? '',
|
'title' => $title ?? '',
|
||||||
'html' => [
|
'html' => [
|
||||||
'products' => $this->view->render('catalog/elements/products.html')
|
'products' => $this->view->render('catalog/elements/products/2columns.html')
|
||||||
],
|
],
|
||||||
'errors' => $this->errors
|
'errors' => $this->errors
|
||||||
]
|
]
|
||||||
|
@ -195,53 +203,27 @@ final class catalog extends core
|
||||||
*/
|
*/
|
||||||
public function product(array $parameters = []): ?string
|
public function product(array $parameters = []): ?string
|
||||||
{
|
{
|
||||||
// Initializing of text fore search
|
// Initializing identifier of a product
|
||||||
preg_match('/[\d]+/', $parameters['id'] ?? '', $matches);
|
preg_match('/[\d]+/', $parameters['identifier'] ?? '', $matches);
|
||||||
$_key = $matches[0] ?? null;
|
$identifier = $matches[0] ?? null;
|
||||||
|
|
||||||
if (!empty($_key)) {
|
if (!empty($identifier)) {
|
||||||
// Received id of prouct (_key)
|
// Received identifier of the product
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
// POST request
|
// POST request
|
||||||
|
|
||||||
// Search for products
|
// Search for products
|
||||||
$product = product::read(
|
$product = product::read(
|
||||||
filter: "d._key == \"$_key\" && d.deleted != true && d.hidden != true",
|
filter: "d.identifier == @identifier && d.deleted != true && d.hidden != true",
|
||||||
sort: 'd.created DESC',
|
sort: 'd.created DESC',
|
||||||
amount: 1,
|
amount: 1,
|
||||||
return: '{id: d._key, title: d.title.ru, description: d.description.ru, cost: d.cost, weight: d.weight, dimensions: d.dimensions, brand: d.brand.ru, compatibility: d.compatibility.ru}',
|
return: '{identifier: d.identifier, name: d.name.@language, description: d.description.@language, cost: d.cost, weight: d.weight, dimensions: d.dimensions, brand: d.brand.@language, compatibility: d.compatibility.@language, images: d.images[*].storage}',
|
||||||
|
parameters: ['identifier' => (int) $identifier],
|
||||||
|
language: $this->language,
|
||||||
errors: $this->errors['catalog']
|
errors: $this->errors['catalog']
|
||||||
)[0]?->getAll();
|
)[0]?->getAll();
|
||||||
|
|
||||||
if (!empty($product)) {
|
|
||||||
// Found the product
|
|
||||||
|
|
||||||
// Initializing buffer of images
|
|
||||||
$images = [];
|
|
||||||
|
|
||||||
foreach (
|
|
||||||
glob(INDEX .
|
|
||||||
DIRECTORY_SEPARATOR .
|
|
||||||
'themes' .
|
|
||||||
DIRECTORY_SEPARATOR .
|
|
||||||
(THEME ?? 'default') .
|
|
||||||
DIRECTORY_SEPARATOR .
|
|
||||||
'images' .
|
|
||||||
DIRECTORY_SEPARATOR .
|
|
||||||
$_key .
|
|
||||||
DIRECTORY_SEPARATOR .
|
|
||||||
"*.{jpg,png,gif}", GLOB_BRACE) as $file
|
|
||||||
) {
|
|
||||||
// Iterate over images of the product
|
|
||||||
|
|
||||||
// Write to buffer of images
|
|
||||||
$images[] = "/images/$_key/" . basename($file);
|
|
||||||
}
|
|
||||||
|
|
||||||
$product = $product + ['images' => $images];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initializing a response headers
|
// Initializing a response headers
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
header('Content-Encoding: none');
|
header('Content-Encoding: none');
|
||||||
|
|
|
@ -10,7 +10,8 @@ use mirzaev\arming_bot\views\templater,
|
||||||
mirzaev\arming_bot\models\account,
|
mirzaev\arming_bot\models\account,
|
||||||
mirzaev\arming_bot\models\session,
|
mirzaev\arming_bot\models\session,
|
||||||
mirzaev\arming_bot\models\settings,
|
mirzaev\arming_bot\models\settings,
|
||||||
mirzaev\arming_bot\models\suspension;
|
mirzaev\arming_bot\models\suspension,
|
||||||
|
mirzaev\arming_bot\models\enumerations\language;
|
||||||
|
|
||||||
// Framework for PHP
|
// Framework for PHP
|
||||||
use mirzaev\minimal\controller;
|
use mirzaev\minimal\controller;
|
||||||
|
@ -31,7 +32,7 @@ class core extends controller
|
||||||
/**
|
/**
|
||||||
* Instance of the settings
|
* Instance of the settings
|
||||||
*/
|
*/
|
||||||
public static settings $settings;
|
protected readonly settings $settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instance of a session
|
* Instance of a session
|
||||||
|
@ -43,6 +44,11 @@ class core extends controller
|
||||||
*/
|
*/
|
||||||
protected readonly ?account $account;
|
protected readonly ?account $account;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Language
|
||||||
|
*/
|
||||||
|
protected language $language = language::en;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registry of errors
|
* Registry of errors
|
||||||
*/
|
*/
|
||||||
|
@ -57,6 +63,8 @@ class core extends controller
|
||||||
* @param bool $initialize Initialize a controller?
|
* @param bool $initialize Initialize a controller?
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
|
*
|
||||||
|
* @todo settings account и session не имеют проверок на возврат null
|
||||||
*/
|
*/
|
||||||
public function __construct(bool $initialize = true)
|
public function __construct(bool $initialize = true)
|
||||||
{
|
{
|
||||||
|
@ -70,7 +78,7 @@ class core extends controller
|
||||||
// Initializing is requested
|
// Initializing is requested
|
||||||
|
|
||||||
// Initializing of models core (connect to ArangoDB...)
|
// Initializing of models core (connect to ArangoDB...)
|
||||||
new models();
|
new models(true);
|
||||||
|
|
||||||
// Initializing of the date until which the session will be active
|
// Initializing of the date until which the session will be active
|
||||||
$expires = strtotime('+1 week');
|
$expires = strtotime('+1 week');
|
||||||
|
@ -106,10 +114,18 @@ class core extends controller
|
||||||
$this->account = $this->session->account($this->errors['account']);
|
$this->account = $this->session->account($this->errors['account']);
|
||||||
|
|
||||||
// Initializing of the settings
|
// Initializing of the settings
|
||||||
self::$settings = settings::active();
|
$this->settings = settings::active();
|
||||||
|
|
||||||
|
// Initializing of language
|
||||||
|
if ($this->account?->language) $this->language = $this->account->language ?? language::en;
|
||||||
|
else if ($this->settings?->language) $this->language = $this->settings->language ?? language::en;
|
||||||
|
|
||||||
// Initializing of preprocessor of views
|
// Initializing of preprocessor of views
|
||||||
$this->view = new templater($this->session, $this->account);
|
$this->view = new templater(
|
||||||
|
session: $this->session,
|
||||||
|
account: $this->account,
|
||||||
|
settings: $this->settings
|
||||||
|
);
|
||||||
|
|
||||||
// @todo перенести в middleware
|
// @todo перенести в middleware
|
||||||
|
|
||||||
|
@ -142,20 +158,20 @@ class core extends controller
|
||||||
suspension:
|
suspension:
|
||||||
|
|
||||||
// Write title of the page to templater global variables
|
// Write title of the page to templater global variables
|
||||||
$this->view->title = match ($account?->language ?? self::$settings?->language) {
|
$this->view->title = match ($this->language) {
|
||||||
'ru' => 'Приостановлено',
|
language::en => 'Suspended',
|
||||||
'en' => 'Suspended',
|
language::ru => 'Приостановлено',
|
||||||
default => 'Suspended'
|
default => 'Suspended'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Write description of the suspension to templater global variables
|
// Write description of the suspension to templater global variables
|
||||||
$this->view->description = $suspension->description[$account?->language ?? self::$settings?->language] ?? array_values($suspension->description)[0];
|
$this->view->description = $suspension->description[$this->language] ?? array_values($suspension->description)[0];
|
||||||
|
|
||||||
// Write message of remaining time of the suspension to templater global variables
|
// Write message of remaining time of the suspension to templater global variables
|
||||||
$this->view->remain = [
|
$this->view->remain = [
|
||||||
'title' => match ($account?->language ?? self::$settings?->language) {
|
'title' => match ($this->language) {
|
||||||
'ru' => 'Осталось времени: ',
|
language::en => 'Time remaining: ',
|
||||||
'en' => 'Time remaining: ',
|
language::ru => 'Осталось времени: ',
|
||||||
default => 'Time remaining: '
|
default => 'Time remaining: '
|
||||||
},
|
},
|
||||||
'value' => $suspension?->message()
|
'value' => $suspension?->message()
|
||||||
|
|
|
@ -8,7 +8,8 @@ namespace mirzaev\arming_bot\models;
|
||||||
use mirzaev\arming_bot\models\core,
|
use mirzaev\arming_bot\models\core,
|
||||||
mirzaev\arming_bot\models\traits\status,
|
mirzaev\arming_bot\models\traits\status,
|
||||||
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
|
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
|
||||||
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface;
|
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface,
|
||||||
|
mirzaev\arming_bot\models\enumerations\language;
|
||||||
|
|
||||||
// Framework for ArangoDB
|
// Framework for ArangoDB
|
||||||
use mirzaev\arangodb\collection,
|
use mirzaev\arangodb\collection,
|
||||||
|
@ -17,6 +18,9 @@ use mirzaev\arangodb\collection,
|
||||||
// Framework for Telegram
|
// Framework for Telegram
|
||||||
use Zanzara\Telegram\Type\User as telegram;
|
use Zanzara\Telegram\Type\User as telegram;
|
||||||
|
|
||||||
|
// Library for ArangoDB
|
||||||
|
use ArangoDBClient\Document as _document;
|
||||||
|
|
||||||
// Built-in libraries
|
// Built-in libraries
|
||||||
use exception;
|
use exception;
|
||||||
|
|
||||||
|
@ -52,10 +56,8 @@ final class account extends core implements arangodb_document_interface
|
||||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||||
// Initialized the collection
|
// Initialized the collection
|
||||||
|
|
||||||
try {
|
// Initializing the account
|
||||||
// Initializing the account and exit (success)
|
$result = collection::execute(
|
||||||
return new static(
|
|
||||||
document: collection::execute(
|
|
||||||
<<<'AQL'
|
<<<'AQL'
|
||||||
FOR d IN @@collection
|
FOR d IN @@collection
|
||||||
FILTER d.identifier == @identifier
|
FILTER d.identifier == @identifier
|
||||||
|
@ -66,14 +68,31 @@ final class account extends core implements arangodb_document_interface
|
||||||
'identifier' => $identifier
|
'identifier' => $identifier
|
||||||
],
|
],
|
||||||
errors: $errors
|
errors: $errors
|
||||||
)
|
|
||||||
);
|
);
|
||||||
} catch (exception $e) {
|
|
||||||
if ($registration) {
|
if ($result instanceof _document) {
|
||||||
|
// Initialized the account
|
||||||
|
|
||||||
|
// Initializing the object
|
||||||
|
$account = new account;
|
||||||
|
|
||||||
|
if (method_exists($account, '__document')) {
|
||||||
|
// Object can implement a document from ArangoDB
|
||||||
|
|
||||||
|
// Abstractioning of parameters
|
||||||
|
$result->language = language::{$result->language} ?? 'en';
|
||||||
|
|
||||||
|
// Writing the instance of account document from ArangoDB to the implement object
|
||||||
|
$account->__document($result);
|
||||||
|
|
||||||
|
// Exit (success)
|
||||||
|
return $account;
|
||||||
|
}
|
||||||
|
} else if ($registration) {
|
||||||
// Not found the account and registration is requested
|
// Not found the account and registration is requested
|
||||||
|
|
||||||
// Creating account
|
// Creating account
|
||||||
$account = document::write(
|
$_id = document::write(
|
||||||
static::COLLECTION,
|
static::COLLECTION,
|
||||||
(is_array($registration)
|
(is_array($registration)
|
||||||
? $registration :
|
? $registration :
|
||||||
|
@ -100,7 +119,7 @@ final class account extends core implements arangodb_document_interface
|
||||||
'messages' => $registration->getCanReadAllGroupMessages()
|
'messages' => $registration->getCanReadAllGroupMessages()
|
||||||
],
|
],
|
||||||
'premium' => $registration->isPremium(),
|
'premium' => $registration->isPremium(),
|
||||||
'language' => $registration->getLanguageCode(),
|
'language' => language::{$registration->getLanguageCode()}->name ?? 'en',
|
||||||
'queries' => [
|
'queries' => [
|
||||||
'inline' => $registration->getSupportsInlineQueries()
|
'inline' => $registration->getSupportsInlineQueries()
|
||||||
]
|
]
|
||||||
|
@ -111,14 +130,13 @@ final class account extends core implements arangodb_document_interface
|
||||||
errors: $errors
|
errors: $errors
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($account) {
|
if ($_id) {
|
||||||
// Created account
|
// Created account
|
||||||
|
|
||||||
// Initializing of the account (without registration request)
|
// Initializing of the account (without registration request)
|
||||||
return static::initialize($identifier, errors: $errors);
|
return static::initialize($identifier, errors: $errors);
|
||||||
} else throw new exception('Failed to register account');
|
} else throw new exception('Failed to register account');
|
||||||
} else throw new exception('Failed to find account');
|
} else throw new exception('Failed to find account');
|
||||||
}
|
|
||||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||||
} catch (exception $e) {
|
} catch (exception $e) {
|
||||||
// Writing to the registry of errors
|
// Writing to the registry of errors
|
||||||
|
|
|
@ -10,6 +10,7 @@ use mirzaev\arming_bot\models\core,
|
||||||
mirzaev\arming_bot\models\category,
|
mirzaev\arming_bot\models\category,
|
||||||
mirzaev\arming_bot\models\entry,
|
mirzaev\arming_bot\models\entry,
|
||||||
mirzaev\arming_bot\models\traits\files,
|
mirzaev\arming_bot\models\traits\files,
|
||||||
|
mirzaev\arming_bot\models\enumerations\language,
|
||||||
mirzaev\arming_bot\models\traits\yandex\disk as yandex;
|
mirzaev\arming_bot\models\traits\yandex\disk as yandex;
|
||||||
|
|
||||||
// Framework for ArangoDB
|
// Framework for ArangoDB
|
||||||
|
@ -52,7 +53,7 @@ final class catalog extends core
|
||||||
* @param int &$products_deleted Counter of deleted products
|
* @param int &$products_deleted Counter of deleted products
|
||||||
* @param int &$products_new Counter of new products
|
* @param int &$products_new Counter of new products
|
||||||
* @param int &$products_old Counter of old products
|
* @param int &$products_old Counter of old products
|
||||||
* @param string &$language Language (en, ru...)
|
* @param language $language Language
|
||||||
* @param array &$errors Registry of errors
|
* @param array &$errors Registry of errors
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
|
@ -75,7 +76,7 @@ final class catalog extends core
|
||||||
int &$products_deleted = 0,
|
int &$products_deleted = 0,
|
||||||
int &$products_old = 0,
|
int &$products_old = 0,
|
||||||
int &$products_new = 0,
|
int &$products_new = 0,
|
||||||
string $language = 'en',
|
language $language = language::en,
|
||||||
array &$errors = []
|
array &$errors = []
|
||||||
): void {
|
): void {
|
||||||
try {
|
try {
|
||||||
|
@ -112,7 +113,7 @@ final class catalog extends core
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!empty($row['identifier']) && !empty($row['name'])) {
|
if (!empty($row['identifier']) && !empty($row['name'])) {
|
||||||
// All required cells are filled in
|
// Required cells are filled in
|
||||||
|
|
||||||
// Incrementing the counter of loaded categories
|
// Incrementing the counter of loaded categories
|
||||||
++$categories_loaded;
|
++$categories_loaded;
|
||||||
|
@ -123,36 +124,40 @@ final class catalog extends core
|
||||||
// Declaring the variable with the category
|
// Declaring the variable with the category
|
||||||
$category = null;
|
$category = null;
|
||||||
|
|
||||||
try {
|
|
||||||
// Initializing the category
|
// Initializing the category
|
||||||
$category = new category(document: category::_read('d.identifier == @identifier', parameters: ['identifier' => (int) $row['identifier']], errors: $errors)[0] ?? null);
|
$category = category::_read('d.identifier == @identifier', parameters: ['identifier' => (int) $row['identifier']], errors: $errors);
|
||||||
|
|
||||||
|
if ($category instanceof category) {
|
||||||
|
// Initialized the category
|
||||||
|
|
||||||
// Initializing name of the category
|
// Initializing name of the category
|
||||||
$category->name[$language] === $row['name'] || $category->name = [[$language => $row['name']]] + $category->name ?? [];
|
if (empty($category->name) || empty($category->name[$language->name]) || $category->name[$language->name] !== $row['name'])
|
||||||
|
$category->name = [$language->name => $row['name']] + ($category->name ?? []);
|
||||||
|
|
||||||
// Initializing position of the category
|
// Initializing position of the category
|
||||||
$category->position === $row['position'] || $category->position = $row['position'];
|
if (empty($category->position) || $category->position === $row['position'])
|
||||||
} catch (exception $e) {
|
$category->position = $row['position'];
|
||||||
// Not found the category
|
} else {
|
||||||
|
// Not initialized the category
|
||||||
|
|
||||||
// Creating the category
|
// Creating the category
|
||||||
$_id = category::write((int) $row['identifier'], [$language => $row['name']], $row['position'] ?? null, $errors);
|
$_id = $created = category::write((int) $row['identifier'], [$language->name => $row['name']], $row['position'] ?? null, $errors);
|
||||||
|
|
||||||
// Initializing the category
|
// Initializing the category
|
||||||
$category = new category(document: $created = category::_read('d._id == @_id', parameters: ['_id' => $_id], errors: $errors)[0] ?? null);
|
$category = category::_read('d._id == @_id', parameters: ['_id' => $_id], errors: $errors);
|
||||||
|
|
||||||
// Incrementing the counter of created categories
|
// Incrementing the counter of created categories
|
||||||
if ($created) ++$categories_created;
|
if ($created) ++$categories_created;
|
||||||
};
|
};
|
||||||
|
|
||||||
if ($category instanceof category) {
|
if ($category instanceof category) {
|
||||||
// Found the category
|
// Initialized the category
|
||||||
|
|
||||||
if (!empty($row['category'])) {
|
if (!empty($row['category'])) {
|
||||||
// Received the ascendant category
|
// Received the ascendant category
|
||||||
|
|
||||||
// Initializing the ascendant category
|
// Initializing the ascendant category
|
||||||
$ascendant = new category(document: category::_read('d.identifier == @identifier', parameters: ['identifier' => (int) $row['category']], errors: $errors)[0] ?? null);
|
$ascendant = category::_read('d.identifier == @identifier', parameters: ['identifier' => (int) $row['category']], errors: $errors);
|
||||||
|
|
||||||
if ($ascendant instanceof category) {
|
if ($ascendant instanceof category) {
|
||||||
// Found the ascendant category
|
// Found the ascendant category
|
||||||
|
@ -265,7 +270,7 @@ final class catalog extends core
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!empty($row['identifier']) && !empty($row['name'])) {
|
if (!empty($row['identifier']) && !empty($row['name'])) {
|
||||||
// All required cells are filled in
|
// Required cells are filled in
|
||||||
|
|
||||||
// Incrementing the counter of loaded products
|
// Incrementing the counter of loaded products
|
||||||
++$products_loaded;
|
++$products_loaded;
|
||||||
|
@ -276,45 +281,63 @@ final class catalog extends core
|
||||||
// Declaring the variable with the product
|
// Declaring the variable with the product
|
||||||
$product = null;
|
$product = null;
|
||||||
|
|
||||||
try {
|
|
||||||
// Initializing the product
|
// Initializing the product
|
||||||
$product = new product(document: product::_read('d.identifier == %u', parameters: ['identifier' => (int) $row['identifier']], errors: $errors)[0] ?? null);
|
$product = product::_read('d.identifier == @identifier', parameters: ['identifier' => (int) $row['identifier']], errors: $errors);
|
||||||
|
|
||||||
// Initializing name of the category
|
if ($product instanceof product) {
|
||||||
$product->name[$language] === $row['name'] || $product->name = [[$language => $row['name']]] + $product->name ?? [];
|
// Initialized the product
|
||||||
|
|
||||||
|
// Initializing name of the product
|
||||||
|
if (empty($product->name) || empty($product->name[$language->name]) || $product->name[$language->name] !== $row['name'])
|
||||||
|
$product->name = [$language->name => $row['name']] + ($product->name ?? []);
|
||||||
|
|
||||||
|
// Initializing description of the product
|
||||||
|
if (empty($product->description) || empty($product->description[$language->name]) || $product->description[$language->name] !== $row['description'])
|
||||||
|
$product->description = [$language->name => $row['description']] + ($product->description ?? []);
|
||||||
|
|
||||||
|
// Initializing brand of the product
|
||||||
|
if (empty($product->brand) || empty($product->brand[$language->name]) || $product->brand[$language->name] !== $row['brand'])
|
||||||
|
$product->brand = [$language->name => $row['brand']] + ($product->brand ?? []);
|
||||||
|
|
||||||
|
// Initializing compatibility of the product
|
||||||
|
if (empty($product->compatibility) || empty($product->brand[$language->name]) || $product->compatibility[$language->name] !== $row['compatibility'])
|
||||||
|
$product->compatibility = [$language->name => $row['compatibility']] + ($product->compatibility ?? []);
|
||||||
|
|
||||||
// Initializing position of the product
|
// Initializing position of the product
|
||||||
$product->position === $row['position'] || $product->position = $row['position'];
|
if (empty($product->position) || $product->position !== $row['position'])
|
||||||
} catch (exception $e) {
|
$product->position = $row['position'];
|
||||||
// Not found the product
|
} else {
|
||||||
|
// Not initialized the product
|
||||||
|
|
||||||
// Creating the product
|
// Creating the product
|
||||||
$_id = product::write(
|
$_id = product::write(
|
||||||
(int) $row['identifier'],
|
(int) $row['identifier'],
|
||||||
[$language => $row['name']],
|
[$language->name => $row['name']],
|
||||||
[$language => $row['description']],
|
[$language->name => $row['description']],
|
||||||
(float) $row['cost'],
|
(float) $row['cost'],
|
||||||
(float) $row['weight'],
|
(float) $row['weight'],
|
||||||
['x' => $row['x'], 'y' => $row['y'], 'z' => $row['z']],
|
['x' => $row['x'], 'y' => $row['y'], 'z' => $row['z']],
|
||||||
|
[$language->name => $row['brand']],
|
||||||
|
[$language->name => $row['compatibility']],
|
||||||
$row['position'] ?? null,
|
$row['position'] ?? null,
|
||||||
errors: $errors
|
errors: $errors
|
||||||
);
|
);
|
||||||
|
|
||||||
// Initializing the product
|
// Initializing the product
|
||||||
$product = new product(document: $created = product::_read(sprintf('d._id == "%s"', $_id), errors: $errors)[0] ?? null);
|
$product = $created = product::_read('d._id == @_id', parameters: ['_id' => $_id], errors: $errors);
|
||||||
|
|
||||||
// Incrementing the counter of created products
|
// Incrementing the counter of created products
|
||||||
if ($created) ++$products_created;
|
if ($created) ++$products_created;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($product instanceof product) {
|
if ($product instanceof product) {
|
||||||
// Found the product
|
// Initialized the product
|
||||||
|
|
||||||
if (!empty($row['category'])) {
|
if (!empty($row['category'])) {
|
||||||
// Received the category
|
// Received the category
|
||||||
|
|
||||||
// Initializing the category
|
// Initializing the category
|
||||||
$category = new category(document: category::_read(sprintf('d.identifier == %u', (int) $row['category']), errors: $errors)[0] ?? null);
|
$category = category::_read(sprintf('d.identifier == %u', (int) $row['category']), errors: $errors);
|
||||||
|
|
||||||
if ($category instanceof category) {
|
if ($category instanceof category) {
|
||||||
// Found the ascendant category
|
// Found the ascendant category
|
||||||
|
@ -410,7 +433,7 @@ final class catalog extends core
|
||||||
sort: 'd.updated DESC',
|
sort: 'd.updated DESC',
|
||||||
amount: 100000,
|
amount: 100000,
|
||||||
errors: $errors
|
errors: $errors
|
||||||
) as $document
|
) ?? [] as $document
|
||||||
) {
|
) {
|
||||||
// Iterating over categories
|
// Iterating over categories
|
||||||
|
|
||||||
|
@ -444,7 +467,7 @@ final class catalog extends core
|
||||||
sort: 'd.updated DESC',
|
sort: 'd.updated DESC',
|
||||||
amount: 100000,
|
amount: 100000,
|
||||||
errors: $errors
|
errors: $errors
|
||||||
) as $document
|
) ?? [] as $document
|
||||||
) {
|
) {
|
||||||
// Iterating over products
|
// Iterating over products
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ use mirzaev\arangodb\connection as arangodb,
|
||||||
mirzaev\arangodb\collection,
|
mirzaev\arangodb\collection,
|
||||||
mirzaev\arangodb\enumerations\collection\type;
|
mirzaev\arangodb\enumerations\collection\type;
|
||||||
|
|
||||||
// Libraries for ArangoDB
|
// Library for ArangoDB
|
||||||
use ArangoDBClient\Document as _document;
|
use ArangoDBClient\Document as _document;
|
||||||
|
|
||||||
// Built-in libraries
|
// Built-in libraries
|
||||||
|
@ -40,6 +40,8 @@ class core extends model
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instance of the session of ArangoDB
|
* Instance of the session of ArangoDB
|
||||||
|
*
|
||||||
|
* @todo ПЕРЕДЕЛАТЬ В php 8.4
|
||||||
*/
|
*/
|
||||||
protected static arangodb $arangodb;
|
protected static arangodb $arangodb;
|
||||||
|
|
||||||
|
@ -61,7 +63,7 @@ class core extends model
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct(bool $initialize = true, ?arangodb $arangodb = null)
|
public function __construct(bool $initialize = false, ?arangodb $arangodb = null)
|
||||||
{
|
{
|
||||||
// For the extends system
|
// For the extends system
|
||||||
parent::__construct($initialize);
|
parent::__construct($initialize);
|
||||||
|
@ -69,17 +71,8 @@ class core extends model
|
||||||
if ($initialize) {
|
if ($initialize) {
|
||||||
// Initializing is requested
|
// Initializing is requested
|
||||||
|
|
||||||
if (isset($arangodb)) {
|
|
||||||
// Recieved an instance of a session of ArangoDB
|
|
||||||
|
|
||||||
// Writing an instance of a session of ArangoDB to the property
|
// Writing an instance of a session of ArangoDB to the property
|
||||||
$this->__set('arangodb', $arangodb);
|
self::$arangodb = $arangodb ?? new arangodb(require static::ARANGODB);
|
||||||
} else {
|
|
||||||
// Not recieved an instance of a session of ArangoDB
|
|
||||||
|
|
||||||
// Initializing of an instance of a session of ArangoDB
|
|
||||||
$this->__get('arangodb');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +97,7 @@ class core extends model
|
||||||
string $return = 'd',
|
string $return = 'd',
|
||||||
array $parameters = [],
|
array $parameters = [],
|
||||||
array &$errors = []
|
array &$errors = []
|
||||||
): _document|array|null {
|
): _document|static|array|null {
|
||||||
try {
|
try {
|
||||||
if (collection::initialize(static::COLLECTION, static::TYPE)) {
|
if (collection::initialize(static::COLLECTION, static::TYPE)) {
|
||||||
// Initialized the collection
|
// Initialized the collection
|
||||||
|
@ -117,22 +110,39 @@ class core extends model
|
||||||
%s
|
%s
|
||||||
%s
|
%s
|
||||||
LIMIT @offset, @amount
|
LIMIT @offset, @amount
|
||||||
RETURN @return
|
RETURN %s
|
||||||
AQL,
|
AQL,
|
||||||
empty($filter) ? '' : "FILTER $filter",
|
empty($filter) ? '' : "FILTER $filter",
|
||||||
empty($sort) ? '' : "SORT $sort",
|
empty($sort) ? '' : "SORT $sort",
|
||||||
|
empty($return) ? 'd' : $return
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
'@collection' => static::COLLECTION,
|
'@collection' => static::COLLECTION,
|
||||||
'offset' => --$page <= 0 ? 0 : $page * $amount,
|
'offset' => --$page <= 0 ? 0 : $page * $amount,
|
||||||
'amount' => $amount,
|
'amount' => $amount
|
||||||
'return' => $return
|
|
||||||
] + $parameters,
|
] + $parameters,
|
||||||
errors: $errors
|
errors: $errors
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ($result instanceof _document) {
|
||||||
|
// Received only 1 document and
|
||||||
|
|
||||||
|
// Initializing the object
|
||||||
|
$object = new static;
|
||||||
|
|
||||||
|
if (method_exists($object, '__document')) {
|
||||||
|
// Object can implement a document from ArangoDB
|
||||||
|
|
||||||
|
// Writing the instance of document from ArangoDB to the implement object
|
||||||
|
$object->__document($result);
|
||||||
|
|
||||||
// Exit (success)
|
// Exit (success)
|
||||||
return is_array($result) ? $result : [$result];
|
return $object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit (success)
|
||||||
|
return $result;
|
||||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||||
} catch (exception $e) {
|
} catch (exception $e) {
|
||||||
// Writing to registry of errors
|
// Writing to registry of errors
|
||||||
|
@ -160,26 +170,9 @@ class core extends model
|
||||||
{
|
{
|
||||||
match ($name) {
|
match ($name) {
|
||||||
'arangodb' => (function () use ($value) {
|
'arangodb' => (function () use ($value) {
|
||||||
if ($this->__isset('arangodb')) {
|
if (isset(static::$arangodb)) throw new exception('Forbidden to reinitialize the session of ArangoDB ($this::$arangodb)', 500);
|
||||||
// Is alredy initialized
|
else if ($value instanceof arangodb) self::$arangodb = $value;
|
||||||
|
else throw new exception('Session of connection to ArangoDB ($this::$arangodb) is need to be mirzaev\arangodb\connection', 500);
|
||||||
// Exit (fail)
|
|
||||||
throw new exception('Forbidden to reinitialize the session of ArangoDB ($this::$arangodb)', 500);
|
|
||||||
} else {
|
|
||||||
// Is not already initialized
|
|
||||||
|
|
||||||
if ($value instanceof arangodb) {
|
|
||||||
// Recieved an appropriate value
|
|
||||||
|
|
||||||
// Writing the property and exit (success)
|
|
||||||
self::$arangodb = $value;
|
|
||||||
} else {
|
|
||||||
// Recieved an inappropriate value
|
|
||||||
|
|
||||||
// Exit (fail)
|
|
||||||
throw new exception('Session of ArangoDB ($this::$arangodb) is need to be mirzaev\arangodb\connection', 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})(),
|
})(),
|
||||||
default => parent::__set($name, $value)
|
default => parent::__set($name, $value)
|
||||||
};
|
};
|
||||||
|
@ -195,22 +188,6 @@ class core extends model
|
||||||
public function __get(string $name): mixed
|
public function __get(string $name): mixed
|
||||||
{
|
{
|
||||||
return match ($name) {
|
return match ($name) {
|
||||||
'arangodb' => (function () {
|
|
||||||
try {
|
|
||||||
if (!$this->__isset('arangodb')) {
|
|
||||||
// Is not initialized
|
|
||||||
|
|
||||||
// Initializing of a default value from settings
|
|
||||||
$this->__set('arangodb', new arangodb(require static::ARANGODB));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit (success)
|
|
||||||
return self::$arangodb;
|
|
||||||
} catch (exception) {
|
|
||||||
// Exit (fail)
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})(),
|
|
||||||
default => parent::__get($name)
|
default => parent::__get($name)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -227,7 +227,8 @@ final class entry extends core implements arangodb_document_interface
|
||||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||||
// Initialized collections
|
// Initialized collections
|
||||||
|
|
||||||
if ($documents = collection::execute(
|
// Execute and exit (success)
|
||||||
|
return is_array($result = collection::execute(
|
||||||
sprintf(
|
sprintf(
|
||||||
<<<'AQL'
|
<<<'AQL'
|
||||||
FOR v IN 1..1 INBOUND @document GRAPH @graph
|
FOR v IN 1..1 INBOUND @document GRAPH @graph
|
||||||
|
@ -241,18 +242,13 @@ final class entry extends core implements arangodb_document_interface
|
||||||
empty($sort) ? '' : "SORT $sort",
|
empty($sort) ? '' : "SORT $sort",
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
'grapth' => 'catalog',
|
'graph' => 'catalog',
|
||||||
'document' => $document->getId(),
|
'document' => $document->getId(),
|
||||||
'offset' => --$page <= 0 ? $page = 0 : $page * $amount,
|
'offset' => --$page <= 0 ? $page = 0 : $page * $amount,
|
||||||
'amount' => $amount
|
'amount' => $amount
|
||||||
],
|
],
|
||||||
errors: $errors
|
errors: $errors
|
||||||
)) {
|
)) ? $result : [$result];
|
||||||
// Fount entries
|
|
||||||
|
|
||||||
// Возврат (успех)
|
|
||||||
return is_array($documents) ? $documents : [$documents];
|
|
||||||
} else return [];
|
|
||||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||||
} else throw new exception('Failed to initialize ' . $document::TYPE . ' collection: ' . $document::COLLECTION);
|
} else throw new exception('Failed to initialize ' . $document::TYPE . ' collection: ' . $document::COLLECTION);
|
||||||
} catch (exception $e) {
|
} catch (exception $e) {
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace mirzaev\arming_bot\models\enumerations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types of human languages
|
||||||
|
*
|
||||||
|
* @package mirzaev\arming_bot\models\enumerations
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
|
enum language
|
||||||
|
{
|
||||||
|
case en;
|
||||||
|
case ru;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate label of language
|
||||||
|
*
|
||||||
|
* @param language|null $language Language into which to translate
|
||||||
|
*
|
||||||
|
* @return string Translated label of language
|
||||||
|
*
|
||||||
|
* @todo
|
||||||
|
* 1. More languages
|
||||||
|
* 2. Cases???
|
||||||
|
*/
|
||||||
|
public function translate(?language $language = language::en): string
|
||||||
|
{
|
||||||
|
// Exit (success)
|
||||||
|
return match ($this) {
|
||||||
|
language::en => match ($language) {
|
||||||
|
language::en => 'English',
|
||||||
|
language::ru => 'Английский'
|
||||||
|
},
|
||||||
|
language::ru => match ($language) {
|
||||||
|
language::en => 'Russian',
|
||||||
|
language::ru => 'Русский'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,14 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace mirzaev\arming_bot\models\enumerations;
|
namespace mirzaev\arming_bot\models\enumerations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types of session verification
|
||||||
|
*
|
||||||
|
* @package mirzaev\arming_bot\models\enumerations
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
enum session
|
enum session
|
||||||
{
|
{
|
||||||
case hash_only;
|
case hash_only;
|
||||||
|
|
|
@ -7,9 +7,6 @@ namespace mirzaev\arming_bot\models\interfaces;
|
||||||
// Library для ArangoDB
|
// Library для ArangoDB
|
||||||
use ArangoDBClient\Document as _document;
|
use ArangoDBClient\Document as _document;
|
||||||
|
|
||||||
// Framework for ArangoDB
|
|
||||||
use mirzaev\arangodb\connection as arangodb;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for implementing a document instance from ArangoDB
|
* Interface for implementing a document instance from ArangoDB
|
||||||
*
|
*
|
||||||
|
|
|
@ -6,6 +6,7 @@ namespace mirzaev\arming_bot\models;
|
||||||
|
|
||||||
// Files of the project
|
// Files of the project
|
||||||
use mirzaev\arming_bot\models\core,
|
use mirzaev\arming_bot\models\core,
|
||||||
|
mirzaev\arming_bot\models\enumerations\language,
|
||||||
mirzaev\arming_bot\models\traits\document as arangodb_document_trait;
|
mirzaev\arming_bot\models\traits\document as arangodb_document_trait;
|
||||||
|
|
||||||
// Framework for ArangoDB
|
// Framework for ArangoDB
|
||||||
|
@ -41,6 +42,8 @@ final class product extends core
|
||||||
* @param float $cost Cost
|
* @param float $cost Cost
|
||||||
* @param float $weight Weight
|
* @param float $weight Weight
|
||||||
* @param array $dimensions Dimensions ['x' => 0.0, 'y' => 0.0, 'z' => 0.0]
|
* @param array $dimensions Dimensions ['x' => 0.0, 'y' => 0.0, 'z' => 0.0]
|
||||||
|
* @param array|null $brand Brand [['en' => value], ['ru' => значение]]
|
||||||
|
* @param array|null $compatibility Compatibility [['en' => value], ['ru' => значение]]
|
||||||
* @param array $images Images (first will be thumbnail)
|
* @param array $images Images (first will be thumbnail)
|
||||||
* @param int|null $position Position for sorting in the catalog (ASC)
|
* @param int|null $position Position for sorting in the catalog (ASC)
|
||||||
* @param array $data Data
|
* @param array $data Data
|
||||||
|
@ -58,6 +61,8 @@ final class product extends core
|
||||||
float $cost = 0,
|
float $cost = 0,
|
||||||
float $weight = 0,
|
float $weight = 0,
|
||||||
array $dimensions = ['x' => 0, 'y' => 0, 'z' => 0],
|
array $dimensions = ['x' => 0, 'y' => 0, 'z' => 0],
|
||||||
|
?array $brand = [['en' => 'ERROR']],
|
||||||
|
?array $compatibility = [['en' => 'ERROR']],
|
||||||
array $images = [],
|
array $images = [],
|
||||||
?int $position = null,
|
?int $position = null,
|
||||||
array $data = [],
|
array $data = [],
|
||||||
|
@ -81,6 +86,8 @@ final class product extends core
|
||||||
'y' => $dimensions['y'] ?? 0,
|
'y' => $dimensions['y'] ?? 0,
|
||||||
'z' => $dimensions['z'] ?? 0,
|
'z' => $dimensions['z'] ?? 0,
|
||||||
],
|
],
|
||||||
|
'brand' => $brand,
|
||||||
|
'compatibility' => $compatibility,
|
||||||
'images' => $images,
|
'images' => $images,
|
||||||
'position' => $position,
|
'position' => $position,
|
||||||
'version' => ROBOT_VERSION
|
'version' => ROBOT_VERSION
|
||||||
|
@ -111,7 +118,8 @@ final class product extends core
|
||||||
* @param int $page Page
|
* @param int $page Page
|
||||||
* @param int $amount Amount per page
|
* @param int $amount Amount per page
|
||||||
* @param string|null $return Return (AQL)
|
* @param string|null $return Return (AQL)
|
||||||
* @param string $language Language code (en, ru...)
|
* @param language $language Language code (en, ru...)
|
||||||
|
* @param array $parameters Binded parameters for placeholders ['placeholder' => parameter]
|
||||||
* @param array &$errors Registry of errors
|
* @param array &$errors Registry of errors
|
||||||
*
|
*
|
||||||
* @return array Массив с найденными товарами (может быть пустым)
|
* @return array Массив с найденными товарами (может быть пустым)
|
||||||
|
@ -123,23 +131,31 @@ final class product extends core
|
||||||
int $page = 1,
|
int $page = 1,
|
||||||
int $amount = 100,
|
int $amount = 100,
|
||||||
?string $return = 'd',
|
?string $return = 'd',
|
||||||
string $language = 'en',
|
language $language = language::en,
|
||||||
|
array $parameters = [],
|
||||||
array &$errors = []
|
array &$errors = []
|
||||||
): array {
|
): array {
|
||||||
try {
|
try {
|
||||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||||
// Initialized the collection
|
// Initialized the collection
|
||||||
|
|
||||||
// Initializing the query
|
// Initializing parameters for search
|
||||||
$aql = <<<'AQL'
|
if ($search) $parameters += [
|
||||||
FOR d IN @@collection
|
'search' => $search,
|
||||||
AQL;
|
'analyzer' => 'text_' . $language->name
|
||||||
|
];
|
||||||
|
|
||||||
if ($search) {
|
// Reading products
|
||||||
// Requested search
|
$documents = collection::execute(
|
||||||
|
sprintf(
|
||||||
// Writing to the query
|
<<<'AQL'
|
||||||
$aql .= <<<'AQL'
|
FOR d IN @@collection %s
|
||||||
|
%s
|
||||||
|
%s
|
||||||
|
LIMIT @offset, @amount
|
||||||
|
RETURN %s
|
||||||
|
AQL,
|
||||||
|
empty($search) ? '' : <<<'AQL'
|
||||||
SEARCH
|
SEARCH
|
||||||
LEVENSHTEIN_MATCH(
|
LEVENSHTEIN_MATCH(
|
||||||
d.name.@language,
|
d.name.@language,
|
||||||
|
@ -159,34 +175,17 @@ final class product extends core
|
||||||
1,
|
1,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
AQL;
|
|
||||||
|
|
||||||
// Adding sorting
|
|
||||||
if ($sort) $sort = "BM25(d) DESC, $sort";
|
|
||||||
else $sort = "BM25(d) DESC";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reading products
|
|
||||||
$documents = collection::execute(
|
|
||||||
sprintf(
|
|
||||||
$aql . <<<'AQL'
|
|
||||||
%s
|
|
||||||
%s
|
|
||||||
LIMIT @offset, @amount
|
|
||||||
RETURN $s
|
|
||||||
AQL,
|
AQL,
|
||||||
empty($filter) ? '' : "FILTER $filter",
|
empty($filter) ? '' : "FILTER $filter",
|
||||||
empty($sort) ? '' : "SORT $sort",
|
empty($search) ? (empty($sort) ? '' : "SORT $sort") : (empty($sort) ? "SORT BM25(d) DESC" : "SORT BM25(d) DESC, $sort"),
|
||||||
empty($return) ? 'd' : $return
|
empty($return) ? 'd' : $return
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
'@collection' => $search ? static::COLLECTION . 's_search' : static::COLLECTION,
|
'@collection' => empty($search) ? static::COLLECTION : static::COLLECTION . 's_search',
|
||||||
'search' => $search,
|
'language' => $language->name,
|
||||||
'language' => $language,
|
|
||||||
'analyzer' => "text_$language",
|
|
||||||
'offset' => --$page <= 0 ? $page = 0 : $page * $amount,
|
'offset' => --$page <= 0 ? $page = 0 : $page * $amount,
|
||||||
'amount' => $amount,
|
'amount' => $amount,
|
||||||
],
|
] + $parameters,
|
||||||
errors: $errors
|
errors: $errors
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -196,7 +195,7 @@ final class product extends core
|
||||||
// Exit (success)
|
// Exit (success)
|
||||||
return is_array($documents) ? $documents : [$documents];
|
return is_array($documents) ? $documents : [$documents];
|
||||||
} else return [];
|
} else return [];
|
||||||
} else throw new exception('Failed to initialize ' . static::COLLECTION . ' collection: ' . static::COLLECTION);
|
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||||
} catch (exception $e) {
|
} catch (exception $e) {
|
||||||
// Writing to the registry of errors
|
// Writing to the registry of errors
|
||||||
$errors[] = [
|
$errors[] = [
|
||||||
|
@ -227,9 +226,9 @@ final class product extends core
|
||||||
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
|
||||||
// Initialized the collection
|
// Initialized the collection
|
||||||
|
|
||||||
if ($values = collection::execute(
|
if ($result = collection::execute(
|
||||||
<<<'AQL'
|
<<<'AQL'
|
||||||
FOR d IN @@collecton
|
FOR d IN @@collection
|
||||||
RETURN DISTINCT @parameter
|
RETURN DISTINCT @parameter
|
||||||
AQL,
|
AQL,
|
||||||
[
|
[
|
||||||
|
@ -241,7 +240,7 @@ final class product extends core
|
||||||
// Found parameters
|
// Found parameters
|
||||||
|
|
||||||
// Exit (success)
|
// Exit (success)
|
||||||
return $values;
|
return is_array($result) ? $result : [$result];
|
||||||
} else return [];
|
} else return [];
|
||||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||||
} catch (exception $e) {
|
} catch (exception $e) {
|
||||||
|
|
|
@ -141,7 +141,7 @@ final class session extends core implements arangodb_document_interface
|
||||||
// Initialized collections
|
// Initialized collections
|
||||||
|
|
||||||
// Search for connected account
|
// Search for connected account
|
||||||
$document = collection::execute(
|
$result = collection::execute(
|
||||||
<<<AQL
|
<<<AQL
|
||||||
FOR v IN INBOUND @session GRAPH sessions
|
FOR v IN INBOUND @session GRAPH sessions
|
||||||
SORT v.created DESC
|
SORT v.created DESC
|
||||||
|
@ -154,17 +154,21 @@ final class session extends core implements arangodb_document_interface
|
||||||
errors: $errors
|
errors: $errors
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($document instanceof _document) {
|
if ($result instanceof _document) {
|
||||||
// Found connected account
|
// Found active settings
|
||||||
|
|
||||||
// Initializing the implement object of the instance of sesson document from ArangoDB
|
// Initializing the object
|
||||||
$account = new account;
|
$account = new static;
|
||||||
|
|
||||||
// Writing the instance of session document from ArangoDB to the implement object
|
if (method_exists($account, '__document')) {
|
||||||
$account->__document($document);
|
// Object can implement a document from ArangoDB
|
||||||
|
|
||||||
|
// Writing the instance of account document from ArangoDB to the implement object
|
||||||
|
$account->__document($result);
|
||||||
|
|
||||||
// Exit (success)
|
// Exit (success)
|
||||||
return $account;
|
return $account;
|
||||||
|
}
|
||||||
} else return null;
|
} else return null;
|
||||||
} else throw new exception('Failed to initialize ' . account::TYPE . ' collection: ' . account::COLLECTION);
|
} else throw new exception('Failed to initialize ' . account::TYPE . ' collection: ' . account::COLLECTION);
|
||||||
} else throw new exception('Failed to initialize ' . connect::TYPE . ' collection: ' . connect::COLLECTION);
|
} else throw new exception('Failed to initialize ' . connect::TYPE . ' collection: ' . connect::COLLECTION);
|
||||||
|
|
|
@ -7,7 +7,8 @@ namespace mirzaev\arming_bot\models;
|
||||||
// Files of the project
|
// Files of the project
|
||||||
use mirzaev\arming_bot\models\core,
|
use mirzaev\arming_bot\models\core,
|
||||||
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
|
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
|
||||||
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface;
|
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface,
|
||||||
|
mirzaev\arming_bot\models\enumerations\language;
|
||||||
|
|
||||||
// Framework for ArangoDB
|
// Framework for ArangoDB
|
||||||
use mirzaev\arangodb\collection,
|
use mirzaev\arangodb\collection,
|
||||||
|
@ -51,7 +52,7 @@ final class settings extends core implements arangodb_document_interface
|
||||||
// Initialized the collection
|
// Initialized the collection
|
||||||
|
|
||||||
// Search for active settings
|
// Search for active settings
|
||||||
$document = collection::execute(
|
$result = collection::execute(
|
||||||
<<<'AQL'
|
<<<'AQL'
|
||||||
FOR d IN @@collection
|
FOR d IN @@collection
|
||||||
FILTER d.status == 'active'
|
FILTER d.status == 'active'
|
||||||
|
@ -65,17 +66,24 @@ final class settings extends core implements arangodb_document_interface
|
||||||
errors: $errors
|
errors: $errors
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($document instanceof _document) {
|
if ($result instanceof _document) {
|
||||||
// Found active settings
|
// Found active settings
|
||||||
|
|
||||||
// Initializing the implement object of the instance of settings document from ArangoDB
|
// Initializing the object
|
||||||
$settings = new static;
|
$settings = new static;
|
||||||
|
|
||||||
|
if (method_exists($settings, '__document')) {
|
||||||
|
// Object can implement a document from ArangoDB
|
||||||
|
|
||||||
|
// Abstractioning of parameters
|
||||||
|
$result->language = language::{$result->language} ?? 'en';
|
||||||
|
|
||||||
// Writing the instance of settings document from ArangoDB to the implement object
|
// Writing the instance of settings document from ArangoDB to the implement object
|
||||||
$settings->__document($document);
|
$settings->__document($result);
|
||||||
|
|
||||||
// Exit (success)
|
// Exit (success)
|
||||||
return $settings;
|
return $settings;
|
||||||
|
}
|
||||||
} else if ($create) {
|
} else if ($create) {
|
||||||
// Not found active settings and requested their creating
|
// Not found active settings and requested their creating
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,7 @@ namespace mirzaev\arming_bot\models;
|
||||||
|
|
||||||
// Files of the project
|
// Files of the project
|
||||||
use mirzaev\arming_bot\models\core,
|
use mirzaev\arming_bot\models\core,
|
||||||
mirzaev\arming_bot\controllers\core as controller,
|
mirzaev\arming_bot\models\enumerations\language,
|
||||||
mirzaev\arming_bot\models\settings,
|
|
||||||
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
|
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
|
||||||
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface;
|
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface;
|
||||||
|
|
||||||
|
@ -53,7 +52,7 @@ final class suspension extends core implements arangodb_document_interface
|
||||||
// Initialized the collection
|
// Initialized the collection
|
||||||
|
|
||||||
// Search for active suspension
|
// Search for active suspension
|
||||||
$document = collection::execute(
|
$result = collection::execute(
|
||||||
<<<'AQL'
|
<<<'AQL'
|
||||||
FOR d IN @@collection
|
FOR d IN @@collection
|
||||||
FILTER d.end > @time
|
FILTER d.end > @time
|
||||||
|
@ -68,17 +67,21 @@ final class suspension extends core implements arangodb_document_interface
|
||||||
errors: $errors
|
errors: $errors
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($document instanceof _document) {
|
if ($result instanceof _document) {
|
||||||
// Found active suspension
|
// Found active settings
|
||||||
|
|
||||||
// Initializing the implement object of the instance of suspension document from ArangoDB
|
// Initializing the object
|
||||||
$suspension = new static;
|
$suspension = new static;
|
||||||
|
|
||||||
|
if (method_exists($suspension, '__document')) {
|
||||||
|
// Object can implement a document from ArangoDB
|
||||||
|
|
||||||
// Writing the instance of suspension document from ArangoDB to the implement object
|
// Writing the instance of suspension document from ArangoDB to the implement object
|
||||||
$suspension->__document($document);
|
$suspension->__document($result);
|
||||||
|
|
||||||
// Exit (success)
|
// Exit (success)
|
||||||
return $suspension;
|
return $suspension;
|
||||||
|
}
|
||||||
} else return null;
|
} else return null;
|
||||||
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
|
||||||
} catch (exception $e) {
|
} catch (exception $e) {
|
||||||
|
@ -98,17 +101,14 @@ final class suspension extends core implements arangodb_document_interface
|
||||||
/**
|
/**
|
||||||
* Generate message about remaining time
|
* Generate message about remaining time
|
||||||
*
|
*
|
||||||
* @param string|null $language Language of the generated text (otherwise used from settings.language)
|
* @param language|null $language Language of the generated text (otherwise used from settings.language)
|
||||||
* @param array &$errors Registry of errors
|
* @param array &$errors Registry of errors
|
||||||
*
|
*
|
||||||
* @return string|null Text: "? days, ? hours and ? minutes"
|
* @return string|null Text: "? days, ? hours and ? minutes"
|
||||||
*/
|
*/
|
||||||
public function message(?string $language = null, array &$errors = []): ?string
|
public function message(?language $language = language::en, array &$errors = []): ?string
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
// Initializing default value
|
|
||||||
$language ??= controller::$settings?->language ?? 'en';
|
|
||||||
|
|
||||||
// Initializing the time until the suspension ends
|
// Initializing the time until the suspension ends
|
||||||
$difference = date_diff(new datetime('@' . $this->document->end), new datetime());
|
$difference = date_diff(new datetime('@' . $this->document->end), new datetime());
|
||||||
|
|
||||||
|
@ -118,54 +118,54 @@ final class suspension extends core implements arangodb_document_interface
|
||||||
$difference->d,
|
$difference->d,
|
||||||
match ($difference->d > 20 ? $difference->d % 10 : $difference->d % 100) {
|
match ($difference->d > 20 ? $difference->d % 10 : $difference->d % 100) {
|
||||||
1 => match ($language) {
|
1 => match ($language) {
|
||||||
'ru' => 'день',
|
language::en => 'day',
|
||||||
'en' => 'day',
|
language::ru => 'день',
|
||||||
default => 'day'
|
default => 'day'
|
||||||
},
|
},
|
||||||
2, 3, 4 => match ($language) {
|
2, 3, 4 => match ($language) {
|
||||||
'ru' => 'дня',
|
language::en => 'days',
|
||||||
'en' => 'days',
|
language::ru => 'дня',
|
||||||
default => 'days'
|
default => 'days'
|
||||||
},
|
},
|
||||||
default => match ($language) {
|
default => match ($language) {
|
||||||
'ru' => 'дней',
|
language::en => 'days',
|
||||||
'en' => 'days',
|
language::ru => 'дней',
|
||||||
default => 'days'
|
default => 'days'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
$difference->h,
|
$difference->h,
|
||||||
match ($difference->h > 20 ? $difference->h % 10 : $difference->h % 100) {
|
match ($difference->h > 20 ? $difference->h % 10 : $difference->h % 100) {
|
||||||
1 => match ($language) {
|
1 => match ($language) {
|
||||||
'ru' => 'час',
|
language::en => 'hours',
|
||||||
'en' => 'hours',
|
language::ru => 'час',
|
||||||
default => 'hour'
|
default => 'hour'
|
||||||
},
|
},
|
||||||
2, 3, 4 => match ($language) {
|
2, 3, 4 => match ($language) {
|
||||||
'ru' => 'часа',
|
language::en => 'hours',
|
||||||
'en' => 'hours',
|
language::ru => 'часа',
|
||||||
default => 'hours'
|
default => 'hours'
|
||||||
},
|
},
|
||||||
default => match ($language) {
|
default => match ($language) {
|
||||||
'ru' => 'часов',
|
language::en => 'hours',
|
||||||
'en' => 'hours',
|
language::ru => 'часов',
|
||||||
default => 'hours'
|
default => 'hours'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
$difference->i,
|
$difference->i,
|
||||||
match ($difference->i > 20 ? $difference->i % 10 : $difference->i % 100) {
|
match ($difference->i > 20 ? $difference->i % 10 : $difference->i % 100) {
|
||||||
1 => match ($language) {
|
1 => match ($language) {
|
||||||
'ru' => 'минута',
|
language::en => 'minute',
|
||||||
'en' => 'minute',
|
language::ru => 'минута',
|
||||||
default => 'minute'
|
default => 'minute'
|
||||||
},
|
},
|
||||||
2, 3, 4 => match ($language) {
|
2, 3, 4 => match ($language) {
|
||||||
'ru' => 'минуты',
|
language::en => 'minutes',
|
||||||
'en' => 'minutes',
|
language::ru => 'минуты',
|
||||||
default => 'minutes'
|
default => 'minutes'
|
||||||
},
|
},
|
||||||
default => match ($language) {
|
default => match ($language) {
|
||||||
'ru' => 'минут',
|
language::en => 'minutes',
|
||||||
'en' => 'minutes',
|
language::ru => 'минут',
|
||||||
default => 'minutes'
|
default => 'minutes'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -410,7 +410,7 @@ final class telegram extends core
|
||||||
$products_deleted,
|
$products_deleted,
|
||||||
$products_old,
|
$products_old,
|
||||||
$products_new,
|
$products_new,
|
||||||
language: 'ru'
|
language: $account->language ?? settings::active()?->language ?? 'en'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Отправка сообщения
|
// Отправка сообщения
|
||||||
|
@ -489,7 +489,7 @@ final class telegram extends core
|
||||||
$telegram = $ctx->getEffectiveUser();
|
$telegram = $ctx->getEffectiveUser();
|
||||||
|
|
||||||
// Инициализация аккаунта
|
// Инициализация аккаунта
|
||||||
$account = account::initialization($telegram->getId(), $telegram);
|
$account = account::initialize($telegram->getId(), $telegram);
|
||||||
|
|
||||||
if ($account) {
|
if ($account) {
|
||||||
// Инициализирован аккаунт
|
// Инициализирован аккаунт
|
||||||
|
@ -561,11 +561,11 @@ final class telegram extends core
|
||||||
}
|
}
|
||||||
|
|
||||||
// Инициализация сообщения
|
// Инициализация сообщения
|
||||||
$message = "⚠️ *Работа приостановлена*\n*Оставшееся время\:* " . $suspension->message($account->language ?? controller::$settings?->language);
|
$message = "⚠️ *Работа приостановлена*\n*Оставшееся время\:* " . $suspension->message($account->language ?? settings::active()?->language ?? 'en');
|
||||||
|
|
||||||
// Добавление описания причины приостановки, если найдена
|
// Добавление описания причины приостановки, если найдена
|
||||||
if (!empty($suspension->description))
|
if (!empty($suspension->description))
|
||||||
$message .= "\n\n" . $suspension->description[$account->language ?? controller::$settings?->language] ?? array_values($suspension->description)[0];
|
$message .= "\n\n" . $suspension->description[$account->language ?? settings::active()?->language ?? 'en'] ?? array_values($suspension->description)[0];
|
||||||
|
|
||||||
// Отправка сообщения
|
// Отправка сообщения
|
||||||
$ctx->sendMessage($message)
|
$ctx->sendMessage($message)
|
||||||
|
|
|
@ -49,7 +49,7 @@ trait document
|
||||||
parent::__construct($initialize, $arangodb);
|
parent::__construct($initialize, $arangodb);
|
||||||
|
|
||||||
// Writing to the property
|
// Writing to the property
|
||||||
if ($document instanceof _document) $this->document = $document;
|
if ($document instanceof _document) $this->__document($document);
|
||||||
else if ($document === null) throw new exception('Failed to initialize an instance of the document from ArangoDB');
|
else if ($document === null) throw new exception('Failed to initialize an instance of the document from ArangoDB');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,6 @@ trait document
|
||||||
{
|
{
|
||||||
// Read a property from an instance of the ArangoDB document and exit (success)
|
// Read a property from an instance of the ArangoDB document and exit (success)
|
||||||
return match ($name) {
|
return match ($name) {
|
||||||
'arangodb' => core::$arangodb,
|
|
||||||
default => $this->document->{$name}
|
default => $this->document->{$name}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,8 +41,9 @@ $router
|
||||||
->write('/', 'catalog', 'index', 'GET')
|
->write('/', 'catalog', 'index', 'GET')
|
||||||
->write('/search', 'catalog', 'search', 'POST')
|
->write('/search', 'catalog', 'search', 'POST')
|
||||||
->write('/session/connect/telegram', 'session', 'telegram', 'POST')
|
->write('/session/connect/telegram', 'session', 'telegram', 'POST')
|
||||||
->write('/product/$id', 'catalog', 'product', 'POST')
|
->write('/category/$identifier', 'catalog', 'index', 'POST')
|
||||||
->write('/$categories...', 'catalog', 'index', 'POST');
|
->write('/category', 'catalog', 'index', 'POST')
|
||||||
|
->write('/product/$identifier', 'catalog', 'product', 'POST');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
|
|
@ -34,16 +34,11 @@ import("/js/core.js").then(() =>
|
||||||
|
|
||||||
// Write to the core
|
// Write to the core
|
||||||
core.catalog = class catalog {
|
core.catalog = class catalog {
|
||||||
/**
|
|
||||||
* Current position in hierarchy of the categories
|
|
||||||
*/
|
|
||||||
static categories = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registry of filters (instead of cookies)
|
* Registry of filters (instead of cookies)
|
||||||
*/
|
*/
|
||||||
static filters = new Map([
|
static filters = new Map([
|
||||||
['brand', null]
|
["brand", null],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,10 +51,12 @@ import("/js/core.js").then(() =>
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
static category(button, clean = true, force = false) {
|
static category(button, clean = true, force = false) {
|
||||||
// Initialize of the new category name
|
// Initializing identifier of the category
|
||||||
const category = button.getAttribute("data-category-name");
|
const identifier = button.getAttribute(
|
||||||
|
"data-category-identifier",
|
||||||
|
);
|
||||||
|
|
||||||
this._category(category, clean, force);
|
this._category(identifier, clean, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -80,22 +77,22 @@ import("/js/core.js").then(() =>
|
||||||
/**
|
/**
|
||||||
* Select a category (system)
|
* Select a category (system)
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} button Button of category <a>
|
* @param {string} identifier Identifier of the category
|
||||||
* @param {bool} clean Clear search bar?
|
* @param {bool} clean Clear search bar?
|
||||||
*
|
*
|
||||||
* @return {Promise} Request to the server
|
* @return {Promise} Request to the server
|
||||||
*/
|
*/
|
||||||
static __category(category = "", clean = true) {
|
static __category(category, clean = true) {
|
||||||
if (typeof category === "string") {
|
if (typeof category === "string") {
|
||||||
//
|
// Received required parameters
|
||||||
|
|
||||||
let urn;
|
return core.request(
|
||||||
if (category === "/" || category === "") urn = "/";
|
"/category" +
|
||||||
else {urn = this.categories.length > 0
|
("/" + category).replace(/^\/*/, "/").trim().replace(
|
||||||
? `/${this.categories.join("/")}/${category}`
|
/\/*$/,
|
||||||
: `/${category}`;}
|
"",
|
||||||
|
),
|
||||||
return core.request(urn)
|
)
|
||||||
.then((json) => {
|
.then((json) => {
|
||||||
if (
|
if (
|
||||||
json.errors !== null &&
|
json.errors !== null &&
|
||||||
|
@ -114,11 +111,6 @@ import("/js/core.js").then(() =>
|
||||||
if (search instanceof HTMLElement) search.value = "";
|
if (search instanceof HTMLElement) search.value = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the category to position in the categories hierarchy
|
|
||||||
if (category !== "/" && category !== "") {
|
|
||||||
this.categories.push(category);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
typeof json.title === "string" &&
|
typeof json.title === "string" &&
|
||||||
json.title.length > 0
|
json.title.length > 0
|
||||||
|
@ -288,9 +280,6 @@ import("/js/core.js").then(() =>
|
||||||
* @todo add animations of errors
|
* @todo add animations of errors
|
||||||
*/
|
*/
|
||||||
static __search(element) {
|
static __search(element) {
|
||||||
// Deinitialization of position in the categories hierarchy
|
|
||||||
this.categories = [];
|
|
||||||
|
|
||||||
return this.__category("/", false)
|
return this.__category("/", false)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
core.request("/search", `text=${element.value}`)
|
core.request("/search", `text=${element.value}`)
|
||||||
|
@ -392,19 +381,19 @@ import("/js/core.js").then(() =>
|
||||||
/**
|
/**
|
||||||
* Open product card (interface)
|
* Open product card (interface)
|
||||||
*
|
*
|
||||||
* @param {string} id Identifier of a product
|
* @param {string} identifier Identifier of a product
|
||||||
* @param {bool} force Ignore the damper?
|
* @param {bool} force Ignore the damper?
|
||||||
*
|
*
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
static product(id, force = false) {
|
static product(identifier, force = false) {
|
||||||
this._product(id, force);
|
this._product(identifier, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open product card (damper)
|
* Open product card (damper)
|
||||||
*
|
*
|
||||||
* @param {string} id Identifier of a product
|
* @param {string} identifier Identifier of a product
|
||||||
* @param {bool} force Ignore the damper?
|
* @param {bool} force Ignore the damper?
|
||||||
*
|
*
|
||||||
* @return {void}
|
* @return {void}
|
||||||
|
@ -418,15 +407,15 @@ import("/js/core.js").then(() =>
|
||||||
/**
|
/**
|
||||||
* Open product card (system)
|
* Open product card (system)
|
||||||
*
|
*
|
||||||
* @param {string} id Identifier of a product
|
* @param {string} identifier Identifier of a product
|
||||||
*
|
*
|
||||||
* @return {Promise} Request to the server
|
* @return {Promise} Request to the server
|
||||||
*/
|
*/
|
||||||
static __product(id) {
|
static __product(identifier) {
|
||||||
if (typeof id === "number") {
|
if (typeof identifier === "number") {
|
||||||
//
|
//
|
||||||
|
|
||||||
return core.request(`/product/${id}`)
|
return core.request(`/product/${identifier}`)
|
||||||
.then((json) => {
|
.then((json) => {
|
||||||
if (
|
if (
|
||||||
json.errors !== null &&
|
json.errors !== null &&
|
||||||
|
@ -455,15 +444,18 @@ import("/js/core.js").then(() =>
|
||||||
card.classList.add("card", "unselectable");
|
card.classList.add("card", "unselectable");
|
||||||
|
|
||||||
const h3 = document.createElement("h3");
|
const h3 = document.createElement("h3");
|
||||||
h3.setAttribute("title", json.product.id);
|
|
||||||
|
|
||||||
const title = document.createElement("span");
|
const name = document.createElement("span");
|
||||||
title.classList.add("title");
|
name.classList.add("name");
|
||||||
title.innerText = json.product.title;
|
name.setAttribute("title", json.product.identifier);
|
||||||
|
name.innerText = json.product.name;
|
||||||
|
|
||||||
const brand = document.createElement("small");
|
const exit = document.createElement("a");
|
||||||
brand.classList.add("brand");
|
exit.classList.add("exit");
|
||||||
brand.innerText = json.product.brand;
|
exit.setAttribute("type", "button");
|
||||||
|
|
||||||
|
const exit_icon = document.createElement("i");
|
||||||
|
exit_icon.classList.add("icon", "close");
|
||||||
|
|
||||||
const images = document.createElement("div");
|
const images = document.createElement("div");
|
||||||
images.classList.add("images", "unselectable");
|
images.classList.add("images", "unselectable");
|
||||||
|
@ -528,6 +520,13 @@ import("/js/core.js").then(() =>
|
||||||
images.append(image);
|
images.append(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const header = document.createElement("p");
|
||||||
|
header.classList.add('header');
|
||||||
|
|
||||||
|
const brand = document.createElement("small");
|
||||||
|
brand.classList.add("brand");
|
||||||
|
brand.innerText = json.product.brand;
|
||||||
|
|
||||||
const description = document.createElement("p");
|
const description = document.createElement("p");
|
||||||
description.classList.add("description");
|
description.classList.add("description");
|
||||||
description.innerText = json.product.description;
|
description.innerText = json.product.description;
|
||||||
|
@ -542,10 +541,24 @@ import("/js/core.js").then(() =>
|
||||||
|
|
||||||
const dimensions = document.createElement("small");
|
const dimensions = document.createElement("small");
|
||||||
dimensions.classList.add("dimensions");
|
dimensions.classList.add("dimensions");
|
||||||
dimensions.innerText = json.product.dimensions.x +
|
|
||||||
"x" +
|
const x = json.product.dimensions.x;
|
||||||
json.product.dimensions.y + "x" +
|
const y = json.product.dimensions.y;
|
||||||
json.product.dimensions.z;
|
const z = json.product.dimensions.z;
|
||||||
|
|
||||||
|
let formatted = "";
|
||||||
|
|
||||||
|
if (x !== '' ) formatted = x;
|
||||||
|
if (y !== '') {
|
||||||
|
if (formatted.length === 0) formatted = y;
|
||||||
|
else formatted += "x" + y;
|
||||||
|
}
|
||||||
|
if (z !== '') {
|
||||||
|
if (formatted.length === 0) formatted = z;
|
||||||
|
else formatted += "x" + z;
|
||||||
|
}
|
||||||
|
|
||||||
|
dimensions.innerText = formatted;
|
||||||
|
|
||||||
const weight = document.createElement("small");
|
const weight = document.createElement("small");
|
||||||
weight.classList.add("weight");
|
weight.classList.add("weight");
|
||||||
|
@ -555,10 +568,13 @@ import("/js/core.js").then(() =>
|
||||||
cost.classList.add("cost");
|
cost.classList.add("cost");
|
||||||
cost.innerText = json.product.cost + "р";
|
cost.innerText = json.product.cost + "р";
|
||||||
|
|
||||||
h3.append(title);
|
h3.append(name);
|
||||||
h3.append(brand);
|
exit.append(exit_icon);
|
||||||
|
h3.append(exit);
|
||||||
card.append(h3);
|
card.append(h3);
|
||||||
card.append(images);
|
card.append(images);
|
||||||
|
header.append(brand);
|
||||||
|
card.append(header);
|
||||||
card.append(description);
|
card.append(description);
|
||||||
card.append(compatibility);
|
card.append(compatibility);
|
||||||
footer.append(dimensions);
|
footer.append(dimensions);
|
||||||
|
@ -581,8 +597,8 @@ import("/js/core.js").then(() =>
|
||||||
);
|
);
|
||||||
|
|
||||||
history.pushState(
|
history.pushState(
|
||||||
{ product_card: json.product.id },
|
{ product_card: json.product.identifier },
|
||||||
json.product.title,
|
json.product.name,
|
||||||
);
|
);
|
||||||
|
|
||||||
// блокировка закрытия карточки
|
// блокировка закрытия карточки
|
||||||
|
@ -595,6 +611,8 @@ import("/js/core.js").then(() =>
|
||||||
wrap.remove();
|
wrap.remove();
|
||||||
wrap.removeEventListener("mousedown", _from);
|
wrap.removeEventListener("mousedown", _from);
|
||||||
wrap.removeEventListener("touchstart", _from);
|
wrap.removeEventListener("touchstart", _from);
|
||||||
|
exit.removeEventListener("click", remove);
|
||||||
|
exit.removeEventListener("touch", remove);
|
||||||
document.removeEventListener("click", close);
|
document.removeEventListener("click", close);
|
||||||
document.removeEventListener("touch", close);
|
document.removeEventListener("touch", close);
|
||||||
window.removeEventListener("popstate", remove);
|
window.removeEventListener("popstate", remove);
|
||||||
|
@ -615,13 +633,15 @@ import("/js/core.js").then(() =>
|
||||||
from = undefined;
|
from = undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exit.addEventListener("click", remove);
|
||||||
|
exit.addEventListener("touch", remove);
|
||||||
document.addEventListener("click", close);
|
document.addEventListener("click", close);
|
||||||
document.addEventListener("touch", close);
|
document.addEventListener("touch", close);
|
||||||
window.addEventListener("popstate", remove);
|
window.addEventListener("popstate", remove);
|
||||||
|
|
||||||
if (width > card.offsetWidth) {
|
if (width > card.offsetWidth) {
|
||||||
images.hotline = new core.hotline(
|
images.hotline = new core.hotline(
|
||||||
json.product.id,
|
json.product.identfier,
|
||||||
images,
|
images,
|
||||||
);
|
);
|
||||||
images.hotline.step = -0.3;
|
images.hotline.step = -0.3;
|
||||||
|
|
|
@ -8,9 +8,10 @@ main>section[data-section="catalog"] {
|
||||||
}
|
}
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"] {
|
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"] {
|
||||||
|
--padding: 0.7rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 23px;
|
height: 23px;
|
||||||
padding: unset;
|
padding: var(--padding);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -27,7 +28,9 @@ main>section[data-section="catalog"][data-catalog-type="categories"]:last-child
|
||||||
}
|
}
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]:has(>img) {
|
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]:has(>img) {
|
||||||
|
min-width: calc(50% - var(--padding) * 2);
|
||||||
height: 180px;
|
height: 180px;
|
||||||
|
padding: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]>img {
|
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]>img {
|
||||||
|
@ -37,7 +40,8 @@ main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[
|
||||||
width: 110%;
|
width: 110%;
|
||||||
height: 110%;
|
height: 110%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
filter: blur(1px);
|
/* filter: blur(1px); */
|
||||||
|
filter: brightness(60%);
|
||||||
}
|
}
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]:hover>img {
|
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]:hover>img {
|
||||||
|
@ -45,14 +49,12 @@ main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[
|
||||||
}
|
}
|
||||||
|
|
||||||
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]:has(>img)>p {
|
main>section[data-section="catalog"][data-catalog-type="categories"]>a.category[type="button"]:has(>img)>p {
|
||||||
--padding: 0.7rem;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: var(--padding);
|
left: var(--padding);
|
||||||
bottom: var(--padding);
|
bottom: var(--padding);
|
||||||
right: var(--padding);
|
right: var(--padding);
|
||||||
margin: unset;
|
margin: unset;
|
||||||
width: min-content;
|
width: min-content;
|
||||||
padding: var(--padding);
|
|
||||||
border-radius: 0.75rem;
|
border-radius: 0.75rem;
|
||||||
background: var(--tg-theme-secondary-bg-color);
|
background: var(--tg-theme-secondary-bg-color);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
i.icon.close {
|
||||||
|
--diameter: 22px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: var(--diameter);
|
||||||
|
height: var(--diameter);
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 40px;
|
||||||
|
}
|
||||||
|
i.icon.close::after,
|
||||||
|
i.icon.close::before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: absolute;
|
||||||
|
width: 16px;
|
||||||
|
height: 2px;
|
||||||
|
background: currentColor;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
border-radius: 5px;
|
||||||
|
top: 8px;
|
||||||
|
left: 1px;
|
||||||
|
}
|
||||||
|
i.icon.close::after {
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
}
|
||||||
|
|
|
@ -108,12 +108,15 @@ search:has(input:disabled) {
|
||||||
backdrop-filter: contrast(0.5);
|
backdrop-filter: contrast(0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*[type="button"] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
:is(button, a[type="button"]) {
|
:is(button, a[type="button"]) {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
|
||||||
color: var(--tg-theme-button-text-color);
|
color: var(--tg-theme-button-text-color);
|
||||||
background-color: var(--tg-theme-button-color);
|
background-color: var(--tg-theme-button-color);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
section#window {
|
section#window {
|
||||||
z-index: 1500;
|
z-index: 1500;
|
||||||
position: absolute;
|
position: fixed;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -24,7 +24,7 @@ section#window>div.card {
|
||||||
section#window>div.card>h3 {
|
section#window>div.card>h3 {
|
||||||
margin: 0 0 0.5rem 0;
|
margin: 0 0 0.5rem 0;
|
||||||
height: 23px;
|
height: 23px;
|
||||||
padding: 1rem 1.5rem;
|
padding: 1rem 0;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
@ -32,16 +32,29 @@ section#window>div.card>h3 {
|
||||||
background-color: var(--tg-theme-header-bg-color);
|
background-color: var(--tg-theme-header-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
section#window>div.card>h3>span.title {
|
section#window>div.card>h3>span.name {
|
||||||
|
margin-left: 1.5rem;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
section#window>div.card>h3>small.brand {
|
/* section#window>div.card>h3>small.brand {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
color: var(--tg-theme-section-header-text-color);
|
color: var(--tg-theme-section-header-text-color);
|
||||||
|
} */
|
||||||
|
|
||||||
|
section#window>div.card>h3>a.exit[type="button"] {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
padding: 0.6rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--tg-theme-section-header-text-color);
|
||||||
|
background-color: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
section#window>div.card>div.images {
|
section#window>div.card>div.images {
|
||||||
|
|
|
@ -16,7 +16,8 @@ use mirzaev\minimal\controller;
|
||||||
use Twig\Loader\FilesystemLoader,
|
use Twig\Loader\FilesystemLoader,
|
||||||
Twig\Environment as twig,
|
Twig\Environment as twig,
|
||||||
Twig\Extra\Intl\IntlExtension as intl,
|
Twig\Extra\Intl\IntlExtension as intl,
|
||||||
Twig\TwigFilter;
|
Twig\TwigFilter,
|
||||||
|
Twig\TwigFunction;
|
||||||
|
|
||||||
|
|
||||||
// Built-in libraries
|
// Built-in libraries
|
||||||
|
@ -43,13 +44,17 @@ final class templater extends controller implements ArrayAccess
|
||||||
/**
|
/**
|
||||||
* Constructor of an instance
|
* Constructor of an instance
|
||||||
*
|
*
|
||||||
* @param session|null $session An object implementing a session instance from ArangoDB
|
* @param session|null $session The object implementing a session instance from ArangoDB
|
||||||
* @param account|null $account An object implementing an account instance from ArangoDB
|
* @param account|null $account The object implementing an account instance from ArangoDB
|
||||||
|
* @param settings|null $settings The object implementing an account instance from ArangoDB
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct(?session $session = null, ?account $account = null)
|
public function __construct(
|
||||||
{
|
?session $session = null,
|
||||||
|
?account $account = null,
|
||||||
|
?settings $settings = null
|
||||||
|
) {
|
||||||
// Initializing of an instance of twig
|
// Initializing of an instance of twig
|
||||||
$this->twig = new twig(new FilesystemLoader(VIEWS));
|
$this->twig = new twig(new FilesystemLoader(VIEWS));
|
||||||
|
|
||||||
|
@ -57,9 +62,41 @@ final class templater extends controller implements ArrayAccess
|
||||||
$this->twig->addGlobal('theme', 'default');
|
$this->twig->addGlobal('theme', 'default');
|
||||||
$this->twig->addGlobal('server', $_SERVER);
|
$this->twig->addGlobal('server', $_SERVER);
|
||||||
$this->twig->addGlobal('cookies', $_COOKIE);
|
$this->twig->addGlobal('cookies', $_COOKIE);
|
||||||
$this->twig->addGlobal('settings', settings::active());
|
$this->twig->addGlobal('settings', $settings);
|
||||||
if (!empty($session?->status())) $this->twig->addGlobal('session', $session);
|
if (!empty($session?->status())) $this->twig->addGlobal('session', $session);
|
||||||
if (!empty($account?->status())) $this->twig->addGlobal('account', $account);
|
if (!empty($account?->status())) $this->twig->addGlobal('account', $account);
|
||||||
|
$this->twig->addGlobal('language', $account?->language->name ?? $settings?->language->name ?? 'en');
|
||||||
|
|
||||||
|
|
||||||
|
// Initialize function of dimensions formattinx
|
||||||
|
$this->twig->addFunction(
|
||||||
|
new TwigFunction(
|
||||||
|
'format_dimensions',
|
||||||
|
function (
|
||||||
|
string|int|float|null $x = null,
|
||||||
|
string|int|float|null $y = null,
|
||||||
|
string|int|float|null $z = null,
|
||||||
|
string|null $before = null,
|
||||||
|
string|null $after = null
|
||||||
|
) {
|
||||||
|
// Initialzing the buffer of result
|
||||||
|
$result = '';
|
||||||
|
|
||||||
|
// Generating
|
||||||
|
if (!empty($x)) $result .= $x;
|
||||||
|
if (!empty($y))
|
||||||
|
if (empty($result)) $result = "$y";
|
||||||
|
else $result .= "x$y";
|
||||||
|
if (!empty($z))
|
||||||
|
if (empty($result)) $result = "$z";
|
||||||
|
else $result .= "x$z";
|
||||||
|
if (!empty($result)) $result = "$before$result$after";
|
||||||
|
|
||||||
|
// Exit (success)
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// Initializing of twig extensions
|
// Initializing of twig extensions
|
||||||
$this->twig->addExtension(new intl());
|
$this->twig->addExtension(new intl());
|
||||||
|
|
|
@ -2,11 +2,19 @@
|
||||||
<section class="unselectable" data-section="catalog" data-catalog-type="categories"
|
<section class="unselectable" data-section="catalog" data-catalog-type="categories"
|
||||||
data-catalog-level="{{ level ?? 0 }}">
|
data-catalog-level="{{ level ?? 0 }}">
|
||||||
{% for category in categories %}
|
{% for category in categories %}
|
||||||
|
{% if category.images %}
|
||||||
<a id="{{ category.getId() }}" class="category" type="button" onclick="return core.catalog.category(this);"
|
<a id="{{ category.getId() }}" class="category" type="button" onclick="return core.catalog.category(this);"
|
||||||
data-category-identifier="{{ category.identifier }}">
|
data-category-identifier="{{ category.identifier }}">
|
||||||
<img src="{{ category.images.0.storage }}" alt="{{ category.name[ account.language ?? settings.language ?? 'en' ] }}" ondrugstart="return false;">
|
<img src="{{ category.images.0.storage }}"
|
||||||
<p>{{ category.name[ account.language ?? settings.language ?? 'en' ] }}</p>
|
alt="{{ category.name[language] }}" ondrugstart="return false;">
|
||||||
|
<p>{{ category.name[language] }}</p>
|
||||||
</a>
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<a id="{{ category.getId() }}" class="category" type="button" onclick="return core.catalog.category(this);"
|
||||||
|
data-category-identifier="{{ category.identifier }}">
|
||||||
|
<p>{{ category.name[language] }}</p>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
{% macro card(product) %}
|
{% macro card(product) %}
|
||||||
{% set title = product.title.ru ~ ' ' ~ product.brand.ru ~ ' ' ~ product.dimensions.x ~ 'x' ~ product.dimensions.y ~ 'x'
|
{% set title = product.name[language] ~ ' ' ~ product.brand[language] ~ format_dimensions(product.dimensions.x, product.dimensions.y, product.dimensions.z, ' ') ~ ' ' ~ product.weight ~ 'г' %}
|
||||||
~ product.dimensions.z ~ ' ' ~ product.weight ~ 'г' %}
|
|
||||||
<article id="{{ product.getId() }}" class="product unselectable">
|
<article id="{{ product.getId() }}" class="product unselectable">
|
||||||
<a onclick="core.catalog.product({{ product.getKey() }})">
|
<a onclick="core.catalog.product({{ product.identifier }})">
|
||||||
<img src="{{ product.images.0.storage }}" alt="{{ product.title.ru }}" ondrugstart="return false;">
|
<img src="{{ product.images.0.storage }}" alt="{{ product.name[language] }}" ondrugstart="return false;">
|
||||||
<p class="title" title="{{ product.title.ru }}">
|
<p class="title" title="{{ product.name[language] }}">
|
||||||
{{ title | length > 45 ? title | slice(0, 45) ~ '...' : title }}
|
{{ title | length > 45 ? title | slice(0, 45) ~ '...' : title }}
|
||||||
</p>
|
</p>
|
||||||
</a>
|
</a>
|
||||||
|
@ -13,7 +12,6 @@
|
||||||
</button>
|
</button>
|
||||||
</article>
|
</article>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% if products is not empty %}
|
{% if products is not empty %}
|
||||||
<section class="unselectable" data-section="catalog" data-catalog-type="products" data-catalog-level="{{ level ?? 0 }}">
|
<section class="unselectable" data-section="catalog" data-catalog-type="products" data-catalog-level="{{ level ?? 0 }}">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/catalog/2columns.css" />
|
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/catalog/2columns.css" />
|
||||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/search.css" />
|
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/search.css" />
|
||||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/shopping_cart.css" />
|
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/shopping_cart.css" />
|
||||||
|
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/close.css" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
Loading…
Reference in New Issue