mirror of
https://github.com/avecms/AVE.cms.git
synced 2026-08-26 03:11:51 +00:00
release: AVE.cms 3.3 build 0.40
This commit is contained in:
+23
-1
@@ -11,7 +11,29 @@
|
||||
|
||||
## Не выпущено
|
||||
|
||||
Новые изменения будут добавлены после build 0.39.
|
||||
Новые изменения будут добавлены после build 0.40.
|
||||
|
||||
## 3.3 build 0.40 — 25 августа 2026
|
||||
|
||||
### Публичный сайт и вход
|
||||
|
||||
- Неизвестный адрес снова показывает назначенную страницу 404, а не содержимое
|
||||
главной страницы. HTTP-статус 404 и метаданные сохранены.
|
||||
- Публичная регистрация и вход могут работать только по телефону: email больше
|
||||
не требуется, номер форматируется по маске, а доступные способы входа
|
||||
определяются настройками системы.
|
||||
- Настройки телефонного входа и подсказки в кабинете приведены к одному набору
|
||||
разрешённых способов авторизации.
|
||||
|
||||
### Пользователи сайта
|
||||
|
||||
- В разделе пользователей сайта форма учитывает регистрацию без email и
|
||||
корректно показывает телефон как основной идентификатор.
|
||||
|
||||
### Выпуск
|
||||
|
||||
- Для build 0.40 подготовлены чистая core-сборка, компактный установщик и
|
||||
накопительное обновление с build 0.20.
|
||||
|
||||
## 3.3 build 0.39 — 25 августа 2026
|
||||
|
||||
|
||||
+18
-18
@@ -2,19 +2,19 @@
|
||||
"format": "ave-core-package-v2",
|
||||
"product": "AVE.cms",
|
||||
"version": "3.3",
|
||||
"build": "0.39",
|
||||
"build": "0.40",
|
||||
"package": "core",
|
||||
"built_at": "2026-08-25T09:00:39+00:00",
|
||||
"built_at": "2026-08-25T11:42:07+00:00",
|
||||
"installed_modules": 0,
|
||||
"bundled_optional_modules": [],
|
||||
"optional_after_install": [
|
||||
"setup/"
|
||||
],
|
||||
"core_fingerprint": "cd348589924dbf95a20d6e41994c39f036bd55372250ad6caa8c3484158bd6a5",
|
||||
"core_fingerprint": "045c60e55daab45dcf75d86b72e01dd6aed8ca7103efa4293dd1676f0be349ab",
|
||||
"files": {
|
||||
".gitignore": "20d4fb177d47da6944a7a1c2dbf5918fd62766881aa52b00901f9054b1db4b49",
|
||||
".htaccess": "71c06b7bc41b09c347a562ca72768acaa0ee1ef8388c749fc67956c793d8a826",
|
||||
"CHANGELOG.md": "58ab5f834aad74ac1f8101983102fce3f0708d720999efadcdff9efdefa2ed64",
|
||||
"CHANGELOG.md": "4adedd967bb28883655dfb981ebcc7fdc1e32367a79efb4e4417e3fd70d4f04e",
|
||||
"README.md": "19b820f1b092780039aae011e133dc6a1e5f1cf213b435e0ff1962b592b9095b",
|
||||
"adminx/.htaccess": "d03ea2d2972af8b7545a2d57c6253f303d15adeb30b775e161d887ca5ff73378",
|
||||
"adminx/Support/AdminLocale.php": "a7fe84d86d65c9f021106172b1789c8bc6221c36b2fe39cf883ca9a505e3e8ad",
|
||||
@@ -185,7 +185,7 @@
|
||||
"adminx/modules/Blocks/migrations/012_merge_visual_blocks.php": "5c9cd6efd66a027134621859da1059b2c403bacb732885d6b405699cbce8fc14",
|
||||
"adminx/modules/Blocks/module.php": "53eb434566d820814f5b25382200b75ed02b0067c066c8f8cb62bee13529b964",
|
||||
"adminx/modules/Blocks/view/index.twig": "39f9225b6a95c65549d0c861afb7db7879ca338c251290653dded6ab7cb715cc",
|
||||
"adminx/modules/Catalog/Controller.php": "22a19db35e22824abf3bea9053050c2cdd74c937022de905684c5b627d52de57",
|
||||
"adminx/modules/Catalog/Controller.php": "dea3f7ac5cdb8bd1757e18f95bcbeb2dd433123536d6f0766bc584bb1c09fcbf",
|
||||
"adminx/modules/Catalog/Model.php": "d524730981ad388928cfc29208a821ed1d474bd01d6db1749fd37fdfb4e4958e",
|
||||
"adminx/modules/Catalog/assets/catalog-drawer.css": "b3e4f798d7bdf329aa91987ae5be435910ddccfeb8a32211059846ab9dc6b7c4",
|
||||
"adminx/modules/Catalog/assets/catalog.css": "49e7a33fc444f8330806e1cc5cbb0d523b673c64faafd25dbf0fe33067643efd",
|
||||
@@ -219,7 +219,7 @@
|
||||
"adminx/modules/Customers/GlobalSearchProvider.php": "6be85ba385ade05cef1167a6acf146a7e078d176b72db773e0d26d2ab8ea65de",
|
||||
"adminx/modules/Customers/Model.php": "fb84df79d1ef9b403f16e44100325d26db5c144b6cbc6a69e3425a9a3f29a043",
|
||||
"adminx/modules/Customers/assets/customers.css": "2542f8ba075ce20f4fa48a966eda5de82b5e574a17f5bebc88727b608eb47bb8",
|
||||
"adminx/modules/Customers/assets/customers.js": "3a0bc1875dea633b78840b75feb11e903d2b2404080e0f505821156f98a8ffa7",
|
||||
"adminx/modules/Customers/assets/customers.js": "7ef3b863eff78c83bce7d78a7cdd67fc715d26fe8d9d9239af8d43f27bbb45c9",
|
||||
"adminx/modules/Customers/language/en/center.xml": "0ebf643e8a2e00fdf07a290adfd108d0d7e53781d15c37f44d55564c7476b738",
|
||||
"adminx/modules/Customers/language/en/client.xml": "6f22d85d37d66aba749b7b4794635fd644a12e9e71e2a5eff66ee3b1c9813e40",
|
||||
"adminx/modules/Customers/language/en/interface.xml": "573a7ca126beeaff77a8865d6114dac0429b588d8061f939c5ed7af316d4070b",
|
||||
@@ -235,10 +235,10 @@
|
||||
"adminx/modules/Customers/migrations/005_checkout_registration.sql": "4e5ac6d4d5868f9e1c91b896fc03ad0982d5716287101e721a3820d21abe1744",
|
||||
"adminx/modules/Customers/migrations/006_phone_identity.php": "97e8aa457fd8f8ade781a03a98bdc7ee03cac5734782f9e72fb0b76bdbd2cdbb",
|
||||
"adminx/modules/Customers/migrations/007_customer_center.php": "7adde1ce2b92f24b7f1d1d483aca4304bad918d06ae2f213226eca96dcff344e",
|
||||
"adminx/modules/Customers/module.php": "a8a567e986129c8960621235c5bb75c03fd19bcae5a0ae4a1065d8d1eb063d21",
|
||||
"adminx/modules/Customers/module.php": "ef1055dbaf8d23748b492f0fcd8a7ce184b534d72714744d684875735f4b7f73",
|
||||
"adminx/modules/Customers/view/customer-center-detail.twig": "2239602955041c4eebe704c70e835fd45272f721abd28bf254904901bd77a4eb",
|
||||
"adminx/modules/Customers/view/customer-center.twig": "223e657861275fbfe482ce624a6216a91e8df893cd1c5fe58a2999606468addf",
|
||||
"adminx/modules/Customers/view/index.twig": "5b011dab9ca33d66fbd138297875861054477af56f093282d08aaa4c576a7f12",
|
||||
"adminx/modules/Customers/view/index.twig": "61b25d07799e50b3a06c5ab91b4895dd685b54a29085f896f9c51aebbfdb3d7b",
|
||||
"adminx/modules/Dashboard/Controller.php": "7e6ad0610df7581bb010cee574d8d093c4737af439bd2b0763142f50ddee1c54",
|
||||
"adminx/modules/Dashboard/Widgets.php": "56fc7ba7c1f9e30d7eae8c61524b6441b1c706a2cd56b1db883bb8bff7186eed",
|
||||
"adminx/modules/Dashboard/assets/dashboard.css": "8e28b56cac2e55d9b57304ea6e265fd94a52400674c082fbdd78f1eed1872424",
|
||||
@@ -1010,7 +1010,7 @@
|
||||
"system/App/Common/Permission.php": "8e2241bca829de0104741dc2518860cd15489866dc310437e07aeab2bf0f0355",
|
||||
"system/App/Common/Proxy.php": "707564208ec8c24ac2ffb5bf41cba34f1b39059ce6bda31a69aaf4b116d864e2",
|
||||
"system/App/Common/PublicAsset.php": "06f9ae414f7336206073e07597669991098114c396e80748c654c82b370a39c4",
|
||||
"system/App/Common/PublicAuthSettings.php": "a3dd7d0bef9ab608c1e99b161d30962cceeacb4babf37bc24c26511d2d433b3d",
|
||||
"system/App/Common/PublicAuthSettings.php": "56b57c58d51f821797edf5d65b445dc4881be761781e4b13e039a2e15c11ec44",
|
||||
"system/App/Common/PublicBootstrap.php": "b22aa593a13d32ea6382b8ec342a5cfd79a9986b627730eb77e6fec4aaca85cd",
|
||||
"system/App/Common/PublicConfiguration.php": "1daf6c26de04277f8618be4e25453aa479de9f7dfc09ba770d405eb7f9454760",
|
||||
"system/App/Common/PublicModuleRuntime.php": "5aafede8a2c0cf35023419a024566ca453676ea2dd9274a4d5ca99faa9273d15",
|
||||
@@ -1192,8 +1192,8 @@
|
||||
"system/App/Content/Rubrics/RubricMutationService.php": "e3190ef1ddd94bdaf077148ce84c5758b2a32683369730a2b847dbbd0d72a74c",
|
||||
"system/App/Content/Templates/TemplateMutationService.php": "8c7e52202454e35eb0c8176325cc23ba760ac40ff0430cbba5e1446c83cf7a87",
|
||||
"system/App/Frontend/Auth/AuthService.php": "1cef24cffd7773f3783ec96479b8e7daa1808b03a34dfc8da37b84e6e09144b8",
|
||||
"system/App/Frontend/Auth/Controller.php": "14a425c24653fdfda4225550635130bcdf67a6d63a1eb3817ad86a41d292efe9",
|
||||
"system/App/Frontend/Auth/Feature.php": "dbaeac564886c7949e0e47c85fc5496c25221d880c4f03cee40cc5e15d042a1c",
|
||||
"system/App/Frontend/Auth/Controller.php": "8002201011ff8e1c477dc879ec57d2138bf325621978ccbc9a56921c4eff7f9d",
|
||||
"system/App/Frontend/Auth/Feature.php": "7910b5f938854a4a32feed637b757182b12f4a7178cad97a1498509173571776",
|
||||
"system/App/Frontend/Auth/FormTemplateRepository.php": "bcd8797e63cb67803aef2267a2c62e3ad368b5fd947057a768adbb1288ee9281",
|
||||
"system/App/Frontend/Auth/OAuth/HttpClient.php": "b09acf92b312bd05ca78500fccaac3b77d36a1046d64d3615a00d60af4e3594f",
|
||||
"system/App/Frontend/Auth/OAuth/IdentityRepository.php": "35ab143e0d536185125c685f17f5f4bb0ba0cc72daba62552ef6f82370e38ec1",
|
||||
@@ -1211,20 +1211,20 @@
|
||||
"system/App/Frontend/Auth/Registration/EmailRegistrationGate.php": "ce6dcad98a944aade0fd9a80444c183b4f29bc50e1f119aa3d1d72bdfc3fa732",
|
||||
"system/App/Frontend/Auth/Registration/RegistrationGateInterface.php": "99e73d0d6db972bb5b2c2a7a21dd50823a9bcef7d05937c525d0e7096775aedd",
|
||||
"system/App/Frontend/Auth/Registration/RegistrationGateRegistry.php": "bc7c8546780daacd23243d16778062384f043661674956d1a5a47a8b6440eb44",
|
||||
"system/App/Frontend/Auth/Renderer.php": "94ed24082c9170c6f7ad09d1b59565c8b4251947979b6502f426d8adfff13efc",
|
||||
"system/App/Frontend/Auth/Renderer.php": "502cfc65cc32e653bae3be85a0a93299365fcec56143cc6e1c7f2bb4d53023ab",
|
||||
"system/App/Frontend/Auth/Tags.php": "92fc1c466e720be36a0adce5a735e47c94bf88d9a3e70ba9d62196ef63e639f5",
|
||||
"system/App/Frontend/Auth/TokenRepository.php": "5e09b154d4747bfb3d2795a9b449ae4aefffe47508edef92d1a7b392c0371207",
|
||||
"system/App/Frontend/Auth/UserRepository.php": "07a35cb3a82201465360b7f75e4be1534c88d53789ad2a5b1ef617be97a060e3",
|
||||
"system/App/Frontend/Auth/assets/auth.css": "a16949511a17462d9157945f2ceccb445254b34243e9e368796dd3137aac49c8",
|
||||
"system/App/Frontend/Auth/assets/auth.js": "840c7322fc21f9a8dd61af8cd16e0ceed36d6beaeac45d948bedbcb87b6012c4",
|
||||
"system/App/Frontend/Auth/view/login.twig": "1f33ca2d7b99d9ad60a82deb5e3904b7b1be1ef6a2c6c021ee565523ee0fe074",
|
||||
"system/App/Frontend/Auth/assets/auth.js": "a619f46dfbdf5dbe783a316ac5964a6ed40b89f872b3bd7a5dd88d5825c1acfe",
|
||||
"system/App/Frontend/Auth/view/login.twig": "87ca03cc43b778ded40a8a6c8481d0463977219c1b48424896a18fd6f0ec84cd",
|
||||
"system/App/Frontend/Auth/view/message.twig": "4e9a660af7d0deae9424a07550816c401902e75279414b11657e0eddf573deea",
|
||||
"system/App/Frontend/Auth/view/oauth.twig": "7ccb2c7ae36a378586334887c32fda96909cb6fa235601f1f697d4c8049af648",
|
||||
"system/App/Frontend/Auth/view/oauth_connections.twig": "e2d845ce9d906ca12d3f642e92ede1aa415203f7dc2ac472006f40e80eb8bf27",
|
||||
"system/App/Frontend/Auth/view/overview.twig": "97ad0cd5f72f63b6c92139f7cba54041840131975fdca417352dacf7f64ddb68",
|
||||
"system/App/Frontend/Auth/view/panel.twig": "3e7237263702462969b937254b6e2d4268599352f801ed4681db7b1aab07f508",
|
||||
"system/App/Frontend/Auth/view/panel.twig": "de638d18587b6c6671d1ba9db4f9eb3c8cc7765be560aee55ceda4625cd9eb50",
|
||||
"system/App/Frontend/Auth/view/password.twig": "099d7c1e48649faba57eb50df9bd9a091c79dad12e9c1fee9b38c3f2e025ed80",
|
||||
"system/App/Frontend/Auth/view/phone.twig": "8119ebc00ef866b80eadb559ce71b4d1c6511fc88523e41e7d3ac693db10dbdf",
|
||||
"system/App/Frontend/Auth/view/phone.twig": "f84dc89e54a9d2197cb6a9f8b3c4b838f008805e109061add189a43232dc427e",
|
||||
"system/App/Frontend/Auth/view/profile.twig": "71032aeeebc80036301181932c90ee004d9440bbc64b1b3f79ba03c0e3037a87",
|
||||
"system/App/Frontend/Auth/view/register.twig": "92f78ab66ba8c16e0b7c3727ac80ae239e01f696399b72049f19a1087d3f3cc9",
|
||||
"system/App/Frontend/Auth/view/remember.twig": "fb170509256e4ecf13ebe5f64cae8cbd6ad8ef143abe645f4bc1f269e0d8a0fe",
|
||||
@@ -1246,7 +1246,7 @@
|
||||
"system/App/Frontend/DocumentFieldRepository.php": "fc8e80455feca50e4bb5bff3566eb6814fcca1994f1167bd8dd7972f1c87ac69",
|
||||
"system/App/Frontend/DocumentPaginationRenderer.php": "560c9441fb758bc760b14f37bd88c39c36207c1717a18f43b5c57599a61231ed",
|
||||
"system/App/Frontend/DocumentRenderCache.php": "9dd0e9e666021e00eda8c4d09cf8f197f17468b8ad545b4d357654bf646bfc15",
|
||||
"system/App/Frontend/DocumentRepository.php": "8aa13746bc4cbb6ff1d1e0550a0cc94a6e5d9f3c0431a3f982668758d2ff1454",
|
||||
"system/App/Frontend/DocumentRepository.php": "f5388b21eee81b3536ef32f239a521dd3187410c9874adbdbdc416949409c27a",
|
||||
"system/App/Frontend/DocumentRevisionPreview.php": "372b751f673411722c2de5f04ee1fac57b745e327a2259916313781a3f175e89",
|
||||
"system/App/Frontend/DocumentRevisionRepository.php": "bbfb1c5a77c979b9998cc647a0e410150ca33727f6caf46f7c49f6ed01774f45",
|
||||
"system/App/Frontend/DocumentRouteRepository.php": "68b7a9c4972613c7fae886c74273cced26d5749d9242f0cf7463176c1ac0d854",
|
||||
@@ -1354,7 +1354,7 @@
|
||||
"system/language/ru/lang.xml": "7d46cc9822f064a1af4d6528ccfe2dd4a9cc318912e327fd807befd2ca3dfd11",
|
||||
"system/preload.php": "66bfb1aaf36ba85945b729b09a37e96ead0be1cc2749ebd9abd4b9da4f25ba0e",
|
||||
"system/public.php": "3f72b5bf44bc0ba5ba6fb6b3adc2e2f9d5e9902d9cdfa3d7b748f4aebd179b87",
|
||||
"system/release.php": "73af5bab9ab6ad78aa6c4fc4509dd4419cf68e66142381c1500c01a72b655c8e",
|
||||
"system/release.php": "313f3e82832ba2ed320647a9a3eee63b54cda5b390b8b1916fb9ceb7d2344764",
|
||||
"system/vendor/Captcha/fonts/palatino_linotype_bold.png": "38b6a1d4a4bd1d0c056205119c5e24176913ac0d898371f53a0339952a319365",
|
||||
"system/vendor/Captcha/fonts/perpetua_bold.png": "55dbbe97dae128dea47b5e8d640414b3d813119c854c688037a20a581ec8fdc4",
|
||||
"system/vendor/Captcha/fonts/times_bold.png": "d3d32ce1f418e3c3c3b1afe3b6acabadb36207bc12368f5e86420cf1a529ff8f",
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
use App\Adminx\Packages\Products\ProductCollections;
|
||||
use App\Adminx\Packages\Products\ProductDemands;
|
||||
use App\Adminx\Packages\Products\ProductDuplicator;
|
||||
use App\Adminx\Packages\Products\QualitySnapshot;
|
||||
use App\Adminx\Packages\Products\ProductReadiness;
|
||||
use App\Adminx\Packages\Products\PublicViewTemplates;
|
||||
use App\Adminx\Packages\Products\ShippingPackageTemplates;
|
||||
@@ -144,6 +145,28 @@
|
||||
));
|
||||
}
|
||||
|
||||
public function refreshProductQualitySnapshot(array $params = array())
|
||||
{
|
||||
if (($error = $this->csrfGuard()) !== null) { return $error; }
|
||||
if (!Permission::check('view_products')) { return $this->error('Недостаточно прав', array(), 403); }
|
||||
|
||||
try {
|
||||
$snapshot = QualitySnapshot::refresh();
|
||||
} catch (\Throwable $e) {
|
||||
error_log('[products.quality] ' . $e->getMessage());
|
||||
return $this->error('Не удалось рассчитать качество товаров', array(), 500);
|
||||
}
|
||||
|
||||
return $this->success('Сводка качества обновлена', array(
|
||||
'data' => array(
|
||||
'generated_label' => (string) $snapshot['generated_label'],
|
||||
'quality_total' => (int) $snapshot['stats']['quality_total'],
|
||||
'required' => (int) $snapshot['required'],
|
||||
'score' => (int) $snapshot['stats']['score'],
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function productComparison(array $params = array())
|
||||
{
|
||||
if (!Permission::check('view_products')) { return $this->renderStatus('@adminx/404.twig', array('title' => 'Недостаточно прав'), 403); }
|
||||
|
||||
@@ -398,6 +398,7 @@
|
||||
syncRegistrationGate: function () {
|
||||
var gate = document.querySelector('[data-registration-gate]');
|
||||
var hasEmail = gate && gate.value !== 'phone';
|
||||
var phoneOnly = gate && gate.value === 'phone';
|
||||
var emailAndPhone = gate && gate.value === 'email_phone';
|
||||
var description = document.querySelector('[data-auth-email-field-description]');
|
||||
var visibility = document.querySelector('[data-auth-email-visibility]');
|
||||
@@ -406,12 +407,16 @@
|
||||
document.querySelectorAll('[data-auth-email-registration], [data-auth-email-form-fields], [data-auth-email-settings]').forEach(function (section) {
|
||||
section.hidden = !hasEmail;
|
||||
});
|
||||
if (description) { description.textContent = emailAndPhone ? 'Запрашивается только при выборе регистрации по email' : 'Логин и канал подтверждения регистрации по email'; }
|
||||
if (visibility) { visibility.textContent = emailAndPhone ? 'В email-форме' : 'Показывается'; }
|
||||
if (description) { description.textContent = phoneOnly ? 'Не используется для входа; можно добавить в профиле как контакт' : (emailAndPhone ? 'Запрашивается только при выборе регистрации по email' : 'Логин и канал подтверждения регистрации по email'); }
|
||||
if (visibility) {
|
||||
visibility.textContent = phoneOnly ? 'Только в профиле' : (emailAndPhone ? 'В email-форме' : 'Показывается');
|
||||
visibility.classList.toggle('badge-blue', !phoneOnly);
|
||||
visibility.classList.toggle('badge-gray', phoneOnly);
|
||||
}
|
||||
if (required) {
|
||||
required.textContent = emailAndPhone ? 'По выбору способа' : 'Обязательно';
|
||||
required.classList.toggle('badge-blue', !emailAndPhone);
|
||||
required.classList.toggle('badge-gray', emailAndPhone);
|
||||
required.textContent = phoneOnly ? 'Необязательно' : (emailAndPhone ? 'По выбору способа' : 'Обязательно');
|
||||
required.classList.toggle('badge-blue', !phoneOnly && !emailAndPhone);
|
||||
required.classList.toggle('badge-gray', phoneOnly || emailAndPhone);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
use App\Adminx\Customers\GlobalSearchProvider;
|
||||
|
||||
return array(
|
||||
'code' => 'customers', 'name' => 'Пользователи сайта', 'version' => '0.10.2',
|
||||
'code' => 'customers', 'name' => 'Пользователи сайта', 'version' => '0.10.3',
|
||||
'permissions' => array('key' => 'customers', 'items' => array(
|
||||
array(
|
||||
'code' => 'view_customers',
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
<section class="card customers-auth-section">
|
||||
<div class="ax-list-head"><div class="ax-list-title"><span class="icon-tile" style="--tile-bg:var(--green-100);--tile-fg:var(--green-600)"><i class="ti ti-user-plus"></i></span><div><h2>Создание аккаунта</h2><p class="text-secondary">Кто может зарегистрироваться и когда станет доступен вход.</p></div></div><label class="switch" data-tooltip="Разрешить регистрацию"><input type="checkbox" name="registration_enabled" value="1"{{ auth_settings.registration_enabled ? ' checked' : '' }}{{ can_manage ? '' : ' disabled' }}><span></span></label></div>
|
||||
<div class="customers-auth-settings-grid">
|
||||
<label class="field"><span class="field-label">Способы регистрации</span><select class="select" name="registration_gate" data-registration-gate{{ can_manage ? '' : ' disabled' }}><option value="email"{{ auth_settings.registration_gate=='email' ? ' selected' : '' }}>По email</option><option value="phone"{{ auth_settings.registration_gate=='phone' ? ' selected' : '' }}>По телефону</option><option value="email_phone"{{ auth_settings.registration_gate=='email_phone' ? ' selected' : '' }}>По email или телефону</option></select><span class="field-hint">Телефонная регистрация появится после подключения SMS-провайдера. Email для такого аккаунта не требуется.</span></label>
|
||||
<label class="field"><span class="field-label">Способ входа и регистрации</span><select class="select" name="registration_gate" data-registration-gate{{ can_manage ? '' : ' disabled' }}><option value="email"{{ auth_settings.registration_gate=='email' ? ' selected' : '' }}>По email и паролю</option><option value="phone"{{ auth_settings.registration_gate=='phone' ? ' selected' : '' }}>По телефону и SMS-коду</option><option value="email_phone"{{ auth_settings.registration_gate=='email_phone' ? ' selected' : '' }}>Оба способа</option></select><span class="field-hint">В телефонном режиме email остаётся необязательным контактом профиля и не используется для публичного входа.</span></label>
|
||||
<label class="field" data-auth-email-registration><span class="field-label">Активация регистрации по email</span><select class="select" name="registration_mode" data-registration-mode{{ can_manage ? '' : ' disabled' }}><option value="email"{{ auth_settings.registration_mode=='email' ? ' selected' : '' }}>После подтверждения email</option><option value="now"{{ auth_settings.registration_mode=='now' ? ' selected' : '' }}>Сразу после регистрации</option><option value="byadmin"{{ auth_settings.registration_mode=='byadmin' ? ' selected' : '' }}>После одобрения администратором</option></select><span class="field-hint">Телефон уже подтверждается одноразовым SMS-кодом.</span></label>
|
||||
<label class="field"><span class="field-label">Группа после регистрации</span><select class="select" name="default_group"{{ can_manage ? '' : ' disabled' }}>{% for group in customer_groups %}<option value="{{ group.id }}"{{ auth_settings.default_group==group.id ? ' selected' : '' }}>#{{ group.id }} · {{ group.name }} · {{ group.permissions_count }} разрешений</option>{% endfor %}</select><span class="field-hint">Вы сами выбираете группу, которая будет назначаться новым пользователям сайта.</span></label>
|
||||
</div>
|
||||
@@ -83,7 +83,7 @@
|
||||
<div class="ax-list-head"><div class="ax-list-title"><span class="icon-tile" style="--tile-bg:var(--cyan-100);--tile-fg:var(--cyan-600)"><i class="ti ti-forms"></i></span><div><h2>Поля регистрации по email</h2><p class="text-secondary">Для регистрации по телефону достаточно номера и SMS-кода. Остальные данные пользователь заполнит в профиле.</p></div></div></div>
|
||||
<div class="customers-auth-fields" data-auth-fields>
|
||||
<div class="customers-auth-fields-head"><span>Поле</span><span>Показывать</span><span>Обязательное</span></div>
|
||||
<div class="customers-auth-field-row"><span><i class="ti ti-mail"></i><span><b>Email</b><small data-auth-email-field-description>{{ auth_settings.registration_gate == 'email_phone' ? 'Запрашивается только при выборе регистрации по email' : 'Логин и канал подтверждения регистрации по email' }}</small></span></span><span class="badge badge-blue" data-auth-email-visibility>{{ auth_settings.registration_gate == 'email_phone' ? 'В email-форме' : 'Показывается' }}</span><span class="badge {{ auth_settings.registration_gate == 'email_phone' ? 'badge-gray' : 'badge-blue' }}" data-auth-email-required>{{ auth_settings.registration_gate == 'email_phone' ? 'По выбору способа' : 'Обязательно' }}</span></div>
|
||||
<div class="customers-auth-field-row"><span><i class="ti ti-mail"></i><span><b>Email</b><small data-auth-email-field-description>{{ auth_settings.registration_gate == 'phone' ? 'Не используется для входа; можно добавить в профиле как контакт' : (auth_settings.registration_gate == 'email_phone' ? 'Запрашивается только при выборе регистрации по email' : 'Логин и канал подтверждения регистрации по email') }}</small></span></span><span class="badge {{ auth_settings.registration_gate == 'phone' ? 'badge-gray' : 'badge-blue' }}" data-auth-email-visibility>{{ auth_settings.registration_gate == 'phone' ? 'Только в профиле' : (auth_settings.registration_gate == 'email_phone' ? 'В email-форме' : 'Показывается') }}</span><span class="badge {{ auth_settings.registration_gate == 'email' ? 'badge-blue' : 'badge-gray' }}" data-auth-email-required>{{ auth_settings.registration_gate == 'phone' ? 'Необязательно' : (auth_settings.registration_gate == 'email_phone' ? 'По выбору способа' : 'Обязательно') }}</span></div>
|
||||
<div class="customers-auth-field-row"><span><i class="ti ti-user"></i><span><b>Имя</b><small>Основное имя пользователя</small></span></span><span class="badge badge-blue">Всегда</span><label class="switch"><input type="checkbox" name="require_firstname" value="1"{{ auth_settings.require_firstname ? ' checked' : '' }}{{ can_manage ? '' : ' disabled' }}><span></span></label></div>
|
||||
<div class="customers-auth-field-row" data-auth-field-row><span><i class="ti ti-id"></i><span><b>Фамилия</b><small>Дополняет отображаемое имя</small></span></span><label class="switch"><input type="checkbox" name="show_lastname" value="1" data-auth-show{{ auth_settings.show_lastname ? ' checked' : '' }}{{ can_manage ? '' : ' disabled' }}><span></span></label><label class="switch"><input type="checkbox" name="require_lastname" value="1" data-auth-required{{ auth_settings.require_lastname ? ' checked' : '' }}{{ can_manage ? '' : ' disabled' }}><span></span></label></div>
|
||||
<div class="customers-auth-field-row" data-auth-field-row><span><i class="ti ti-phone"></i><span><b>Телефон</b><small>Контакт для заказов и доставки</small></span></span><label class="switch"><input type="checkbox" name="show_phone" value="1" data-auth-show{{ auth_settings.show_phone ? ' checked' : '' }}{{ can_manage ? '' : ' disabled' }}><span></span></label><label class="switch"><input type="checkbox" name="require_phone" value="1" data-auth-required{{ auth_settings.require_phone ? ' checked' : '' }}{{ can_manage ? '' : ' disabled' }}><span></span></label></div>
|
||||
|
||||
@@ -78,6 +78,18 @@
|
||||
return $gate === $method || $gate === 'email_phone';
|
||||
}
|
||||
|
||||
/** Password login is disabled when public access is configured as phone-only. */
|
||||
public static function allowsPasswordLogin(array $settings = null)
|
||||
{
|
||||
$settings = $settings === null ? self::all() : $settings;
|
||||
$gate = isset($settings['registration_gate'])
|
||||
? strtolower(trim((string) $settings['registration_gate']))
|
||||
: 'email';
|
||||
if ($gate === 'both') { $gate = 'email_phone'; }
|
||||
|
||||
return $gate !== 'phone';
|
||||
}
|
||||
|
||||
protected static function defaults(array $fallback)
|
||||
{
|
||||
return array(
|
||||
|
||||
@@ -47,6 +47,10 @@
|
||||
return $this->redirect(Feature::url('overview'));
|
||||
}
|
||||
|
||||
if (!Feature::passwordLoginEnabled()) {
|
||||
return $this->loginError('Вход по паролю отключён. Используйте код из SMS.', 403);
|
||||
}
|
||||
|
||||
if (!$this->verifyCsrf()) {
|
||||
return $this->loginError('Сессия устарела. Обновите страницу.', 403);
|
||||
}
|
||||
|
||||
@@ -100,6 +100,11 @@
|
||||
}));
|
||||
}
|
||||
|
||||
public static function passwordLoginEnabled()
|
||||
{
|
||||
return PublicAuthSettings::allowsPasswordLogin(self::$config);
|
||||
}
|
||||
|
||||
public static function page($key)
|
||||
{
|
||||
return isset(self::$config['pages'][$key]) ? self::$config['pages'][$key] : array();
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
$displayUser = $user ?: $systemUser;
|
||||
$accountLinks = Hooks::filter('auth.account.links', array());
|
||||
if (!is_array($accountLinks)) { $accountLinks = array(); }
|
||||
$html = (new FormTemplateRepository())->render('panel', array(
|
||||
$context = array(
|
||||
'base' => $base,
|
||||
'csrf' => Session::csrfToken(),
|
||||
'return_url' => self::returnUrl(),
|
||||
@@ -49,20 +49,26 @@
|
||||
'admin_url' => AdminLocation::url('/'),
|
||||
'registration_enabled' => Feature::registrationFormEnabled(),
|
||||
'password_reset_enabled' => !empty(Feature::config()['password_reset_enabled']),
|
||||
'password_login_enabled' => Feature::passwordLoginEnabled(),
|
||||
'auth_urls' => Feature::urls(),
|
||||
'login_page' => Feature::page('login'),
|
||||
'oauth_providers' => ProviderRegistry::publicItems(),
|
||||
'phone_providers' => PhoneProviderRegistry::publicItems(),
|
||||
'account_extension_links' => $accountLinks,
|
||||
'error' => (string) $error,
|
||||
));
|
||||
);
|
||||
$html = (new FormTemplateRepository())->render('panel', $context);
|
||||
if (empty($context['password_login_enabled'])
|
||||
&& preg_match('/name\s*=\s*["\']user_login["\']/i', $html)) {
|
||||
$html = Twig::twig()->render('@system_auth/panel.twig', $context);
|
||||
}
|
||||
|
||||
if (!$includeAssets) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
return '<link rel="stylesheet" href="' . $base . '/system/App/Frontend/Auth/assets/auth.css">'
|
||||
. '<script src="' . $base . '/system/App/Frontend/Auth/assets/auth.js" defer></script>'
|
||||
return '<link rel="stylesheet" href="' . $base . '/system/App/Frontend/Auth/assets/auth.css?v=' . self::assetVersion('auth.css') . '">'
|
||||
. '<script src="' . $base . '/system/App/Frontend/Auth/assets/auth.js?v=' . self::assetVersion('auth.js') . '" defer></script>'
|
||||
. $html;
|
||||
}
|
||||
|
||||
@@ -80,11 +86,17 @@
|
||||
'preview' => false,
|
||||
'oauth_providers' => ProviderRegistry::publicItems(),
|
||||
'phone_providers' => PhoneProviderRegistry::publicItems(),
|
||||
'password_login_enabled' => Feature::passwordLoginEnabled(),
|
||||
'email_registration_enabled' => Feature::emailRegistrationEnabled(),
|
||||
'phone_registration_enabled' => Feature::phoneRegistrationEnabled(),
|
||||
'registration_phone_providers' => Feature::phoneRegistrationProviders(),
|
||||
), $data);
|
||||
$html = (new FormTemplateRepository())->render($template, $context);
|
||||
if ($template === 'login' && empty($context['password_login_enabled'])
|
||||
&& preg_match('/name\s*=\s*["\']user_login["\']/i', $html)) {
|
||||
$html = Twig::twig()->render('@system_auth/login.twig', $context);
|
||||
}
|
||||
|
||||
if ($template === 'register' && empty($context['email_registration_enabled'])
|
||||
&& preg_match('/name\s*=\s*["\']email["\']/i', $html)) {
|
||||
$html = Twig::twig()->render('@system_auth/register.twig', $context);
|
||||
@@ -120,8 +132,8 @@
|
||||
$html = '<div class="system-auth-preview"><b>Предпросмотр формы</b><span>Отправка данных отключена.</span></div>' . $html;
|
||||
}
|
||||
|
||||
$html = '<link rel="stylesheet" href="' . $base . '/system/App/Frontend/Auth/assets/auth.css">'
|
||||
. '<script src="' . $base . '/system/App/Frontend/Auth/assets/auth.js" defer></script>'
|
||||
$html = '<link rel="stylesheet" href="' . $base . '/system/App/Frontend/Auth/assets/auth.css?v=' . self::assetVersion('auth.css') . '">'
|
||||
. '<script src="' . $base . '/system/App/Frontend/Auth/assets/auth.js?v=' . self::assetVersion('auth.js') . '" defer></script>'
|
||||
. $html;
|
||||
PublicModuleRuntime::deferPage($html, array(
|
||||
'title' => isset($context['title']) ? (string) $context['title'] : (isset($page['title']) ? (string) $page['title'] : ''),
|
||||
@@ -132,6 +144,13 @@
|
||||
return '';
|
||||
}
|
||||
|
||||
protected static function assetVersion($name)
|
||||
{
|
||||
$file = __DIR__ . '/assets/' . basename((string) $name);
|
||||
$modified = is_file($file) ? @filemtime($file) : false;
|
||||
return $modified === false ? '1' : (string) $modified;
|
||||
}
|
||||
|
||||
protected static function returnUrl()
|
||||
{
|
||||
$query = Request::queryString();
|
||||
|
||||
@@ -59,6 +59,41 @@
|
||||
message.classList.toggle('is-error', !!error);
|
||||
}
|
||||
|
||||
function phoneDigits(value) {
|
||||
var digits = String(value || '').replace(/\D/g, '');
|
||||
if (digits.charAt(0) === '8') { digits = '7' + digits.slice(1); }
|
||||
if (digits.charAt(0) !== '7') { digits = '7' + digits; }
|
||||
return digits.slice(0, 11);
|
||||
}
|
||||
|
||||
function formatPhone(value) {
|
||||
var digits = phoneDigits(value);
|
||||
var local = digits.slice(1);
|
||||
var result = '+7';
|
||||
if (local.length > 0) { result += ' ' + local.slice(0, 3); }
|
||||
if (local.length > 3) { result += ' ' + local.slice(3, 6); }
|
||||
if (local.length > 6) { result += '-' + local.slice(6, 8); }
|
||||
if (local.length > 8) { result += '-' + local.slice(8, 10); }
|
||||
return result;
|
||||
}
|
||||
|
||||
function applyPhoneMask(input) {
|
||||
if (!input) { return; }
|
||||
input.value = formatPhone(input.value);
|
||||
}
|
||||
|
||||
document.addEventListener('focusin', function (event) {
|
||||
var input = event.target.closest('[data-phone-mask]');
|
||||
if (!input) { return; }
|
||||
applyPhoneMask(input);
|
||||
});
|
||||
|
||||
document.addEventListener('input', function (event) {
|
||||
var input = event.target.closest('[data-phone-mask]');
|
||||
if (!input) { return; }
|
||||
applyPhoneMask(input);
|
||||
});
|
||||
|
||||
function phoneCountdown(root, seconds) {
|
||||
var button = root.querySelector('[data-phone-auth-resend]');
|
||||
var remaining = Math.max(0, Number(seconds) || 0);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<div class="static_page_inner">
|
||||
{% if page.description %}<p class="text-muted">{{ page.description }}</p>{% endif %}
|
||||
{% if error %}<div class="alert alert-danger" data-auth-error role="alert" tabindex="-1">{{ error }}</div>{% endif %}
|
||||
{% if password_login_enabled %}
|
||||
<form method="post" action="{{ base }}{{ auth_urls.login }}" class="system-public-auth-form system-public-auth-form--compact" data-auth-login>
|
||||
<input type="hidden" name="_csrf" value="{{ csrf|e }}">
|
||||
<input type="hidden" name="_return" value="{{ return_url|default('/')|e }}">
|
||||
@@ -12,6 +13,7 @@
|
||||
<div class="system-public-auth-actions"><button class="btn btn-primary" type="submit">{{ page.submit_label }}</button>{% if config.password_reset_enabled %}<a href="{{ base }}{{ auth_urls.remember }}">Забыли пароль?</a>{% endif %}</div>
|
||||
{% if registration_enabled %}<p class="system-public-auth-secondary">Нет аккаунта? <a href="{{ base }}{{ auth_urls.register }}">Зарегистрироваться</a></p>{% endif %}
|
||||
</form>
|
||||
{% endif %}
|
||||
{% include '@system_auth/phone.twig' %}
|
||||
{% include '@system_auth/oauth.twig' %}
|
||||
</div>
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<li>
|
||||
<div class="panel m-b-none">
|
||||
<div class="panel-body">
|
||||
{% if password_login_enabled %}
|
||||
<form class="form" method="post" action="{{ base }}{{ auth_urls.login }}" accept-charset="UTF-8" id="form-login-top" data-auth-login>
|
||||
<input type="hidden" name="_csrf" value="{{ csrf|e }}">
|
||||
<input type="hidden" name="_return" value="{{ return_url|e }}">
|
||||
@@ -48,6 +49,9 @@
|
||||
<div class="form-group"><button type="submit" class="btn btn-purple btn-sm btn-block">{{ login_page.submit_label }}</button></div>
|
||||
<div class="checkbox"><label><input type="checkbox" name="keep_in" value="1"> Запомнить меня</label></div>
|
||||
</form>
|
||||
{% else %}
|
||||
<a class="btn btn-purple btn-sm btn-block" href="{{ base }}{{ auth_urls.login }}">Войти по телефону</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if registration_enabled %}
|
||||
<div class="panel-footer bottom text-center">Нет аккаунта? <a href="{{ base }}{{ auth_urls.register }}"><b>Зарегистрироваться</b></a></div>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{% if phone_providers %}
|
||||
{% set phone_provider = phone_providers|first %}
|
||||
<div class="system-public-auth-phone" data-phone-auth data-provider="{{ phone_provider.code }}">
|
||||
<div class="system-public-auth-oauth-divider"><span>{{ phone_auth_registration|default(false) ? (email_registration_enabled ? 'или зарегистрируйтесь по телефону' : 'Регистрация по телефону') : 'или войдите по телефону' }}</span></div>
|
||||
<div class="system-public-auth-oauth-divider"><span>{{ phone_auth_registration|default(false) ? (email_registration_enabled ? 'или зарегистрируйтесь по телефону' : 'Регистрация по телефону') : ((password_login_enabled is defined and not password_login_enabled) ? 'Вход по номеру телефона' : 'или войдите по телефону') }}</span></div>
|
||||
<form action="{{ base }}/auth/phone/request" method="post" data-phone-auth-request>
|
||||
<input type="hidden" name="_csrf" value="{{ csrf|e }}">
|
||||
<input type="hidden" name="provider" value="{{ phone_provider.code|e }}">
|
||||
<input type="hidden" name="_return" value="{{ return_url|default('/')|e }}">
|
||||
<div class="system-public-auth-phone-row">
|
||||
<label class="sr-only" for="phone-auth-number">Номер телефона</label>
|
||||
<input class="form-control" id="phone-auth-number" type="tel" name="phone" autocomplete="tel" inputmode="tel" placeholder="+7 900 000-00-00" required>
|
||||
<input class="form-control" id="phone-auth-number" type="tel" name="phone" autocomplete="tel" inputmode="tel" placeholder="+7 000 000-00-00" maxlength="16" pattern="[+]7 [0-9]{3} [0-9]{3}-[0-9]{2}-[0-9]{2}" data-phone-mask required>
|
||||
<button class="btn btn-secondary" type="submit">Получить код</button>
|
||||
</div>
|
||||
{% if phone_provider.allow_registration and phone_registration_enabled %}
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
|
||||
public function findNotFoundPage($documentId, $userGroupId)
|
||||
{
|
||||
return $this->findWithPageContext($documentId, $userGroupId, false);
|
||||
return $this->findForPage($documentId, $userGroupId);
|
||||
}
|
||||
|
||||
protected function findWithPageContext($documentId, $userGroupId, $includeAlternateTemplate)
|
||||
|
||||
+1
-1
@@ -17,5 +17,5 @@
|
||||
return array(
|
||||
'product' => 'AVE.cms',
|
||||
'version' => '3.3',
|
||||
'build' => '0.39',
|
||||
'build' => '0.40',
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user