diff --git a/_build/data/transport.menu.php b/_build/data/transport.menu.php
index 9a6859a97..ae476147b 100644
--- a/_build/data/transport.menu.php
+++ b/_build/data/transport.menu.php
@@ -36,6 +36,12 @@
'menuindex' => 3,
'action' => 'mgr/help',
],
+ 'ms2_utilites' => [
+ 'description' => 'ms2_utilites_desc',
+ 'parent' => 'minishop2',
+ 'menuindex' => 4,
+ 'action' => 'mgr/utilites',
+ ],
];
foreach ($tmp as $k => $v) {
diff --git a/assets/components/minishop2/css/mgr/utilites/gallery.css b/assets/components/minishop2/css/mgr/utilites/gallery.css
new file mode 100644
index 000000000..e7b9736ee
--- /dev/null
+++ b/assets/components/minishop2/css/mgr/utilites/gallery.css
@@ -0,0 +1,36 @@
+#ms-utility-gallery-range_outer{
+ position: relative;
+ padding: 20px 0 0;
+ visibility: hidden;
+}
+
+.ms-utility-gallery-labels{
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+
+ font-weight: bold;
+}
+
+#ms-utility-gallery-iteration{
+ position: absolute;
+ top: 0;
+ right: 0;
+}
+
+#ms-utility-gallery-progress{
+ position: relative;
+ height: 5px;
+ background-color: #ddd;
+}
+
+#ms-utility-gallery-progress-bar{
+ position: absolute;
+ top: 0;
+ left: 0;
+
+ height: 100%;
+
+ background-color: #32AB9A;
+}
\ No newline at end of file
diff --git a/assets/components/minishop2/js/mgr/utilites/gallery/panel.js b/assets/components/minishop2/js/mgr/utilites/gallery/panel.js
new file mode 100644
index 000000000..1ef65dd75
--- /dev/null
+++ b/assets/components/minishop2/js/mgr/utilites/gallery/panel.js
@@ -0,0 +1,135 @@
+miniShop2.panel.UtilitesGallery = function (config) {
+ config = config || {};
+
+ Ext.apply(config, {
+ cls: 'container form-with-labels',
+ autoHeight: true,
+ url: miniShop2.config.connector_url,
+ saveMsg: _('ms2_utilites_gallery_updating'),
+
+ progress:true,
+ baseParams: {
+ action: 'mgr/utilites/gallery/update'
+ },
+ items: [{
+ layout: 'form',
+ cls: 'main-wrapper',
+ labelWidth: 200,
+ labelAlign: 'left',
+ border: false,
+ buttonAlign: 'left',
+ style: 'padding: 0 0 0 7px',
+ items: [
+ {
+ html: String.format(
+ _('ms2_utilites_gallery_information'),
+ miniShop2.config.utility_gallery_source_name,
+ miniShop2.config.utility_gallery_source_id,
+ miniShop2.config.utility_gallery_total_products,
+ miniShop2.config.utility_gallery_total_products_files
+ ),
+ },
+ {
+ xtype: 'fieldset',
+ title: _('ms2_utilites_params'),
+ id: 'ms2-utilites-gallery-params',
+ cls: 'x-fieldset-checkbox-toggle',
+ style: 'margin: 5px 0 15px ',
+ collapsible: true,
+ collapsed: true,
+ stateful: true,
+ labelAlign: 'top',
+ stateEvents: ['collapse', 'expand'],
+ items: [
+ {
+ html: miniShop2.config.utility_gallery_thumbnails
+ },
+ ]
+ },
+ {
+ name: 'limit',
+ xtype: 'numberfield',
+ value: 10,
+ width: 80,
+ fieldLabel: _('ms2_utilites_gallery_for_step')
+ },
+ {
+ name: 'offset',
+ xtype: 'numberfield',
+ value: 0,
+ hidden: true,
+ },
+ {
+ xtype: 'button',
+ style: 'margin: 15px 0 0 2px',
+ text: ' ' + _('ms2_utilites_gallery_refresh'),
+ handler: function() {
+ var form = this.getForm();
+ form.setValues({
+ offset: 0
+ });
+ this.submit(this);
+ }, scope: this
+ },
+ {
+ style: 'padding: 15px 0',
+ html: '\
+
\
+ '
+ }
+ ]
+ }],
+ listeners: {
+ success: {fn: function(response) {
+ var data = response.result.object;
+ var form = this.getForm();
+ this.updateProgress(data);
+
+ if (!data.done) {
+ form.setValues({
+ offset: Number(data.offset)
+ });
+ this.submit(this);
+ }
+ else {
+ MODx.msg.status({
+ title: _('ms2_utilites_gallery_done'),
+ message: _('ms2_utilites_gallery_done_message'),
+ delay: 5
+ });
+ }
+ },scope: this}
+ }
+ });
+ miniShop2.panel.UtilitesGallery.superclass.constructor.call(this, config);
+};
+
+Ext.extend(miniShop2.panel.UtilitesGallery, MODx.FormPanel,{
+
+ updateProgress: function (data) {
+ const progressblock = document.getElementById('ms-utility-gallery-range_outer');
+ const progresslabel = document.getElementById('ms-utility-gallery-label');
+ const progressbar = document.getElementById('ms-utility-gallery-progress-bar');
+ const progressiteration = document.getElementById('ms-utility-gallery-iteration');
+ progressblock.style.visibility = 'visible';
+
+ if(data.done) {
+ progresslabel.innerHTML = '100%';
+ progressbar.style.width = '100%';
+ progressiteration.style.visibility = 'hidden';
+ } else {
+ let progress = (parseFloat((data.offset/data.total) * 100)).toFixed(2);
+ progresslabel.innerHTML = progress + '%';
+ progressbar.style.width = progress + '%';
+
+ // count iterations
+ const totalIterations = Math.ceil(data.total/data.limit);
+ const currentIteration = data.offset/data.limit;
+ progressiteration.innerHTML = currentIteration + "/" + totalIterations;
+ }
+ }
+});
+Ext.reg('minishop2-utilites-gallery', miniShop2.panel.UtilitesGallery);
diff --git a/assets/components/minishop2/js/mgr/utilites/panel.js b/assets/components/minishop2/js/mgr/utilites/panel.js
new file mode 100644
index 000000000..8c13cd0c0
--- /dev/null
+++ b/assets/components/minishop2/js/mgr/utilites/panel.js
@@ -0,0 +1,51 @@
+miniShop2.panel.Utilites = function (config) {
+ config = config || {};
+ Ext.apply(config, {
+ cls: 'container',
+ items: [{
+ html: '' + _('minishop2') + ' :: ' + _('ms2_utilites') + '
',
+ cls: 'modx-page-header',
+ }, {
+ xtype: 'modx-tabs',
+ id: 'minishop2-utilites-tabs',
+ stateful: true,
+ stateId: 'minishop2-utilites-tabs',
+ stateEvents: ['tabchange'],
+ cls: 'minishop2-panel',
+ getState: function () {
+ return {
+ activeTab: this.items.indexOf(this.getActiveTab())
+ };
+ },
+ items: [{
+ title: _('ms2_utilites_gallery'),
+ layout: 'anchor',
+ items: [{
+ html: _('ms2_utilites_gallery_intro'),
+ bodyCssClass: 'panel-desc',
+ }, {
+ xtype: 'minishop2-utilites-gallery',
+ cls: 'main-wrapper',
+ }]
+ },
+ /*
+ // todo
+ {
+ title: _('ms2_utilites_import'),
+ layout: 'anchor',
+ items: [{
+ html: _('ms2_utilites_import_intro'),
+ bodyCssClass: 'panel-desc',
+ }, {
+ xtype: 'minishop2-utilites-import',
+ cls: 'main-wrapper',
+ }]
+ }*/
+ ]
+ }]
+
+ });
+ miniShop2.panel.Utilites.superclass.constructor.call(this, config);
+};
+Ext.extend(miniShop2.panel.Utilites, MODx.Panel);
+Ext.reg('minishop2-utilites', miniShop2.panel.Utilites);
diff --git a/core/components/minishop2/controllers/mgr/utilites.class.php b/core/components/minishop2/controllers/mgr/utilites.class.php
new file mode 100644
index 000000000..e6c6c1ef9
--- /dev/null
+++ b/core/components/minishop2/controllers/mgr/utilites.class.php
@@ -0,0 +1,67 @@
+modx->lexicon('ms2_utilites') . ' | miniShop2';
+ }
+
+ /**
+ * @return array
+ */
+ public function getLanguageTopics()
+ {
+ return ['minishop2:default', 'minishop2:product', 'minishop2:manager'];
+ }
+
+ /**
+ *
+ */
+ public function loadCustomCssJs()
+ {
+
+ $this->addCss($this->miniShop2->config['cssUrl'] . 'mgr/utilites/gallery.css');
+
+ $this->addJavascript($this->miniShop2->config['jsUrl'] . 'mgr/minishop2.js');
+ $this->addJavascript($this->miniShop2->config['jsUrl'] . 'mgr/utilites/panel.js');
+ $this->addJavascript($this->miniShop2->config['jsUrl'] . 'mgr/utilites/gallery/panel.js');
+
+ $config = $this->miniShop2->config;
+
+ // get source properties
+ $productSource = $this->getOption('ms2_product_source_default', null, 1);
+ if ($source = $this->modx->getObject('modMediaSource', $productSource)) {
+ $config['utility_gallery_source_id'] = $productSource;
+ $config['utility_gallery_source_name'] = $source->get('name');
+
+ $properties = $source->get('properties');
+ $propertiesString = '';
+ foreach (json_decode($properties['thumbnails']['value'], true) as $key => $value) {
+ $propertiesString .= "$key: " . json_encode($value) . "
";
+ }
+ $config['utility_gallery_thumbnails'] = $propertiesString;
+ }
+
+ // get information about products and files
+ $config['utility_gallery_total_products'] = $this->modx->getCount('msProduct', ['class_key' => 'msProduct']);
+ $config['utility_gallery_total_products_files'] = $this->modx->getCount('msProductFile', ['parent' => 0]);
+
+ $this->addHtml(
+ ''
+ );
+ }
+}
diff --git a/core/components/minishop2/lexicon/ru/default.inc.php b/core/components/minishop2/lexicon/ru/default.inc.php
index dc2de2ca6..72994762c 100644
--- a/core/components/minishop2/lexicon/ru/default.inc.php
+++ b/core/components/minishop2/lexicon/ru/default.inc.php
@@ -165,5 +165,7 @@
$_lang['ms2_system_settings'] = 'Системные настройки';
$_lang['ms2_system_settings_desc'] = 'Системные настройки miniShop2';
$_lang['ms2_type'] = 'Тип';
+$_lang['ms2_utilites'] = 'Утилиты';
+$_lang['ms2_utilites_desc'] = 'Инструменты разработчика';
$_lang['ms2_vendors'] = 'Производители товаров';
$_lang['ms2_vendors_intro'] = 'Список возможных производителей товаров. То, что вы сюда добавите, можно выбрать в поле "vendor" товара.';
diff --git a/core/components/minishop2/lexicon/ru/manager.inc.php b/core/components/minishop2/lexicon/ru/manager.inc.php
index ddb7d7836..0b44e8d4b 100644
--- a/core/components/minishop2/lexicon/ru/manager.inc.php
+++ b/core/components/minishop2/lexicon/ru/manager.inc.php
@@ -152,6 +152,16 @@
$_lang['ms2_updatedon'] = 'Дата изменения';
$_lang['ms2_user'] = 'Пользователь';
$_lang['ms2_username'] = 'Логин';
+$_lang['ms2_utilites_gallery'] = 'Галерея';
+$_lang['ms2_utilites_gallery_done'] = 'Завершено';
+$_lang['ms2_utilites_gallery_done_message'] = 'Обновление превью изображений успешно завершено.';
+$_lang['ms2_utilites_gallery_err_noproducts'] = 'В каталоге нет товаров';
+$_lang['ms2_utilites_gallery_for_step'] = 'Обработать товаров за 1 шаг';
+$_lang['ms2_utilites_gallery_information'] = 'Выбранный источник файлов: {0}
Всего товаров: {2} шт.
Изображений: {3} шт. ';
+$_lang['ms2_utilites_gallery_intro'] = 'Обновление всех изображений товаров согласно указанным параметрам.
Данная операция является трудозатратной, поэтому не указывайте большое число для одной итерации.';
+$_lang['ms2_utilites_gallery_refresh'] = 'Обновить';
+$_lang['ms2_utilites_gallery_updating'] = 'Обновлению превью';
+$_lang['ms2_utilites_params'] = 'Параметры ';
$_lang['ms2_weight'] = 'Вес';
$_lang['ms2_weight_price'] = 'Стоимость ед/вес';
$_lang['ms2_weight_price_help'] = 'Добавочная стоимость доставки за единицу веса.
Может быть использовано в кастомных классах.';
diff --git a/core/components/minishop2/processors/mgr/utilites/gallery/update.class.php b/core/components/minishop2/processors/mgr/utilites/gallery/update.class.php
new file mode 100644
index 000000000..d192ae470
--- /dev/null
+++ b/core/components/minishop2/processors/mgr/utilites/gallery/update.class.php
@@ -0,0 +1,122 @@
+modx->hasPermission($this->permission)) {
+ return $this->modx->lexicon('access_denied');
+ }
+
+ return parent::initialize();
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getLanguageTopics()
+ {
+ return $this->languageTopics;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public function process()
+ {
+ $this->limit = (int)$this->getProperty('limit', 10);
+ $this->offset = (int)$this->getProperty('offset', 0);
+
+ $c = $this->modx->newQuery('msProduct');
+ $c->sortby('id', 'ASC');
+ $c->where(['class_key' => 'msProduct']);
+ $c->select('msProduct.id');
+
+ $this->total = $this->modx->getCount('msProduct', $c);
+ $c->limit($this->limit, $this->offset);
+
+ $products = [];
+ if ($c->prepare() && $c->stmt->execute()) {
+ $products = $c->stmt->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ if (!is_array($products) || empty($products)) {
+ return $this->failure($this->modx->lexicon('ms2_utilites_gallery_err_noproducts'));
+ }
+
+ $i = 0;
+ foreach ($products as $product) {
+ $this->generateThumbnails($product['id']);
+ $i++;
+ }
+
+ $offset = $this->offset + $this->limit;
+ $done = $offset >= $this->total;
+
+ return $this->success('', [
+ 'updated' => $i,
+ 'offset' => $done ? 0 : $offset,
+ 'done' => $done,
+ 'total' => $this->total,
+ 'limit' => $this->limit,
+ ]);
+ }
+
+
+ public function generateThumbnails($product_id)
+ {
+ if (empty($product_id)) {
+ return $this->failure($this->modx->lexicon('ms2_gallery_err_ns'));
+ }
+
+ $files = $this->modx->getCollection('msProductFile', ['product_id' => $product_id, 'parent' => 0]);
+ /** @var msProductFile $file */
+ foreach ($files as $file) {
+ $children = $file->getMany('Children');
+ /** @var msProductFile $child */
+ foreach ($children as $child) {
+ $child->remove();
+ }
+ $file->generateThumbnails();
+ }
+
+ /** @var msProductData $product */
+ $product = $this->modx->getObject('msProductData', ['id' => $product_id]);
+ if ($product) {
+ $thumb = $product->updateProductImage();
+ /** @var miniShop2 $miniShop2 */
+ if (empty($thumb) && $miniShop2 = $this->modx->getService('miniShop2')) {
+ $thumb = $miniShop2->config['defaultThumb'];
+ }
+ return $this->success('', ['thumb' => $thumb]);
+ }
+
+ return $this->success();
+ }
+}
+
+return 'msUtilityGalleryUpdateProcessor';