diff --git a/AGENTS.md b/AGENTS.md
index 53e8dee662..4434ce4613 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -178,6 +178,11 @@ under one policy. Do not add a restore path that skips it:
prefixes `custom/` to path repositories and to `extra.merge-plugin.include` / `require`, a list or a
single path) and removed again before `moveFiles()`, which
would otherwise lay it over the site's own.
+- `make:site` scopes Composer authentication through `ComposerEnvironment`: keep the normal
+ Composer home (explicit, user/XDG or Windows), with `core/composer` only as the no-home fallback.
+ Its legacy auth file supplies fallback credentials; normal global config/auth, `core/auth.json`,
+ then `COMPOSER_AUTH` take precedence. Preflight and restore inherit the same environment;
+ never copy credentials into the archive or shell arguments, and restore the caller's environment.
- The merge plugin runs with `"replace": true`: a custom constraint replaces the core's own, so a
custom requirement on a core package is checked against the lock like any other (`pins()`).
- A restore that fails leaves the installers' update incomplete: the CLI exits with 1 and keeps `install/`,
diff --git a/core/database/seeders/UserPermissionsTableSeeder.php b/core/database/seeders/UserPermissionsTableSeeder.php
index ab08808ab7..cfde7beb2a 100644
--- a/core/database/seeders/UserPermissionsTableSeeder.php
+++ b/core/database/seeders/UserPermissionsTableSeeder.php
@@ -116,13 +116,6 @@ public function run(): void
'disabled' => 0,
'group_id' => 1
],
- [
- 'name' => 'View action completed screen',
- 'lang_key' => 'role_actionok',
- 'key' => 'action_ok',
- 'disabled' => 1,
- 'group_id' => 1
- ],
[
'name' => 'View error dialog',
'lang_key' => 'role_errors',
@@ -549,6 +542,13 @@ public function run(): void
'disabled' => 0,
'group_id' => 14
],
+ [
+ 'name' => 'Change security settings',
+ 'key' => 'security_settings',
+ 'lang_key' => 'role_security_settings',
+ 'disabled' => 0,
+ 'group_id' => 14
+ ],
[
'name' => 'Use the Backup Manager',
'key' => 'bk_manager',
diff --git a/core/database/seeders/UserRolesTableSeeder.php b/core/database/seeders/UserRolesTableSeeder.php
index d20d3268ef..234a39a6f1 100644
--- a/core/database/seeders/UserRolesTableSeeder.php
+++ b/core/database/seeders/UserRolesTableSeeder.php
@@ -71,7 +71,7 @@ public function run(): void
[
'id' => 3,
'name' => 'Publisher',
- 'description' => 'Editor with expanded permissions including manage users, update Elements and site settings',
+ 'description' => 'Editor with expanded permissions including manage users and update Elements',
]
]);
@@ -81,7 +81,6 @@ public function run(): void
['permission' => 'home', 'role_id' => 1],
['permission' => 'widget_recent_info', 'role_id' => 1],
['permission' => 'help', 'role_id' => 1],
- ['permission' => 'role_actionok', 'role_id' => 1],
['permission' => 'error_dialog', 'role_id' => 1],
['permission' => 'about', 'role_id' => 1],
['permission' => 'widget_online_info', 'role_id' => 1],
@@ -140,6 +139,7 @@ public function run(): void
['permission' => 'delete_eventlog', 'role_id' => 1],
['permission' => 'logs', 'role_id' => 1],
['permission' => 'settings', 'role_id' => 1],
+ ['permission' => 'security_settings', 'role_id' => 1],
['permission' => 'bk_manager', 'role_id' => 1],
['permission' => 'remove_locks', 'role_id' => 1],
['permission' => 'display_locks', 'role_id' => 1],
@@ -152,7 +152,6 @@ public function run(): void
['permission' => 'home', 'role_id' => 2],
['permission' => 'widget_recent_info', 'role_id' => 2],
['permission' => 'help', 'role_id' => 2],
- ['permission' => 'role_actionok', 'role_id' => 2],
['permission' => 'error_dialog', 'role_id' => 2],
['permission' => 'about', 'role_id' => 2],
['permission' => 'widget_online_info', 'role_id' => 2],
@@ -187,7 +186,6 @@ public function run(): void
['permission' => 'home', 'role_id' => 3],
['permission' => 'widget_recent_info', 'role_id' => 3],
['permission' => 'help', 'role_id' => 3],
- ['permission' => 'role_actionok', 'role_id' => 3],
['permission' => 'error_dialog', 'role_id' => 3],
['permission' => 'about', 'role_id' => 3],
['permission' => 'widget_online_info', 'role_id' => 3],
@@ -221,8 +219,6 @@ public function run(): void
['permission' => 'save_user', 'role_id' => 3],
['permission' => 'delete_user', 'role_id' => 3],
['permission' => 'logs', 'role_id' => 3],
- ['permission' => 'settings', 'role_id' => 3],
- ['permission' => 'bk_manager', 'role_id' => 3],
['permission' => 'remove_locks', 'role_id' => 3],
['permission' => 'display_locks', 'role_id' => 3],
['permission' => 'access_permissions', 'role_id' => 3],
diff --git a/core/functions/actions/files.php b/core/functions/actions/files.php
index 36ea2feb59..240bf039e6 100644
--- a/core/functions/actions/files.php
+++ b/core/functions/actions/files.php
@@ -104,6 +104,31 @@ function fileManagerDenied($reason = '', $path = null)
}
}
+if(!function_exists('fileManagerRoot')) {
+ /**
+ * The folder the current manager's file manager works in. A manager's own filemanager_path
+ * (a user setting) may narrow the site-wide root, never widen it: it is used only when
+ * fileManagerCanonicalPath(), the rule every path below the root is held to, names it inside
+ * the site-wide root. Otherwise the site-wide root is used, and when that does not resolve
+ * either the answer is null: callers refuse rather than fall back to the site folder.
+ *
+ * @return string|null forward slashes, no trailing slash
+ */
+ function fileManagerRoot()
+ {
+ $evo = evolutionCMS();
+ $system = $evo->configGlobal['filemanager_path'] ?? $evo->getConfig('filemanager_path');
+ $system = realpath(str_replace('[(base_path)]', EVO_BASE_PATH, (string) $system));
+ if ($system === false) {
+ return null;
+ }
+ $system = rtrim(str_replace('\\', '/', $system), '/');
+ $own = fileManagerCanonicalPath($evo->getConfig('filemanager_path'));
+
+ return $own !== false && FileManagerAccess::isWithin($system, $own) ? $own : $system;
+ }
+}
+
if(!function_exists('fileManagerAclKey')) {
/**
* The file_groups key of a path given relative to the current manager's file manager root.
@@ -117,8 +142,8 @@ function fileManagerAclKey($relativePath)
{
static $roots = null;
if ($roots === null) {
- $userRoot = fileManagerCanonicalPath(evolutionCMS()->getConfig('filemanager_path')) ?: realpath(EVO_BASE_PATH);
- $roots = [FileManagerAccess::aclRoot(), rtrim(str_replace('\\', '/', $userRoot), '/')];
+ $aclRoot = FileManagerAccess::aclRoot();
+ $roots = [$aclRoot, fileManagerRoot() ?? $aclRoot];
}
return FileManagerAccess::aclKey($roots[0], $roots[1], $relativePath);
diff --git a/core/lang/az/global.php b/core/lang/az/global.php
index b578033e64..bb5b542443 100644
--- a/core/lang/az/global.php
+++ b/core/lang/az/global.php
@@ -343,6 +343,7 @@
$_lang["role_edit_plugin"] = 'Plaqin redaktə et';
$_lang["role_edit_role"] = 'Rol redaktə et';
$_lang["role_edit_settings"] = 'Sayt ayarlarını dəyiş';
+$_lang["role_security_settings"] = 'Təhlükəsizlik ayarlarını dəyiş';
$_lang["role_edit_snippet"] = 'Snippet redaktə et';
$_lang["role_edit_template"] = 'Şablon redaktə et';
$_lang["role_edit_user"] = 'İstifadəçi redaktə et';
@@ -357,6 +358,7 @@
$_lang["role_help"] = 'Kömək səhifələrinə baxış';
$_lang["role_home"] = 'Menecerin giriş səhifəsinə keçid';
$_lang["role_import_static"] = 'HTML idxalı';
+$_lang["role_in_use_message"] = 'Bu rol hələ də bir və ya bir neçə istifadəçiyə təyin edilib. Silməzdən əvvəl həmin istifadəçiləri başqa rola təyin edin.';
$_lang["role_widget_recent_info"] = 'Son redaktə olunan/yaradılan resurslar vidcetinə bax';
$_lang["role_list_module"] = 'Modulların siyahısı';
$_lang["role_manage_metatags"] = 'Saytın META teqlərini və açar sözlərini idarə et';
@@ -515,6 +517,7 @@
$_lang["access_permissions_user_toggle"] = 'Giriş icazələrini dəyiş';
$_lang["access_permissions_user_groups"] = 'İstifadəçi qrupları';
$_lang["access_permissions_user_message"] = 'Bu istifadəçinin aid olduğu istifadəçi qruplarını seçin:';
+$_lang["access_permissions_user_group_locked"] = 'Boz rəngdə göstərilən qruplar sizin üzvü olmadığınız qruplardır. Onları yalnız administrator və ya "Sənəd və istifadəçi qruplarını idarə et" icazəsinə malik menecer dəyişə bilər, ona görə burada yalnız oxumaq üçündür.';
$_lang["access_permissions_users_in_group"] = 'Qrupdakı istifadəçilər:';
$_lang["access_permissions_users_tab"] = 'Mövcud istifadəçi qruplarını görün. Yeni qruplar yaradın, adlarını dəyişin, qrupları silin və hansı istifadəçilərin hansı qruplara aid olduğunu görün. İstifadəçini qrupa əlavə etmək və ya çıxarmaq üçün onu birbaşa redaktə edin. Administratorlar (ID-si 1 olan rol təyin edilmiş istifadəçilər) bütün resurslara avtomatik giriş əldə edirlər və onlara xüsusi qruplar təyin etməyə ehtiyac yoxdur.';
@@ -1075,6 +1078,7 @@
$_lang["preview"] = 'Önizləmə';
$_lang["preview_msg"] = 'Bu, son saxlanılan dəyişikliklərinizin önizləməsidir. Buraya klikləyin: Saxla və Yenilə cari dəyişikliklərinizi.';
$_lang["preview_resource"] = 'Resursun önizləməsi';
+$_lang["preview_unsupported_url"] = 'Bu keçidə önizləmə mümkün deyil: idarəetmə panelindən yalnız http və https ünvanları açılır.';
$_lang["private"] = 'Şəxsi';
$_lang["public"] = 'İctimai';
diff --git a/core/lang/be/global.php b/core/lang/be/global.php
index c6860b1884..687d2d58f3 100644
--- a/core/lang/be/global.php
+++ b/core/lang/be/global.php
@@ -331,6 +331,7 @@
$_lang["role_edit_plugin"] = 'Рэдагаваць плагін';
$_lang["role_edit_role"] = 'Рэдагаваць ролі';
$_lang["role_edit_settings"] = 'Змяніць налады сайта';
+$_lang["role_security_settings"] = 'Змяніць налады бяспекі';
$_lang["role_edit_snippet"] = 'Рэдагаваць сніпеты';
$_lang["role_edit_template"] = 'Рэдагаваць шаблоны сайтаў';
$_lang["role_edit_user"] = 'Рэдагаваць карыстальнікаў';
@@ -345,6 +346,7 @@
$_lang["role_help"] = 'Прагляд старонкі даведкі';
$_lang["role_home"] = 'Уводная старонка менеджэра запытаў';
$_lang["role_import_static"] = 'Імпарт HTML';
+$_lang["role_in_use_message"] = 'Гэтая роля ўсё яшчэ прызначана аднаму ці некалькім карыстальнікам. Перш чым яе выдаліць, прызначце гэтым карыстальнікам іншую ролю.';
$_lang["role_widget_recent_info"] = 'Прагляд віджэта «Нядаўна рэдагаваныя/створаныя рэсурсы»';
$_lang["role_list_module"] = 'Модуль спісу';
$_lang["role_manage_metatags"] = 'Кіруйце мета-тэгамі і ключавымі словамі сайта';
@@ -503,6 +505,7 @@
$_lang["access_permissions_user_toggle"] = 'Пераключыць правы доступу карыстальніка';
$_lang["access_permissions_user_groups"] = 'Групы карыстальнікаў';
$_lang["access_permissions_user_message"] = 'Выберыце, да якіх груп карыстальнікаў павінен належаць гэты карыстальнік:';
+$_lang["access_permissions_user_group_locked"] = 'Групы, паказаныя шэрым, знаходзяцца па-за вашым уласным сяброўствам. Змяніць іх можа толькі адміністратар або менеджэр з дазволам "Кіраванне дакументамі і групамі карыстальнікаў", таму тут яны даступныя толькі для чытання.';
$_lang["access_permissions_users_in_group"] = 'Карыстальнікі ў групе:';
$_lang["access_permissions_users_tab"] = 'Прагляд створаных груп карыстальнікаў. Таксама вы можаце ствараць новыя групы, змяняць іх назвы, выдаляць групы і бачыць, якія карыстальнікі ўваходзяць у розныя групы. Каб дадаць новага карыстальніка ў групу або выдаліць карыстальніка з групы, адрэдагуйце карыстальніка непасрэдна. Адміністратары (карыстальнікі з роляй ID 1) заўсёды маюць доступ да ўсіх рэсурсаў, таму іх не трэба дадаваць у якія-небудзь групы.';
@@ -1061,6 +1064,7 @@
$_lang["preview"] = 'Папярэдні прагляд';
$_lang["preview_msg"] = 'Гэта папярэдні прагляд вашых апошніх захаваных зменаў. Націсніце тут, каб захаваць і абнавіць бягучыя змены';
$_lang["preview_resource"] = 'Рэсурс для папярэдняга прагляду';
+$_lang["preview_unsupported_url"] = 'Гэтую спасылку немагчыма папярэдне праглядзець: з мэнэджэра адкрываюцца толькі адрасы http і https.';
$_lang["private"] = 'Прыватны';
$_lang["public"] = 'Грамадскі';
diff --git a/core/lang/bg/global.php b/core/lang/bg/global.php
index 318597ae69..c354c6ff4d 100644
--- a/core/lang/bg/global.php
+++ b/core/lang/bg/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Тук можете да видите коя Група Документи е настроена. Също така можете да създадете Нова Група, преименувате Групи, изтриете Групи и да видите кои Документи са в различните Групи (сложете мишката върху id на Документа, за да видите неговото име). За да добавите или махнете Документ от групата, редактирайте директно.';
$_lang["access_permissions_user_groups"] = 'Потребителски групи';
$_lang["access_permissions_user_message"] = 'Тук можете да изберете към кои Потребителски Групи принадлежи този Потребител:';
+$_lang["access_permissions_user_group_locked"] = 'Групите, показани в сиво, са извън вашето собствено членство. Само администратор или мениджър с правото "Управление на групи документи и потребители" може да ги променя, затова тук са само за четене.';
$_lang["access_permissions_users_in_group"] = 'Потребители в група: ';
$_lang["access_permissions_users_tab"] = 'Тук можете да видите коя Група Потребители е настроена. Можете също да създавате Нови Групи, да преименувате Групи, да изтривате Групи и да видите Потребителите в различните Групи. За да добавите или премахнете Нов Потребител в дадена Група, редактирайте Потребителя директно. Администраторите (потребители, които имам присвоена Роля с ID 1) винаги имат достъп до всички документи, така че не е необходимо те да бъдат добавяни в каквато и да е група.';
$_lang["account_email"] = 'Емейл на акаунта';
@@ -687,6 +688,7 @@
$_lang["preview"] = 'Преглед';
$_lang["preview_msg"] = 'Това е преглед на последно съхранените промени. Щракнете Съхранение и Обновяване на текущите промени';
$_lang["preview_resource"] = 'Преглед на документ';
+$_lang["preview_unsupported_url"] = 'Тази връзка не може да бъде прегледана: от мениджъра се отварят само адреси с http и https.';
$_lang["private"] = 'Частен';
$_lang["public"] = 'Публичен';
$_lang["publish_date"] = 'Дата на публикуване';
@@ -801,6 +803,7 @@
$_lang["role_edit_plugin"] = 'Редактиране на плъгини';
$_lang["role_edit_role"] = 'Редактиране на роли';
$_lang["role_edit_settings"] = 'Промяна на настройките на сайта';
+$_lang["role_security_settings"] = 'Промяна на настройките за сигурност';
$_lang["role_edit_snippet"] = 'Редактиране на снипети';
$_lang["role_edit_template"] = 'Редактиране на шаблоните на сайта';
$_lang["role_edit_user"] = 'Редактиране на потребители';
@@ -814,6 +817,7 @@
$_lang["role_help"] = 'Преглед на страниците Помощ';
$_lang["role_home"] = 'Викане на началната страница на мениджъра';
$_lang["role_import_static"] = 'Импорт на HTML';
+$_lang["role_in_use_message"] = 'Тази роля все още е присвоена на един или повече потребители. Преди да я изтриете, преназначете тези потребители към друга роля.';
$_lang["role_widget_recent_info"] = 'Преглед на уиджета „Наскоро редактирани/създадени документи“';
$_lang["role_manage_metatags"] = 'Управление на META тагове и ключови думи на сайта';
$_lang["role_management_msg"] = 'Тук можете да изберете коя роля да редактирате.';
diff --git a/core/lang/cs/global.php b/core/lang/cs/global.php
index 6b0eda2a89..605ea28281 100644
--- a/core/lang/cs/global.php
+++ b/core/lang/cs/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Tady můžete vidět, které skupiny dokumentů jsou nastavené. Můžete vytvářet nové skupiny, přejmenovávat je, mazat je a procházet, které dokumenty se nacházejí, v kterých skupinách (po přesunu kurzoru myši na ID dokumentu se zobrazí jeho název). Přidat dokument do skupiny nebo ho odstranit se da jen prímou změnou v samotném dokumentu.';
$_lang["access_permissions_user_groups"] = 'Skupiny návštěvníků';
$_lang["access_permissions_user_message"] = 'Tady můžete zvolit, který uživatel patří, do které skupiny:';
+$_lang["access_permissions_user_group_locked"] = 'Skupiny zobrazené šedě jsou mimo vaše vlastní členství. Změnit je může pouze administrátor nebo správce s oprávněním "Správa skupin dokumentů a uživatelů", proto jsou zde pouze ke čtení.';
$_lang["access_permissions_users_in_group"] = 'Uživatelé ve skupině: ';
$_lang["access_permissions_users_tab"] = 'Tady můžete vidět, které skupiny uživatelů jsou nastavené. Můžete vytvářet nové skupiny, přejmenovávat je a prohlížet, kteří uživatelé jsou ve skupinách registrováni. Jestliže chcete přidat nového uživatele do skupiny nebo ho odstranit, musíte upravit nastavení konkrétního uživatele ve "Správě uživatelů". Administrátoři (užívatelé s přiděleným ID 1) mají vždy přístup ke všem dokumentům a není potřeba je přidávat do kterékoli skupiny.';
$_lang["account_email"] = 'Email účtu';
@@ -687,6 +688,7 @@
$_lang["preview"] = 'Náhled';
$_lang["preview_msg"] = 'Tohle je náhled na vaše poslední uložené změny. Kliknutím zde na Uložit a obnovit uložíte vaše současné změny';
$_lang["preview_resource"] = 'Náhled dokumentu';
+$_lang["preview_unsupported_url"] = 'Tento odkaz nelze zobrazit v náhledu: ze správce se otevírají pouze adresy http a https.';
$_lang["private"] = 'Soukromý';
$_lang["public"] = 'Veřejný';
$_lang["publish_date"] = 'Datum publikování';
@@ -805,6 +807,7 @@
$_lang["role_edit_plugin"] = 'Upravit pluginy';
$_lang["role_edit_role"] = 'Upravit oprávnění';
$_lang["role_edit_settings"] = 'Změnit nastavení stránek';
+$_lang["role_security_settings"] = 'Změnit nastavení zabezpečení';
$_lang["role_edit_snippet"] = 'Upravit snippety';
$_lang["role_edit_template"] = 'Upravit šablony stránek';
$_lang["role_edit_user"] = 'Upravit uživatele';
@@ -818,6 +821,7 @@
$_lang["role_help"] = 'Zobrazit pomocníka';
$_lang["role_home"] = 'Použít správce úvodní stránky';
$_lang["role_import_static"] = 'Importovat HTML';
+$_lang["role_in_use_message"] = 'Tato role je stále přiřazena jednomu nebo více uživatelům. Než ji bude možné smazat, přiřaďte tyto uživatele k jiné roli.';
$_lang["role_widget_recent_info"] = 'Zobrazit widget Naposledy vytvořené nebo upravené dokumenty';
$_lang["role_manage_metatags"] = 'Upravit META tagy a klíčové slova celého webu';
$_lang["role_management_msg"] = 'Zde můžete zvolit oprávnění, které chcete upravit.';
diff --git a/core/lang/da/global.php b/core/lang/da/global.php
index 6856677327..b53a2c4194 100644
--- a/core/lang/da/global.php
+++ b/core/lang/da/global.php
@@ -42,6 +42,7 @@
$_lang["access_permissions_resources_tab"] = 'Se hvilke ressource grupper der er oprettet. Du kan også oprette nye grupper, omdøbe grupper, slette grupper, samt se hvilke ressourcer der er i de respektive grupper. (Hold musen over ressourcens ID for at se dets navn). For at tilføje eller fjerne en ressource til en gruppe, skal du redigere selve ressourcen.';
$_lang["access_permissions_user_groups"] = 'Bruger grupper';
$_lang["access_permissions_user_message"] = 'Vælg bruger gruppen som denne bruger skal tilhøre:';
+$_lang["access_permissions_user_group_locked"] = 'Grupper vist i gråt er uden for dit eget medlemskab. Kun en administrator eller en manager med tilladelsen "Administrer dokument- og brugergrupper" kan ændre dem, så de er skrivebeskyttede her.';
$_lang["access_permissions_users_in_group"] = 'Brugere i gruppen:';
$_lang["access_permissions_users_tab"] = 'Se hvilke bruger grupper der er oprettet. Du kan også oprette nye bruger grupper, omdøbe grupper, slette grupper, samt se hvilke brugere der tilhører de forskellige grupper. For at tilføje en ny bruger til en brugergruppe, eller fjerne en bruger i en brugergruppe, skal du redigere den specifikke bruger. Administratoren (dvs. den der har ID nr. 1) har altid adgang til alle ressourcer, så derfor skal administratoren ikke tilføjes til nogen af grupperne.';
$_lang["account_email"] = 'E-mail konto';
@@ -688,6 +689,7 @@
$_lang["preview"] = 'Preview';
$_lang["preview_msg"] = 'Dette er et preview af de sidste gemte ændringer. Klik her for at gemme og opdatere ændringerne';
$_lang["preview_resource"] = 'Preview ressourcen';
+$_lang["preview_unsupported_url"] = 'Dette link kan ikke vises som preview: kun http- og https-adresser åbnes fra administrationen.';
$_lang["private"] = 'Privat';
$_lang["public"] = 'Offentligt';
$_lang["publish_date"] = 'Publiceringsdato';
@@ -802,6 +804,7 @@
$_lang["role_edit_plugin"] = 'Redigere plugins';
$_lang["role_edit_role"] = 'Redigere roller';
$_lang["role_edit_settings"] = 'Skifte websitets indstillinger';
+$_lang["role_security_settings"] = 'Skifte sikkerhedsindstillinger';
$_lang["role_edit_snippet"] = 'Rediger snippets';
$_lang["role_edit_template"] = 'Redigere ressource skabeloner';
$_lang["role_edit_user"] = 'Redigere brugere';
@@ -815,6 +818,7 @@
$_lang["role_help"] = 'Se hjælpe siderne';
$_lang["role_home"] = 'Må se CMS\'ets introduktionsside';
$_lang["role_import_static"] = 'Importere HTML';
+$_lang["role_in_use_message"] = 'Denne rolle er stadig tildelt en eller flere brugere. Tildel disse brugere en anden rolle, før den kan slettes.';
$_lang["role_widget_recent_info"] = 'Vis widgetten Seneste redigerede eller oprettede ressourcer';
$_lang["role_manage_metatags"] = 'Administrere websitets META tags og keywords';
$_lang["role_management_msg"] = 'Vælg rollen der skal redigeres.';
diff --git a/core/lang/de/global.php b/core/lang/de/global.php
index 16352f0921..c679424ce6 100644
--- a/core/lang/de/global.php
+++ b/core/lang/de/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Hier sehen Sie die angelegten Ressourcen-Gruppen. Sie können auch neue Gruppen anlegen, Gruppen umbenennen, Gruppen löschen und sehen welche Ressourcen in den unterschiedlichen Gruppen enthalten sind (den Namen der Ressource sehen Sie, in dem Sie mit der Maus über die ID fahren). Um eine Ressource zu einer Gruppe hinzuzufügen oder von einer Gruppe zu entfernen, ändern Sie die entsprechende Ressource direkt.';
$_lang["access_permissions_user_groups"] = 'Benutzergruppen';
$_lang["access_permissions_user_message"] = 'Hier können Sie auswählen welcher Benutzergruppe dieser Benutzer zugeordnet ist:';
+$_lang["access_permissions_user_group_locked"] = 'Grau dargestellte Gruppen liegen außerhalb Ihrer eigenen Mitgliedschaft. Nur ein Administrator oder ein Manager mit der Berechtigung "Dokument- und Benutzergruppen verwalten" kann sie ändern, daher sind sie hier schreibgeschützt.';
$_lang["access_permissions_users_in_group"] = 'Benutzer in Gruppe:';
$_lang["access_permissions_users_tab"] = 'Hier sehen Sie die angelegten Benutzergruppen. Sie können auch neue Gruppen anlegen, Gruppen umbenennen, Gruppen löschen und die Zugehörigkeit der Benutzer zu Gruppen sehen. Um ein Benutzer zu einer Gruppe hinzuzufügen oder zu entfernen, bearbeiten Sie direkt den entsprechenden Benutzer. Administratoren (Benutzer mit der Rollen ID 1) haben immer Zugriff auf alle Ressourcen und müssen keiner speziellen Gruppen angehören.';
$_lang["account_email"] = 'E-Mail-Adresse';
@@ -730,6 +731,7 @@
$_lang["preview"] = 'Vorschau';
$_lang["preview_msg"] = 'Dies ist eine Vorschau Ihrer zuletzt gespeicherten Änderungen. Klicken Sie Speichern und aktualisieren um Ihren aktuellen Änderungen zu speichern.';
$_lang["preview_resource"] = 'Ressource-Vorschau';
+$_lang["preview_unsupported_url"] = 'Dieser Link kann nicht in der Vorschau angezeigt werden: Aus dem Manager werden nur http- und https-Adressen geöffnet.';
$_lang["private"] = 'Privat';
$_lang["public"] = 'Öffentlich';
$_lang["publish_date"] = 'Veröffentlichungstermin';
@@ -850,6 +852,7 @@
$_lang["role_edit_plugin"] = 'Plugins bearbeiten';
$_lang["role_edit_role"] = 'Rollen bearbeiten';
$_lang["role_edit_settings"] = 'Konfiguration bearbeiten';
+$_lang["role_security_settings"] = 'Sicherheitseinstellungen bearbeiten';
$_lang["role_edit_snippet"] = 'Snippets bearbeiten';
$_lang["role_edit_template"] = 'Templates bearbeiten';
$_lang["role_edit_user"] = 'Manager-Benutzer bearbeiten';
@@ -864,6 +867,7 @@
$_lang["role_help"] = 'Anzeigen der Hilfeseiten';
$_lang["role_home"] = 'Manager Startseite ermitteln';
$_lang["role_import_static"] = 'HTML importieren';
+$_lang["role_in_use_message"] = 'Diese Rolle ist noch einem oder mehreren Benutzern zugewiesen. Weisen Sie diese Benutzer einer anderen Rolle zu, bevor sie gelöscht werden kann.';
$_lang["role_widget_recent_info"] = 'Widget „Kürzlich bearbeitete oder erstellte Ressourcen“ anzeigen';
$_lang["role_manage_metatags"] = 'Meta-Tags und Schlüsselwörter verwalten';
$_lang["role_management_msg"] = 'Hier können Sie eine Rolle zum Bearbeiten wählen.';
diff --git a/core/lang/en/global.php b/core/lang/en/global.php
index 934844e250..11c5547446 100644
--- a/core/lang/en/global.php
+++ b/core/lang/en/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'See which Resource Groups have been created. Also create new groups, rename groups, delete groups and see which Resources are in the different groups (hover over the id of the Resource to see its name). To add a Resource to a group or remove a Resource from a group, edit the Resource directly.';
$_lang["access_permissions_user_groups"] = 'User groups';
$_lang["access_permissions_user_message"] = 'Select which User Groups this user belongs to:';
+$_lang["access_permissions_user_group_locked"] = 'Groups shown in grey are outside your own membership. Only an administrator, or a manager with the "Manage document and user groups" permission, can change them, so they are read-only here.';
$_lang["access_permissions_users_in_group"] = 'Users in group:';
$_lang["access_permissions_users_tab"] = 'View the User Groups that have been created. You can also create new groups, rename groups, delete groups and see which users are members of the different groups. To add a new user to a group or to remove a user from a group, edit the user directly. Administrators (users who have been assigned the role with ID 1) always have access to all Resources, so they don\'t need to be added to any groups.';
$_lang["account_email"] = 'Account email';
@@ -758,6 +759,7 @@
$_lang["preview"] = 'Preview';
$_lang["preview_msg"] = 'This is a preview of your last saved changes. Click here to Save and Refresh your current changes';
$_lang["preview_resource"] = 'Preview Resource';
+$_lang["preview_unsupported_url"] = 'This link cannot be previewed: only http and https addresses open from the manager.';
$_lang["private"] = 'Private';
$_lang["public"] = 'Public';
$_lang["publish_date"] = 'Publish Date';
@@ -883,6 +885,7 @@
$_lang["role_edit_plugin"] = 'Edit Plugins';
$_lang["role_edit_role"] = 'Edit roles';
$_lang["role_edit_settings"] = 'Change site settings';
+$_lang["role_security_settings"] = 'Change security settings';
$_lang["role_edit_snippet"] = 'Edit Snippets';
$_lang["role_edit_template"] = 'Edit site Templates';
$_lang["role_edit_user"] = 'Edit users';
@@ -897,6 +900,7 @@
$_lang["role_help"] = 'View help pages';
$_lang["role_home"] = 'Request manager intro page';
$_lang["role_import_static"] = 'Import HTML';
+$_lang["role_in_use_message"] = 'This role is still assigned to one or more users. Reassign those users to another role before it can be deleted.';
$_lang["role_widget_recent_info"] = 'View widget Recently edited/created Resources';
$_lang["role_list_module"] = 'List Module';
$_lang["role_manage_metatags"] = 'Manage site META tags and keywords';
diff --git a/core/lang/es/global.php b/core/lang/es/global.php
index 986cb7d1a5..4e04536113 100644
--- a/core/lang/es/global.php
+++ b/core/lang/es/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Aquí puedes ver qué grupos de documentos han sido configurados. También puedes crear nuevos grupos, renombrarlos, borrarlos y ver qué documentos están en los diferentes grupos (pon el mouse sobre el ID del documento para ver su nombre). Para añadir un documento a un grupo, o removerlo de un grupo, edita el documento directamente.';
$_lang["access_permissions_user_groups"] = 'Grupos de usuarios';
$_lang["access_permissions_user_message"] = 'Aquí puedes seleccionar a qué grupos de usuarios pertenece este usuario:';
+$_lang["access_permissions_user_group_locked"] = 'Los grupos mostrados en gris están fuera de su propia pertenencia. Solo un administrador o un gestor con el permiso "Administrar grupos de documentos y usuarios" puede cambiarlos, por lo que aquí son de solo lectura.';
$_lang["access_permissions_users_in_group"] = 'Usuarios en Grupo:';
$_lang["access_permissions_users_tab"] = 'Aquí puedes ver que grupos de usuarios han sido configurados. También puedes crear nuevos grupos, renombrarlos, borrarlos y ver qué usuarios son miembros de los diferentes grupos. Para añadir un nuevo usuario a un grupo, o removerlo de uno, edita el usuario directamente. Los Administradores (usuarios a los que se les ha asignado un rol con ID 1) siempre tienen acceso a todos los documentos, por lo que no necesitan ser añadidos a ningún grupo.';
$_lang["account_email"] = 'Email de la cuenta';
@@ -732,6 +733,7 @@
$_lang["preview"] = 'Previsualizar';
$_lang["preview_msg"] = 'Esta es una previsualización de tus últimos cambios guardados. Haz clic aquí para Guardar y Recargar tus cambios actuales';
$_lang["preview_resource"] = 'Previsualizar documento';
+$_lang["preview_unsupported_url"] = 'Este enlace no se puede previsualizar: desde el administrador solo se abren direcciones http y https.';
$_lang["private"] = 'Privado';
$_lang["public"] = 'Público';
$_lang["publish_date"] = 'Fecha de publicación';
@@ -852,6 +854,7 @@
$_lang["role_edit_plugin"] = 'Editar plugins';
$_lang["role_edit_role"] = 'Editar roles';
$_lang["role_edit_settings"] = 'Cambiar la configuración del sitio';
+$_lang["role_security_settings"] = 'Cambiar la configuración de seguridad';
$_lang["role_edit_snippet"] = 'Editar snippets';
$_lang["role_edit_template"] = 'Editar templates del sitio';
$_lang["role_edit_user"] = 'Editar usuarios';
@@ -866,6 +869,7 @@
$_lang["role_help"] = 'Ver las páginas de ayuda';
$_lang["role_home"] = 'Solicitar página de inicio del administrador';
$_lang["role_import_static"] = 'Importar HTML';
+$_lang["role_in_use_message"] = 'Este rol todavía está asignado a uno o más usuarios. Reasigne esos usuarios a otro rol antes de poder eliminarlo.';
$_lang["role_widget_recent_info"] = 'Ver widget Documentos recientemente editados/creados';
$_lang["role_manage_metatags"] = 'Administrar las etiquetas META y las Palabras clave del sitio';
$_lang["role_management_msg"] = 'Elige el rol que deseas editar.';
diff --git a/core/lang/et/global.php b/core/lang/et/global.php
index f94425e2a2..c4212e0f9c 100644
--- a/core/lang/et/global.php
+++ b/core/lang/et/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Vaadake loodud ressursirühmi. Saate ka luua uusi rühmi, neid ümber nimetada ja kustutada ning vaadata eri rühmadesse kuuluvaid ressursse (nime nägemiseks liikuge kursoriga ressursi ID-le). Ressursi lisamiseks rühma või sealt eemaldamiseks muutke ressurssi otse.';
$_lang["access_permissions_user_groups"] = 'Kasutajarühmad';
$_lang["access_permissions_user_message"] = 'Valige kasutajarühmad, kuhu see kasutaja kuulub:';
+$_lang["access_permissions_user_group_locked"] = 'Hallina kuvatud rühmad on väljaspool teie enda liikmesust. Neid saab muuta ainult administraator või haldur, kellel on õigus "Halda dokumendi- ja kasutajarühmi", seega on need siin ainult lugemiseks.';
$_lang["access_permissions_users_in_group"] = 'Kasutajad rühmas:';
$_lang["access_permissions_users_tab"] = 'Vaadake loodud kasutajarühmi. Saate ka luua uusi rühmi, neid ümber nimetada ja kustutada ning vaadata eri rühmadesse kuuluvaid kasutajaid. Kasutaja lisamiseks rühma või sealt eemaldamiseks muutke kasutajat otse. Administraatoritel (kasutajatel, kellele on määratud roll ID-ga 1) on alati juurdepääs kõigile ressurssidele, seega ei pea neid ühegi rühmaga liitma.';
$_lang["account_email"] = 'Konto e-posti aadress';
@@ -758,6 +759,7 @@
$_lang["preview"] = 'Eelvaade';
$_lang["preview_msg"] = 'See on viimati salvestatud muudatuste eelvaade. Praeguste muudatuste salvestamiseks ja värskendamiseks klõpsa siin';
$_lang["preview_resource"] = 'Ressursi eelvaade';
+$_lang["preview_unsupported_url"] = 'Seda linki ei saa eelvaates avada: haldusliidesest avanevad ainult http- ja https-aadressid.';
$_lang["private"] = 'Privaatne';
$_lang["public"] = 'Avalik';
$_lang["publish_date"] = 'Avaldamise kuupäev';
@@ -883,6 +885,7 @@
$_lang["role_edit_plugin"] = 'Muuda pluginaid';
$_lang["role_edit_role"] = 'Muuda rolle';
$_lang["role_edit_settings"] = 'Muuda saidi seadeid';
+$_lang["role_security_settings"] = 'Muuda turvaseadeid';
$_lang["role_edit_snippet"] = 'Muuda koodilõike';
$_lang["role_edit_template"] = 'Muuda saidimalle';
$_lang["role_edit_user"] = 'Muuda kasutajaid';
@@ -897,6 +900,7 @@
$_lang["role_help"] = 'Vaata abilehti';
$_lang["role_home"] = 'Laadi haldusliidese avaleht';
$_lang["role_import_static"] = 'Impordi HTML-i';
+$_lang["role_in_use_message"] = 'See roll on endiselt määratud ühele või mitmele kasutajale. Enne kustutamist määrake need kasutajad teisele rollile.';
$_lang["role_widget_recent_info"] = 'Vaata vidinat „Hiljuti muudetud või loodud ressursid“';
$_lang["role_list_module"] = 'Kuva moodulite loend';
$_lang["role_manage_metatags"] = 'Halda saidi META-silte ja märksõnu';
diff --git a/core/lang/fa/global.php b/core/lang/fa/global.php
index ee203983a9..7ad85007d2 100644
--- a/core/lang/fa/global.php
+++ b/core/lang/fa/global.php
@@ -42,6 +42,7 @@
$_lang["access_permissions_resources_tab"] = 'در اینجا شما میتوانید مشاهده کنید که چه گروههای پرونده ای تهیه شده است. همچنین شما قادر خواهید بود گروههای جدیدی را تعریف و ایجاد کنید, نام گروه ها را تغییر دهید, گروه ها را حذف کنید و ببنید که چه پرونده ای در چه چه گروهی قرار دارد (نشانگر موس را روی ردیف یا شناسه ی پرونده برانید تا اسم آنرا مشاهده کنید). برای افزودن پرونده به گروه یا حذف پرونده از یک گروه, پوشه ی پرونده را ویرایش کنید.';
$_lang["access_permissions_user_groups"] = 'گروههای کاربرات';
$_lang["access_permissions_user_message"] = 'در اینجا میتوانید مشخص کنید که این کاربر به چه گروههایی از کاربری متعلق است:';
+$_lang["access_permissions_user_group_locked"] = 'گروههایی که به رنگ خاکستری نمایش داده میشوند خارج از عضویت شما هستند. فقط یک مدیر کل یا مدیری با دسترسی "مدیریت گروههای سند و کاربر" میتواند آنها را تغییر دهد، بنابراین در اینجا فقط خواندنی هستند.';
$_lang["access_permissions_users_in_group"] = 'کاربران در گروه: ';
$_lang["access_permissions_users_tab"] = 'در اینجا شما میتوانید مشاهده کنید که چه گروههای کاربری تعریف شده اند. همچنین شما میتوانید گروههای جدیدی ایجاد نمایید, تغییر نام دهید, گروه ها را حذف کنید و ببینید که کدام کاربران عضو گروههای مختلف دیگر هستند. برای افزودن کاربر جدید به یک گروه یا حذف یک کاربر از یک گروه, خود کاربر را مستقیما ویرایش کنید. مدیران کل (کاربرانی که با نقش اول یا 1 تعریف شده اند) همیشه به کلیه ی پرونده ها دسترسی خواهند داشت, به همین دلیل نیازی نیست که آنها به گروه خاصی اضافه شوند.';
$_lang["account_email"] = 'حساب ایمیل';
@@ -688,6 +689,7 @@
$_lang["preview"] = 'نمایش وبگاه';
$_lang["preview_msg"] = 'این پیش نمایش آخرین تغییرات ذخیره شده ی شماست. برای ذخیره نهایی و بازخوانی تغییرات جدیداینجا کلیک کنید';
$_lang["preview_resource"] = 'نمایش پرونده';
+$_lang["preview_unsupported_url"] = 'این پیوند قابل نمایش نیست: از مدیر فقط نشانیهای http و https باز میشوند.';
$_lang["private"] = 'خصوصی';
$_lang["public"] = 'عمومی';
$_lang["publish_date"] = 'تاریخ انتشار';
@@ -802,6 +804,7 @@
$_lang["role_edit_plugin"] = 'ویرایش پلاگین';
$_lang["role_edit_role"] = 'ئیرایش نقش ها';
$_lang["role_edit_settings"] = 'اعمال تغییر در تنظیمات وبگاه';
+$_lang["role_security_settings"] = 'اعمال تغییر در تنظیمات امنیتی';
$_lang["role_edit_snippet"] = 'ویرایش اسنیپتها';
$_lang["role_edit_template"] = 'ویرایش پوسته یا قالب وبگاه';
$_lang["role_edit_user"] = 'ویرایش کاربران';
@@ -815,6 +818,7 @@
$_lang["role_help"] = 'مشاهده ی صفحات راهنما';
$_lang["role_home"] = 'درخواست صفحه ی مقدمه ی مدیریت';
$_lang["role_import_static"] = 'واردسازی HTML';
+$_lang["role_in_use_message"] = 'این نقش هنوز به یک یا چند کاربر اختصاص داده شده است. پیش از حذف، این کاربران را به نقش دیگری منتقل کنید.';
$_lang["role_widget_recent_info"] = 'مشاهده ویجت پرونده هایی که به تازگی ایجاد / ویرایش شده اند';
$_lang["role_manage_metatags"] = 'مدیریت کلمات شاخص و کلیدی وبگاه';
$_lang["role_management_msg"] = 'در اینجا شما میتوانید نقش مورد نظر خود را برای ویرایش انتخاب کنید.';
diff --git a/core/lang/fi/global.php b/core/lang/fi/global.php
index 3eb88300a9..381677af23 100644
--- a/core/lang/fi/global.php
+++ b/core/lang/fi/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Muokkaa sivuryhmiä. Lisää yksittäinen sivu ryhmään tai poista yksittäinen sivu ryhmästä muokkaamalla suoraan kyseistä sivua.';
$_lang["access_permissions_user_groups"] = 'Ylläpidon käyttäjäryhmät';
$_lang["access_permissions_user_message"] = 'Valitse käyttäjän käyttäjäryhmät:';
+$_lang["access_permissions_user_group_locked"] = 'Harmaana näytetyt ryhmät ovat oman jäsenyytesi ulkopuolella. Vain ylläpitäjä tai hallitsija, jolla on oikeus "Hallitse asiakirja- ja käyttäjäryhmiä", voi muuttaa niitä, joten ne ovat tässä vain luettavissa.';
$_lang["access_permissions_users_in_group"] = 'Käyttäjiä ryhmässä: ';
$_lang["access_permissions_users_tab"] = 'Muokkaa käyttäjäryhmiä. Lisää yksittäinen käyttäjä ryhmään tai poista yksittäinen käyttäjä ryhmästä muokkaamalla suoraan kyseistä käyttäjää. Käyttäjillä, joiden rooli on pääkäyttäjä (Adminstrator), on aina oikeudet kaikkiin sivuihin.';
$_lang["account_email"] = 'Käyttäjän sähköpostiosoite';
@@ -687,6 +688,7 @@
$_lang["preview"] = 'Esikatselu';
$_lang["preview_msg"] = 'Esikatselunäkymä viimeksi tallennetuista muutoksista. Napsauta tästä tallentaaksesi ja päivittääksesi tämän hetkiset muutokset';
$_lang["preview_resource"] = 'Esikatsele';
+$_lang["preview_unsupported_url"] = 'Tätä linkkiä ei voi esikatsella: ylläpidosta avautuvat vain http- ja https-osoitteet.';
$_lang["private"] = 'Yksityinen';
$_lang["public"] = 'Julkinen';
$_lang["publish_date"] = 'Julkaisupäiväys';
@@ -801,6 +803,7 @@
$_lang["role_edit_plugin"] = 'Muokkaa liitännäisiä';
$_lang["role_edit_role"] = 'Muokkaa roolia';
$_lang["role_edit_settings"] = 'Muuta sivuston asetuksia';
+$_lang["role_security_settings"] = 'Muuta tietoturva-asetuksia';
$_lang["role_edit_snippet"] = 'Muokkaa PHP-palasta';
$_lang["role_edit_template"] = 'Muokkaa sivupohjaa';
$_lang["role_edit_user"] = 'Muokkaa käyttäjää';
@@ -814,6 +817,7 @@
$_lang["role_help"] = 'Näytä ohjesivut';
$_lang["role_home"] = 'Näytä aloitussivu';
$_lang["role_import_static"] = 'Tuo HTML';
+$_lang["role_in_use_message"] = 'Tämä rooli on edelleen määritetty yhdelle tai useammalle käyttäjälle. Määritä nämä käyttäjät toiseen rooliin, ennen kuin se voidaan poistaa.';
$_lang["role_widget_recent_info"] = 'Näytä widget Viimeisimmät sivut';
$_lang["role_manage_metatags"] = 'Hallitse sivuston metatietoja ja avainsanoja';
$_lang["role_management_msg"] = 'Muokkaa rooleja.';
diff --git a/core/lang/fr/global.php b/core/lang/fr/global.php
index cf8ac7ba23..6b7b4947bd 100644
--- a/core/lang/fr/global.php
+++ b/core/lang/fr/global.php
@@ -44,6 +44,7 @@
$_lang["access_permissions_resources_tab"] = 'Les Groupes de Ressources actuels sont affichés ci-dessous. Vous pouvez en créer de nouveaux, les renommer, les supprimer et voir quelles Ressources appartiennent aux différents groupes (le survol par le pointeur de l\'ID de la Ressource en affichera le nom). Pour ajouter une Ressource à un groupe ou pour l\'en retirer, modifiez directement la Ressource.';
$_lang["access_permissions_user_groups"] = 'Groupes d\'Utilisateurs';
$_lang["access_permissions_user_message"] = 'Vous pouvez choisir ici les Groupes d\'Utilisateurs auxquels cet utilisateur appartient';
+$_lang["access_permissions_user_group_locked"] = 'Les groupes affichés en gris sont en dehors de votre propre appartenance. Seul un administrateur ou un gestionnaire disposant de l\'autorisation "Gérer les groupes de documents et d\'utilisateurs" peut les modifier ; ils sont donc en lecture seule ici.';
$_lang["access_permissions_users_in_group"] = 'Utilisateurs dans le groupe:';
$_lang["access_permissions_users_tab"] = 'Les Groupes d\'Utilisateurs actuels sont affichés ci-dessous. Vous pouvez en créer de nouveaux, les renommer, les supprimer et voir quels utilisateurs sont membres des différents groupes. Pour ajouter un nouvel utilisateur à un groupe ou pour l\'en retirer, modifiez directement l\'utilisateur. Les administrateurs ont toujours accès à toutes les Ressources. Il n\'est donc pas nécessaire de les ajouter à un groupe en particulier.';
$_lang["account_email"] = 'Email du compte utilisateur';
@@ -690,6 +691,7 @@
$_lang["preview"] = 'Prévisualiser';
$_lang["preview_msg"] = 'Voici la prévisualisation de vos derniers changements enregistrés. Cliquer ici pour Enregistrer et Afficher vos modifications actuelles';
$_lang["preview_resource"] = 'Prévisualiser';
+$_lang["preview_unsupported_url"] = 'Ce lien ne peut pas être prévisualisé : seules les adresses http et https s\'ouvrent depuis le gestionnaire.';
$_lang["private"] = 'Privé';
$_lang["public"] = 'Public';
$_lang["publish_date"] = 'Date de publication';
@@ -804,6 +806,7 @@
$_lang["role_edit_plugin"] = 'Modifier les Plugins';
$_lang["role_edit_role"] = 'Modifier les rôles';
$_lang["role_edit_settings"] = 'Modifier la configuration du site';
+$_lang["role_security_settings"] = 'Modifier la configuration de sécurité';
$_lang["role_edit_snippet"] = 'Modifier les Snippets';
$_lang["role_edit_template"] = 'Modifier les Modèles';
$_lang["role_edit_user"] = 'Modifier les Utilisateurs';
@@ -817,6 +820,7 @@
$_lang["role_help"] = 'Accès aux pages d\'aide';
$_lang["role_home"] = 'Accès à la page d\'introduction du gestionnaire';
$_lang["role_import_static"] = 'Importation HTML';
+$_lang["role_in_use_message"] = 'Ce rôle est encore attribué à un ou plusieurs utilisateurs. Réaffectez ces utilisateurs à un autre rôle avant de pouvoir le supprimer.';
$_lang["role_widget_recent_info"] = 'Voir le widget Ressources éditées/créées récemment';
$_lang["role_manage_metatags"] = 'Gérer les balises META et les mots-clés du site';
$_lang["role_management_msg"] = 'Choisissez le rôle que vous souhaitez modifier.';
diff --git a/core/lang/he/global.php b/core/lang/he/global.php
index ef4d010f12..ac8ce7581a 100644
--- a/core/lang/he/global.php
+++ b/core/lang/he/global.php
@@ -42,6 +42,7 @@
$_lang["access_permissions_resources_tab"] = 'כאן תוכלו לראות איזה קבוצות מסמכים הוגדרו. ניתן ליצור קבוצות חדשות, לשנות שם לקבוצות, למחוק קבוצות ולראות אילו מסמכים קיימים בקבוצות שונות (ניתן לראות את שם המסמך במעבר עכבר מעל מזהה המסמך). כדי להוסיף מסמך לקבוצה או להסיר מסמך מהקבוצה, יש לערוך את המסמך ישירות.';
$_lang["access_permissions_user_groups"] = 'קבוצות משתמשים';
$_lang["access_permissions_user_message"] = 'כאן אתה יכול לבחור לאילו קבוצות משתמשים משתמש זה שייך:';
+$_lang["access_permissions_user_group_locked"] = 'הקבוצות המוצגות באפור נמצאות מחוץ לחברות שלך. רק מנהל מערכת, או מנהל בעל ההרשאה "ניהול קבוצות מסמכים ומשתמשים", יכול לשנות אותן, ולכן הן לקריאה בלבד כאן.';
$_lang["access_permissions_users_in_group"] = 'משתמשים בקבוצות:';
$_lang["access_permissions_users_tab"] = 'כאן תוכלו לראות איזה קבוצות משתמשים הוגדרו. אתה יכול גם ליצור קבוצות חדשות, לשנות שם קבוצה, למחוק קבוצות ולראות מהם המשתמשים חברי הקבוצות השונות. כדי להוסיף משתמש חדש לקבוצה או להסיר משתמש מהקבוצה, יש לערוך את המשתמש ישירות. למנהלים (משתמשים שיש להם כבר את התפקיד שהוקצה עם מזהה 1) תמיד יש גישה לכל המסמכים, ולכן לא צריך להוסיף להם את כל הקבוצות.';
$_lang["account_email"] = 'חשבון מייל';
@@ -688,6 +689,7 @@
$_lang["preview"] = 'תצוגה מקדימה';
$_lang["preview_msg"] = 'This is a preview of your last saved changes. Click here to Save and Refresh your current changes';
$_lang["preview_resource"] = 'תצוגה מקדימה למסמך';
+$_lang["preview_unsupported_url"] = 'לא ניתן להציג תצוגה מקדימה של קישור זה: רק כתובות http ו-https נפתחות מתוך המנהל.';
$_lang["private"] = 'פרטי';
$_lang["public"] = 'ציבורי';
$_lang["publish_date"] = 'תאריך פרסום';
@@ -802,6 +804,7 @@
$_lang["role_edit_plugin"] = 'ערוך התקנים';
$_lang["role_edit_role"] = 'ערוך תפקיד';
$_lang["role_edit_settings"] = 'שנה הגדרות אתר';
+$_lang["role_security_settings"] = 'שנה הגדרות אבטחה';
$_lang["role_edit_snippet"] = 'ערוך קטעי קוד';
$_lang["role_edit_template"] = 'ערוך תבניות עיצוב';
$_lang["role_edit_user"] = 'ערוך משתמשים';
@@ -815,6 +818,7 @@
$_lang["role_help"] = 'הצג דפי עזרה';
$_lang["role_home"] = 'Request manager intro page';
$_lang["role_import_static"] = 'Import HTML';
+$_lang["role_in_use_message"] = 'תפקיד זה עדיין משויך למשתמש אחד או יותר. שייך משתמשים אלה לתפקיד אחר לפני שניתן יהיה למחוק אותו.';
$_lang["role_widget_recent_info"] = 'הצג את הווידג׳ט מסמכים שנערכו/נוצרו לאחרונה';
$_lang["role_manage_metatags"] = 'Manage site META tags and keywords';
$_lang["role_management_msg"] = 'Choose the role you wish to edit.';
diff --git a/core/lang/hy/global.php b/core/lang/hy/global.php
index 5bb4a3ae97..0a6fd3feb0 100644
--- a/core/lang/hy/global.php
+++ b/core/lang/hy/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Դիտեք ստեղծված ռեսուրսների խմբերը։ Կարող եք նաև ստեղծել նոր խմբեր, վերանվանել կամ ջնջել խմբերը և տեսնել տարբեր խմբերի ռեսուրսները (անունը տեսնելու համար մկնիկը պահեք ռեսուրսի նույնացուցիչի վրա)։ Ռեսուրսը խմբում ավելացնելու կամ խմբից հեռացնելու համար խմբագրեք հենց ռեսուրսը։';
$_lang["access_permissions_user_groups"] = 'Օգտատերերի խմբեր';
$_lang["access_permissions_user_message"] = 'Ընտրեք օգտատերերի այն խմբերը, որոնց պատկանում է այս օգտատերը՝';
+$_lang["access_permissions_user_group_locked"] = 'Մոխրագույնով ցուցադրված խմբերը ձեր անդամակցությունից դուրս են։ Դրանք կարող է փոփոխել միայն ադմինիստրատորը կամ "Կառավարել փաստաթղթերի և օգտատերերի խմբերը" թույլտվությունն ունեցող կառավարիչը, ուստի այստեղ դրանք միայն ընթերցման համար են։';
$_lang["access_permissions_users_in_group"] = 'Խմբի օգտատերերը՝';
$_lang["access_permissions_users_tab"] = 'Դիտեք ստեղծված օգտատերերի խմբերը։ Կարող եք նաև ստեղծել նոր խմբեր, վերանվանել կամ ջնջել խմբերը և տեսնել, թե որ օգտատերերն են տարբեր խմբերի անդամներ։ Օգտատիրոջը խմբում ավելացնելու կամ խմբից հեռացնելու համար խմբագրեք հենց օգտատիրոջը։ Ադմինիստրատորները (1 նույնացուցիչով դեր ունեցող օգտատերերը) միշտ ունեն բոլոր ռեսուրսների հասանելիություն, ուստի նրանց որևէ խմբում ավելացնել պետք չէ։';
$_lang["account_email"] = 'Հաշվի էլ․ փոստ';
@@ -758,6 +759,7 @@
$_lang["preview"] = 'Նախադիտում';
$_lang["preview_msg"] = 'Սա ձեր վերջին պահված փոփոխությունների նախադիտումն է։ Սեղմեք այստեղ՝ ընթացիկ փոփոխությունները պահելու և թարմացնելու համար';
$_lang["preview_resource"] = 'Նախադիտել ռեսուրսը';
+$_lang["preview_unsupported_url"] = 'Այս հղումը հնարավոր չէ նախադիտել։ Կառավարման վահանակից բացվում են միայն http և https հասցեները։';
$_lang["private"] = 'Մասնավոր';
$_lang["public"] = 'Հանրային';
$_lang["publish_date"] = 'Հրապարակման ամսաթիվ';
@@ -883,6 +885,7 @@
$_lang["role_edit_plugin"] = 'Խմբագրել փլագինները';
$_lang["role_edit_role"] = 'Խմբագրել դերերը';
$_lang["role_edit_settings"] = 'Փոխել կայքի կարգավորումները';
+$_lang["role_security_settings"] = 'Փոխել անվտանգության կարգավորումները';
$_lang["role_edit_snippet"] = 'Խմբագրել սնիփեթները';
$_lang["role_edit_template"] = 'Խմբագրել կայքի ձևանմուշները';
$_lang["role_edit_user"] = 'Խմբագրել օգտատերերին';
@@ -897,6 +900,7 @@
$_lang["role_help"] = 'Դիտել օգնության էջերը';
$_lang["role_home"] = 'Բացել կառավարման վահանակի ներածական էջը';
$_lang["role_import_static"] = 'Ներմուծել HTML';
+$_lang["role_in_use_message"] = 'Այս դերը դեռ վերագրված է մեկ կամ մի քանի օգտատերի: Ջնջելուց առաջ վերագրեք այդ օգտատերերին մեկ այլ դերի:';
$_lang["role_widget_recent_info"] = 'Դիտել վերջերս խմբագրված/ստեղծված ռեսուրսների վիջեթը';
$_lang["role_list_module"] = 'Ցուցադրել մոդուլների ցանկը';
$_lang["role_manage_metatags"] = 'Կառավարել կայքի META թեգերն ու հիմնաբառերը';
diff --git a/core/lang/it/global.php b/core/lang/it/global.php
index a01fdf454e..d4e83ffe21 100644
--- a/core/lang/it/global.php
+++ b/core/lang/it/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Qui potete vedere i Gruppi di Risorse che sono stati impostati. Potete anche crearne di nuovi, rinominarli, eliminarli e vedere quali Risorse sono presenti nei differenti gruppi (posizionate il mouse sull\'ID della Risorsa per vederne il nome). Per aggiungere una Risorsa ad un gruppo o per rimuoverla da un gruppo, modificate direttamente la Risorsa.';
$_lang["access_permissions_user_groups"] = 'Gruppi di Utenti';
$_lang["access_permissions_user_message"] = 'Qui potete selezionare a quali Gruppi di Utenti appartiene l\'utente:';
+$_lang["access_permissions_user_group_locked"] = 'I gruppi mostrati in grigio sono al di fuori della tua appartenenza. Solo un amministratore o un gestore con l\'autorizzazione "Gestisci gruppi di documenti e utenti" può modificarli, quindi qui sono di sola lettura.';
$_lang["access_permissions_users_in_group"] = 'Utenti del gruppo: ';
$_lang["access_permissions_users_tab"] = 'Qui potete vedere i Gruppi di Utenti che sono stati impostati. Potete anche crearne di nuovi, rinominarli, eliminarli e vedere quali utenti fanno parte dei differenti gruppi. Per aggiungere un nuovo utente a un gruppo o per rimuoverlo da un gruppo, modificate direttamente l\'utente. Gli amministratori (utenti a cui è assegnato il Ruolo con ID 1) hanno sempre accesso a tutte le Risorse, quindi non hanno necessità di essere aggiunti ad alcun gruppo.';
$_lang["account_email"] = 'Indirizzo email';
@@ -732,6 +733,7 @@
$_lang["preview"] = 'Anteprima';
$_lang["preview_msg"] = 'Questa è solo l\'anteprima delle modifiche. Clicca su Salva e Aggiorna per salvare le modifiche';
$_lang["preview_resource"] = 'Anteprima Risorsa';
+$_lang["preview_unsupported_url"] = 'Questo link non può essere visualizzato in anteprima: dal Manager si aprono solo indirizzi http e https.';
$_lang["private"] = 'Privato';
$_lang["public"] = 'Pubblico';
$_lang["publish_date"] = 'Data di pubblicazione';
@@ -852,6 +854,7 @@
$_lang["role_edit_plugin"] = 'Modifica Plugin';
$_lang["role_edit_role"] = 'Modifica ruoli';
$_lang["role_edit_settings"] = 'Cambia le impostazioni del sito';
+$_lang["role_security_settings"] = 'Cambia le impostazioni di sicurezza';
$_lang["role_edit_snippet"] = 'Modifica Snippet';
$_lang["role_edit_template"] = 'Modifica Template del sito';
$_lang["role_edit_user"] = 'Modifica utenti';
@@ -866,6 +869,7 @@
$_lang["role_help"] = 'Visualizza le pagine di aiuto';
$_lang["role_home"] = 'Visualizza la pagina introduttiva del Manager EVO';
$_lang["role_import_static"] = 'Importa HTML';
+$_lang["role_in_use_message"] = 'Questo ruolo è ancora assegnato a uno o più utenti. Riassegna tali utenti a un altro ruolo prima di poterlo eliminare.';
$_lang["role_widget_recent_info"] = 'Visualizza il widget Risorse modificate o create di recente';
$_lang["role_manage_metatags"] = 'Gestione META tag e parole chiave';
$_lang["role_management_msg"] = 'Qui potete scegliere il Ruolo da modificare o creare un nuovo Ruolo.';
diff --git a/core/lang/ja/global.php b/core/lang/ja/global.php
index 4e07801ae1..2698b614fc 100644
--- a/core/lang/ja/global.php
+++ b/core/lang/ja/global.php
@@ -44,6 +44,7 @@
$_lang["access_permissions_resources_tab"] = 'IDの上にマウスポインタを重ねるとリソース名を確認できます。リソースをグループに加えるには、リソース編集画面を個別に開いて「所属グループ」タブで設定してください。管理画面とウェブページを区別して扱わないため、両方のグループを作る場合はグループ名を工夫するとよいでしょう。';
$_lang["access_permissions_user_groups"] = 'ユーザーグループ';
$_lang["access_permissions_user_message"] = 'このユーザーを所属させたいユーザーグループを選択してください。ユーザは所属しているグループに割り当てられたリソースのみを編集できます。';
+$_lang["access_permissions_user_group_locked"] = 'グレーで表示されているグループは、あなた自身が所属していないグループです。管理者、または「ドキュメントとユーザーグループの管理」権限を持つマネージャーのみが変更でき、ここでは読み取り専用です。';
$_lang["access_permissions_users_in_group"] = 'グループ内ユーザー: ';
$_lang["access_permissions_users_tab"] = '管理画面を扱うユーザのグループと、ウェブページにアクセスする権限を持つユーザのグループは区別されます。ユーザーをグループに参加させるには、個々のユーザー設定で編集する必要があります。※Administrator(ID=1のロールを持つユーザ)は常に全てのリソースにアクセスすることができるため、Administratorをグループに追加する必要はありません。';
$_lang["account_email"] = 'メールアドレス';
@@ -757,6 +758,7 @@
$_lang["preview"] = 'プレビュー';
$_lang["preview_msg"] = 'ここには最後に更新した内容をプレビューしています。 更新と再描画を行うにはここをクリックしてください。';
$_lang["preview_resource"] = 'プレビュー(別窓)';
+$_lang["preview_unsupported_url"] = 'このリンクはプレビューできません。管理画面から開けるのは http と https のアドレスのみです。';
$_lang["private"] = 'グループ設定';
$_lang["public"] = 'グループ制限なし';
$_lang["publish_date"] = '公開日時';
@@ -881,6 +883,7 @@
$_lang["role_edit_plugin"] = 'プラグイン編集画面を開く';
$_lang["role_edit_role"] = 'ロール編集画面を開く';
$_lang["role_edit_settings"] = 'グローバル設定を変更';
+$_lang["role_security_settings"] = 'セキュリティ設定を変更';
$_lang["role_edit_snippet"] = 'スニペット編集画面を開く';
$_lang["role_edit_template"] = 'テンプレート編集画面を開く';
$_lang["role_edit_user"] = 'ユーザー編集画面を開く';
@@ -895,6 +898,7 @@
$_lang["role_help"] = 'ヘルプページを表示';
$_lang["role_home"] = '管理画面ホームへのアクセス';
$_lang["role_import_static"] = '静的HTMLファイルをインポート';
+$_lang["role_in_use_message"] = 'このロールはまだ 1 人以上のユーザーに割り当てられています。削除する前に、これらのユーザーを別のロールに割り当て直してください。';
$_lang["role_widget_recent_info"] = 'ウィジェット「最近編集したリソース」を表示';
$_lang["role_list_module"] = 'モジュール一覧';
$_lang["role_manage_metatags"] = 'サイト全体METAタグとキーワードの管理';
diff --git a/core/lang/ka/global.php b/core/lang/ka/global.php
index f3fab9c0fa..18df10dc61 100644
--- a/core/lang/ka/global.php
+++ b/core/lang/ka/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'იხილეთ შექმნილი რესურსების ჯგუფები. ასევე შეგიძლიათ ჯგუფების შექმნა, სახელის შეცვლა, წაშლა და მათში შემავალი რესურსების ნახვა (სახელის სანახავად კურსორი რესურსის ID-ზე მიიტანეთ). რესურსის ჯგუფში დასამატებლად ან ჯგუფიდან ამოსაშლელად უშუალოდ რესურსი შეცვალეთ.';
$_lang["access_permissions_user_groups"] = 'მომხმარებლების ჯგუფები';
$_lang["access_permissions_user_message"] = 'აირჩიეთ მომხმარებლების ჯგუფები, რომლებსაც ეს მომხმარებელი ეკუთვნის:';
+$_lang["access_permissions_user_group_locked"] = 'ნაცრისფრად ნაჩვენები ჯგუფები თქვენი წევრობის გარეთაა. მათი შეცვლა შეუძლია მხოლოდ ადმინისტრატორს ან მენეჯერს, რომელსაც აქვს უფლება "დოკუმენტებისა და მომხმარებლების ჯგუფების მართვა", ამიტომ აქ ისინი მხოლოდ წასაკითხად არის.';
$_lang["access_permissions_users_in_group"] = 'მომხმარებლები ჯგუფში:';
$_lang["access_permissions_users_tab"] = 'იხილეთ შექმნილი მომხმარებლების ჯგუფები. ასევე შეგიძლიათ ჯგუფების შექმნა, სახელის შეცვლა, წაშლა და მათი წევრების ნახვა. მომხმარებლის ჯგუფში დასამატებლად ან ჯგუფიდან ამოსაშლელად უშუალოდ მომხმარებელი შეცვალეთ. ადმინისტრატორებს (მომხმარებლებს, რომელთა როლის ID არის 1) ყველა რესურსზე ყოველთვის აქვთ წვდომა, ამიტომ მათი ჯგუფებში დამატება საჭირო არ არის.';
$_lang["account_email"] = 'ანგარიშის ელფოსტა';
@@ -758,6 +759,7 @@
$_lang["preview"] = 'წინასწარი ნახვა';
$_lang["preview_msg"] = 'ეს არის ბოლოს შენახული ცვლილებების წინასწარი ნახვა. მიმდინარე ცვლილებების შესანახად და განსაახლებლად დააწკაპუნეთ აქ';
$_lang["preview_resource"] = 'რესურსის წინასწარი ნახვა';
+$_lang["preview_unsupported_url"] = 'ამ ბმულის წინასწარი ნახვა შეუძლებელია: მართვის პანელიდან იხსნება მხოლოდ http და https მისამართები.';
$_lang["private"] = 'პირადი';
$_lang["public"] = 'საჯარო';
$_lang["publish_date"] = 'გამოქვეყნების თარიღი';
@@ -883,6 +885,7 @@
$_lang["role_edit_plugin"] = 'პლაგინების რედაქტირება';
$_lang["role_edit_role"] = 'როლების რედაქტირება';
$_lang["role_edit_settings"] = 'საიტის პარამეტრების შეცვლა';
+$_lang["role_security_settings"] = 'უსაფრთხოების პარამეტრების შეცვლა';
$_lang["role_edit_snippet"] = 'სნიპეტების რედაქტირება';
$_lang["role_edit_template"] = 'საიტის შაბლონების რედაქტირება';
$_lang["role_edit_user"] = 'მომხმარებლების რედაქტირება';
@@ -897,6 +900,7 @@
$_lang["role_help"] = 'დახმარების გვერდების ნახვა';
$_lang["role_home"] = 'მართვის პანელის საწყისი გვერდის მოთხოვნა';
$_lang["role_import_static"] = 'HTML-ის იმპორტი';
+$_lang["role_in_use_message"] = 'ეს როლი ჯერ კიდევ მინიჭებულია ერთ ან მეტ მომხმარებელს. წაშლამდე მიანიჭეთ ამ მომხმარებლებს სხვა როლი.';
$_lang["role_widget_recent_info"] = 'ბოლოს შეცვლილი/შექმნილი რესურსების ვიჯეტის ნახვა';
$_lang["role_list_module"] = 'მოდულების ჩამონათვალი';
$_lang["role_manage_metatags"] = 'საიტის META ტეგებისა და საკვანძო სიტყვების მართვა';
diff --git a/core/lang/kk/global.php b/core/lang/kk/global.php
index 0682db8411..fde51e2d62 100644
--- a/core/lang/kk/global.php
+++ b/core/lang/kk/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Жасалған ресурс топтарын қараңыз. Жаңа топтар жасауға, оларды қайта атауға, жоюға және әр топтағы ресурстарды көруге болады (ресурс атауын көру үшін оның ID үстіне меңзерді апарыңыз). Ресурсты топқа қосу немесе топтан шығару үшін ресурстың өзін өңдеңіз.';
$_lang["access_permissions_user_groups"] = 'Пайдаланушы топтары';
$_lang["access_permissions_user_message"] = 'Осы пайдаланушы кіретін топтарды таңдаңыз:';
+$_lang["access_permissions_user_group_locked"] = 'Сұр түспен көрсетілген топтар сіздің мүшелігіңізден тыс. Оларды тек әкімші немесе "Құжат және пайдаланушы топтарын басқару" рұқсаты бар менеджер өзгерте алады, сондықтан олар мұнда тек оқуға арналған.';
$_lang["access_permissions_users_in_group"] = 'Топтағы пайдаланушылар:';
$_lang["access_permissions_users_tab"] = 'Жасалған пайдаланушы топтарын қараңыз. Топтарды жасауға, қайта атауға, жоюға және олардың мүшелерін көруге болады. Пайдаланушыны топқа қосу немесе одан шығару үшін пайдаланушының өзін өңдеңіз. Әкімшілер (рөлінің ID мәні 1) барлық ресурстарға қатынай алады, оларды топтарға қосу қажет емес.';
$_lang["account_email"] = 'Тіркелгінің электрондық поштасы';
@@ -779,6 +780,7 @@
$_lang["preview"] = 'Алдын ала қарау';
$_lang["preview_msg"] = 'Бұл соңғы сақталған өзгерістердің көрінісі. Ағымдағы өзгерістерді сақтау және жаңарту үшін басыңыз';
$_lang["preview_resource"] = 'Ресурсты алдын ала қарау';
+$_lang["preview_unsupported_url"] = 'Бұл сілтемені алдын ала қарау мүмкін емес: басқару тақтасынан тек http және https мекенжайлары ашылады.';
$_lang["private"] = 'Жеке';
$_lang["public"] = 'Жалпыға ашық';
$_lang["publish_date"] = 'Жариялау күні';
@@ -904,6 +906,7 @@
$_lang["role_edit_plugin"] = 'Плагиндерді өңдеу';
$_lang["role_edit_role"] = 'Рөлдерді өңдеу';
$_lang["role_edit_settings"] = 'Сайт баптауларын өзгерту';
+$_lang["role_security_settings"] = 'Қауіпсіздік баптауларын өзгерту';
$_lang["role_edit_snippet"] = 'Сниппеттерді өңдеу';
$_lang["role_edit_template"] = 'Сайт үлгілерін өңдеу';
$_lang["role_edit_user"] = 'Пайдаланушыларды өңдеу';
@@ -918,6 +921,7 @@
$_lang["role_help"] = 'Көмек беттерін қарау';
$_lang["role_home"] = 'Басқару тақтасының бастапқы бетін сұрау';
$_lang["role_import_static"] = 'HTML импорттау';
+$_lang["role_in_use_message"] = 'Бұл рөл әлі де бір немесе бірнеше пайдаланушыға тағайындалған. Жоймас бұрын бұл пайдаланушыларды басқа рөлге тағайындаңыз.';
$_lang["role_widget_recent_info"] = 'Жақында өңделген/жасалған ресурстар виджетін қарау';
$_lang["role_list_module"] = 'Модуль тізімі';
$_lang["role_manage_metatags"] = 'Сайттың META тегтері мен кілт сөздерін басқару';
diff --git a/core/lang/ky/global.php b/core/lang/ky/global.php
index 0e464b791a..37e214183c 100644
--- a/core/lang/ky/global.php
+++ b/core/lang/ky/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Түзүлгөн ресурс топторун караңыз. Жаңы топ түзүп, атын өзгөртүп, өчүрүп жана топтогу ресурстарды көрө аласыз (аталышын көрүү үчүн ресурстун ID маанисине көрсөткүчтү алып барыңыз). Ресурсту топко кошуу же андан чыгаруу үчүн ресурстун өзүн түзөтүңүз.';
$_lang["access_permissions_user_groups"] = 'Колдонуучу топтору';
$_lang["access_permissions_user_message"] = 'Бул колдонуучу кире турган топторду тандаңыз:';
+$_lang["access_permissions_user_group_locked"] = 'Боз түс менен көрсөтүлгөн топтор сиздин мүчөлүгүңүздөн тышкары. Аларды администратор же "Документ жана колдонуучу топторун башкаруу" уруксаты бар менеджер гана өзгөртө алат, ошондуктан бул жерде алар окуу үчүн гана.';
$_lang["access_permissions_users_in_group"] = 'Топтогу колдонуучулар:';
$_lang["access_permissions_users_tab"] = 'Түзүлгөн колдонуучу топторун карап, жаңысын түзүп, атын өзгөртүп, өчүрүп жана мүчөлөрүн көрө аласыз. Колдонуучуну топко кошуу же андан чыгаруу үчүн колдонуучунун өзүн түзөтүңүз. Администраторлор (ID 1 ролундагы колдонуучулар) бардык ресурстарга дайыма кире алышат, аларды топко кошуу зарыл эмес.';
$_lang["account_email"] = 'Каттоо эсебинин электрондук почтасы';
@@ -758,6 +759,7 @@
$_lang["preview"] = 'Алдын ала көрүү';
$_lang["preview_msg"] = 'Акыркы сакталган өзгөртүүлөр көрсөтүлүүдө. Учурдагы өзгөртүүлөрдү сактап жаңыртуу үчүн басыңыз';
$_lang["preview_resource"] = 'Ресурсту алдын ала көрүү';
+$_lang["preview_unsupported_url"] = 'Бул шилтемени алдын ала көрүү мүмкүн эмес: башкаруу панелинен http жана https даректери гана ачылат.';
$_lang["private"] = 'Жеке';
$_lang["public"] = 'Жалпыга ачык';
$_lang["publish_date"] = 'Жарыялоо күнү';
@@ -883,6 +885,7 @@
$_lang["role_edit_plugin"] = 'Плагиндерди түзөтүү';
$_lang["role_edit_role"] = 'Ролдорду түзөтүү';
$_lang["role_edit_settings"] = 'Сайттын жөндөөлөрүн өзгөртүү';
+$_lang["role_security_settings"] = 'Коопсуздук жөндөөлөрүн өзгөртүү';
$_lang["role_edit_snippet"] = 'Сниппеттерди түзөтүү';
$_lang["role_edit_template"] = 'Сайттын калыптарын түзөтүү';
$_lang["role_edit_user"] = 'Колдонуучуларды түзөтүү';
@@ -897,6 +900,7 @@
$_lang["role_help"] = 'Жардам барактарын көрүү';
$_lang["role_home"] = 'Башкаруу панелинин башкы барагын суроо';
$_lang["role_import_static"] = 'HTML импорттоо';
+$_lang["role_in_use_message"] = 'Бул роль дагы эле бир же бир нече колдонуучуга дайындалган. Аны өчүрүүдөн мурда ал колдонуучуларды башка ролго дайындаңыз.';
$_lang["role_widget_recent_info"] = 'Жакында түзөтүлгөн/түзүлгөн ресурстар виджетин көрүү';
$_lang["role_list_module"] = 'Модулдардын тизмесин көрүү';
$_lang["role_manage_metatags"] = 'Сайттын META тегдерин жана ачкыч сөздөрүн башкаруу';
diff --git a/core/lang/lt/global.php b/core/lang/lt/global.php
index 0b43124765..5763e44d06 100644
--- a/core/lang/lt/global.php
+++ b/core/lang/lt/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Peržiūrėkite sukurtas išteklių grupes. Taip pat galite kurti naujas grupes, jas pervadinti, ištrinti ir peržiūrėti skirtingų grupių išteklius (užveskite pelės žymeklį ant ištekliaus ID, kad pamatytumėte jo pavadinimą). Norėdami pridėti išteklių prie grupės arba jį pašalinti, redaguokite patį išteklių.';
$_lang["access_permissions_user_groups"] = 'Naudotojų grupės';
$_lang["access_permissions_user_message"] = 'Pasirinkite naudotojų grupes, kurioms priklauso šis naudotojas:';
+$_lang["access_permissions_user_group_locked"] = 'Pilkai rodomos grupės yra už jūsų narystės ribų. Jas keisti gali tik administratorius arba vadybininkas, turintis teisę "Tvarkyti dokumentų ir naudotojų grupes", todėl čia jos skirtos tik skaityti.';
$_lang["access_permissions_users_in_group"] = 'Grupės naudotojai:';
$_lang["access_permissions_users_tab"] = 'Peržiūrėkite sukurtas naudotojų grupes. Taip pat galite kurti naujas grupes, jas pervadinti, ištrinti ir peržiūrėti skirtingų grupių narius. Norėdami pridėti naudotoją prie grupės arba jį pašalinti, redaguokite patį naudotoją. Administratoriai (naudotojai, kuriems priskirtas vaidmuo, kurio ID yra 1) visada turi prieigą prie visų išteklių, todėl jų nereikia pridėti prie jokių grupių.';
$_lang["account_email"] = 'Paskyros el. paštas';
@@ -758,6 +759,7 @@
$_lang["preview"] = 'Peržiūra';
$_lang["preview_msg"] = 'Tai paskutinių išsaugotų pakeitimų peržiūra. Spustelėkite čia, kad išsaugotumėte ir atnaujintumėte dabartinius pakeitimus';
$_lang["preview_resource"] = 'Peržiūrėti išteklių';
+$_lang["preview_unsupported_url"] = 'Šios nuorodos peržiūrėti negalima: iš valdymo skydelio atidaromi tik http ir https adresai.';
$_lang["private"] = 'Privatus';
$_lang["public"] = 'Viešas';
$_lang["publish_date"] = 'Paskelbimo data';
@@ -883,6 +885,7 @@
$_lang["role_edit_plugin"] = 'Redaguoti papildinius';
$_lang["role_edit_role"] = 'Redaguoti vaidmenis';
$_lang["role_edit_settings"] = 'Keisti svetainės nuostatas';
+$_lang["role_security_settings"] = 'Keisti saugumo nuostatas';
$_lang["role_edit_snippet"] = 'Redaguoti kodo fragmentus';
$_lang["role_edit_template"] = 'Redaguoti svetainės šablonus';
$_lang["role_edit_user"] = 'Redaguoti naudotojus';
@@ -897,6 +900,7 @@
$_lang["role_help"] = 'Peržiūrėti žinyno puslapius';
$_lang["role_home"] = 'Užklausti administravimo pradžios puslapį';
$_lang["role_import_static"] = 'Importuoti HTML';
+$_lang["role_in_use_message"] = 'Šis vaidmuo vis dar priskirtas vienam ar keliems naudotojams. Prieš ištrindami priskirkite tuos naudotojus kitam vaidmeniui.';
$_lang["role_widget_recent_info"] = 'Peržiūrėti neseniai redaguotų / sukurtų išteklių valdiklį';
$_lang["role_list_module"] = 'Rodyti modulį sąraše';
$_lang["role_manage_metatags"] = 'Tvarkyti svetainės META žymas ir reikšminius žodžius';
diff --git a/core/lang/lv/global.php b/core/lang/lv/global.php
index 2e3657c64f..a0d5257327 100644
--- a/core/lang/lv/global.php
+++ b/core/lang/lv/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Apskatiet izveidotās resursu grupas. Varat arī veidot jaunas grupas, pārdēvēt un dzēst tās, kā arī apskatīt dažādu grupu resursus (novietojiet kursoru virs resursa ID, lai redzētu tā nosaukumu). Lai pievienotu resursu grupai vai izņemtu to no grupas, rediģējiet pašu resursu.';
$_lang["access_permissions_user_groups"] = 'Lietotāju grupas';
$_lang["access_permissions_user_message"] = 'Izvēlieties lietotāju grupas, kurām šis lietotājs pieder:';
+$_lang["access_permissions_user_group_locked"] = 'Pelēkā krāsā parādītās grupas ir ārpus jūsu dalības. Tās var mainīt tikai administrators vai vadītājs ar atļauju "Pārvaldīt dokumentu un lietotāju grupas", tāpēc šeit tās ir tikai lasāmas.';
$_lang["access_permissions_users_in_group"] = 'Lietotāji grupā:';
$_lang["access_permissions_users_tab"] = 'Apskatiet izveidotās lietotāju grupas. Varat arī veidot jaunas grupas, pārdēvēt un dzēst tās, kā arī apskatīt dažādu grupu dalībniekus. Lai pievienotu lietotāju grupai vai izņemtu to no grupas, rediģējiet pašu lietotāju. Administratoriem (lietotājiem, kuriem piešķirta loma ar ID 1) vienmēr ir piekļuve visiem resursiem, tāpēc tie nav jāpievieno nevienai grupai.';
$_lang["account_email"] = 'Konta e-pasta adrese';
@@ -758,6 +759,7 @@
$_lang["preview"] = 'Priekšskatījums';
$_lang["preview_msg"] = 'Šis ir pēdējo saglabāto izmaiņu priekšskatījums. Noklikšķiniet šeit, lai saglabātu un atsvaidzinātu pašreizējās izmaiņas';
$_lang["preview_resource"] = 'Priekšskatīt resursu';
+$_lang["preview_unsupported_url"] = 'Šo saiti nevar priekšskatīt: no pārvaldības paneļa tiek atvērtas tikai http un https adreses.';
$_lang["private"] = 'Privāts';
$_lang["public"] = 'Publisks';
$_lang["publish_date"] = 'Publicēšanas datums';
@@ -883,6 +885,7 @@
$_lang["role_edit_plugin"] = 'Rediģēt spraudņus';
$_lang["role_edit_role"] = 'Rediģēt lomas';
$_lang["role_edit_settings"] = 'Mainīt vietnes iestatījumus';
+$_lang["role_security_settings"] = 'Mainīt drošības iestatījumus';
$_lang["role_edit_snippet"] = 'Rediģēt koda fragmentus';
$_lang["role_edit_template"] = 'Rediģēt vietnes veidnes';
$_lang["role_edit_user"] = 'Rediģēt lietotājus';
@@ -897,6 +900,7 @@
$_lang["role_help"] = 'Skatīt palīdzības lapas';
$_lang["role_home"] = 'Pieprasīt pārvaldības paneļa sākumlapu';
$_lang["role_import_static"] = 'Importēt HTML';
+$_lang["role_in_use_message"] = 'Šī loma joprojām ir piešķirta vienam vai vairākiem lietotājiem. Pirms tās dzēšanas piešķiriet šos lietotājus citai lomai.';
$_lang["role_widget_recent_info"] = 'Skatīt nesen rediģēto/izveidoto resursu logrīku';
$_lang["role_list_module"] = 'Rādīt moduli sarakstā';
$_lang["role_manage_metatags"] = 'Pārvaldīt vietnes META tagus un atslēgvārdus';
diff --git a/core/lang/nl/global.php b/core/lang/nl/global.php
index df39b9c683..42d4932e03 100644
--- a/core/lang/nl/global.php
+++ b/core/lang/nl/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Bekijk hier de Paginagroepen. U kunt ook nieuwe groepen maken, bestaande bewerken of verwijderen of nagaan welke Pagina deel uitmaakt van een groep. Door met de muis over de ID van de Pagina te bewegen, ziet u de naam ervan. Door de Pagina te bewerken kunt u de groepsrechten wijzigen.';
$_lang["access_permissions_user_groups"] = 'Gebruikersgroepen';
$_lang["access_permissions_user_message"] = 'Geef aan tot welke groepen deze Gebruiker behoort:';
+$_lang["access_permissions_user_group_locked"] = 'Grijs weergegeven groepen vallen buiten uw eigen lidmaatschap. Alleen een beheerder of een manager met de rechten "Document- en gebruikersgroepen beheren" kan ze wijzigen, dus ze zijn hier alleen-lezen.';
$_lang["access_permissions_users_in_group"] = 'Gebruikers in deze groep:';
$_lang["access_permissions_users_tab"] = 'Bekijk de Gebruikersgroepen. U kunt ook nieuwe groepen maken, bestaande bewerken of verwijderen, of nagaan wie deel uitmaakt van een groep. Om een Gebruiker uit een groep te halen of aan een groep toe te wijzen, wijzigt u de gegevens van de Gebruiker. Beheerders (dit zijn Gebruikers met profiel ID 1) hebben altijd toegang tot alle Pagina\'s dus hoeft u hen niet toe te voegen aan een groep.';
$_lang["account_email"] = 'E-mail account';
@@ -720,6 +721,7 @@
$_lang["preview"] = 'Voorbeeld';
$_lang["preview_msg"] = 'Dit is een voorbeeld van uw laatst opgeslagen wijzigingen. Klik hier om uw huidige wijzigingen op te slaan en te vernieuwen.';
$_lang["preview_resource"] = 'Pagina voorbeeld';
+$_lang["preview_unsupported_url"] = 'Van deze link kan geen voorbeeld worden getoond: vanuit de Manager worden alleen http- en https-adressen geopend.';
$_lang["private"] = 'Gesloten';
$_lang["public"] = 'Open';
$_lang["publish_date"] = 'Publicatiedatum';
@@ -840,6 +842,7 @@
$_lang["role_edit_plugin"] = 'Plug-ins bewerken';
$_lang["role_edit_role"] = 'Rollen bewerken';
$_lang["role_edit_settings"] = 'Website instellingen veranderen';
+$_lang["role_security_settings"] = 'Beveiligingsinstellingen veranderen';
$_lang["role_edit_snippet"] = 'Snippets bewerken';
$_lang["role_edit_template"] = 'Website Templates bewerken';
$_lang["role_edit_user"] = 'Bewerk manager gebruikers';
@@ -854,6 +857,7 @@
$_lang["role_help"] = 'Help Pagina\'s weergeven';
$_lang["role_home"] = 'Verzoek om Content Management Systeem Intropagina';
$_lang["role_import_static"] = 'Importeer HTML';
+$_lang["role_in_use_message"] = 'Deze rol is nog toegewezen aan een of meer gebruikers. Wijs deze gebruikers aan een andere rol toe voordat de rol kan worden verwijderd.';
$_lang["role_widget_recent_info"] = 'Widget Recent gemaakte/bewerkte Pagina\'s weergeven';
$_lang["role_manage_metatags"] = 'Beheer \'META tags\' en keywords';
$_lang["role_management_msg"] = 'Hier kunt u kiezen welke Rol u wenst te wijzigen.';
diff --git a/core/lang/nn/global.php b/core/lang/nn/global.php
index e88ca83cf5..92b0ab3499 100644
--- a/core/lang/nn/global.php
+++ b/core/lang/nn/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Her kan du se hvilke dokumentgrupper som har blitt konfigurert. Du kan til og med lage nye og bytte navn på grupper, samt se hvilke dokumenter som tilhører en spesifikk gruppe (hold musepekeren over dokumentets ID for å se navnet på dokumentet). For å legge til eller fjerne et dokument fra en gruppe, rediger dokumentet direkte.';
$_lang["access_permissions_user_groups"] = 'Brukergrupper';
$_lang["access_permissions_user_message"] = 'Her kan du velge hvilke brukergrupper denne brukeren skal tilhøre:';
+$_lang["access_permissions_user_group_locked"] = 'Grupper vist i grått er utanfor ditt eige medlemskap. Berre ein administrator eller ein leiar med løyvet "Administrer dokument- og brukargrupper" kan endra dei, så dei er skriveverna her.';
$_lang["access_permissions_users_in_group"] = 'Brukere i gruppen: ';
$_lang["access_permissions_users_tab"] = 'Her kan du se hvilke brukergrupper som har blitt konfigurert. Du kan også opprette nye, bytte navn på og fjerne grupper, samt se hvilke brukere som er medlemmer i en spesifikk gruppe. For å legge til eller fjerne en bruker fra en gruppe, rediger brukeren direkte. Administratorer (som har rolle-id 1) har alltid tillgang til alle dokumenter, så de trenger ikke å legges til noen grupper.';
$_lang["account_email"] = 'Konto e-post';
@@ -687,6 +688,7 @@
$_lang["preview"] = 'Forhåndsvis';
$_lang["preview_msg"] = 'Dette er en forhåndsvisning av dine siste endringer. Klikk her for å lagre og laste om dine nåværende endringer';
$_lang["preview_resource"] = 'Preview Resource';
+$_lang["preview_unsupported_url"] = 'Denne lenken kan ikke forhåndsvises: bare http- og https-adresser åpnes fra håndtereren.';
$_lang["private"] = 'Privat';
$_lang["public"] = 'Offentlig';
$_lang["publish_date"] = 'Publiseringsdato';
@@ -801,6 +803,7 @@
$_lang["role_edit_plugin"] = 'Rediger pluginer';
$_lang["role_edit_role"] = 'Rediger roller';
$_lang["role_edit_settings"] = 'Endre webplassens instillinger';
+$_lang["role_security_settings"] = 'Endre sikkerheitsinstillingar';
$_lang["role_edit_snippet"] = 'Rediger kodesnutter';
$_lang["role_edit_template"] = 'Rediger maler';
$_lang["role_edit_user"] = 'Rediger brukere';
@@ -814,6 +817,7 @@
$_lang["role_help"] = 'Vis hjelpesider';
$_lang["role_home"] = 'Last introduksjonssiden';
$_lang["role_import_static"] = 'Import HTML';
+$_lang["role_in_use_message"] = 'Denne rolla er framleis tildelt éin eller fleire brukarar. Tildel desse brukarane ei anna rolle før ho kan slettast.';
$_lang["role_widget_recent_info"] = 'Vis widgeten Nylig opprettede/redigerte dokumenter';
$_lang["role_manage_metatags"] = 'Håndtere webplassens META-tagger og nøkkelord';
$_lang["role_management_msg"] = 'Her kan du opprette en ny rolle eller velge en allerede eksisterende rolle for redigering.';
diff --git a/core/lang/pl/global.php b/core/lang/pl/global.php
index 66b36f8c69..bb24dae257 100644
--- a/core/lang/pl/global.php
+++ b/core/lang/pl/global.php
@@ -42,6 +42,7 @@
$_lang["access_permissions_resources_tab"] = 'Tutaj możesz zobaczyć jakie grupy dokumentów zostały stworzone. Możesz także stworzyć nowe grupy, zmieniać nazwy, usuwać i sprawdzać które dokumenty należą do danej grupy (najedź kursorem na ID dokumentu, aby zobaczyć jego nazwę). Aby dodać dokument do grupy albo usunąć go z grupy, edytuj dokument bezpośrednio.';
$_lang["access_permissions_user_groups"] = 'Grupy użytkowników';
$_lang["access_permissions_user_message"] = 'Wybierz do których grup należy ten użytkownik:';
+$_lang["access_permissions_user_group_locked"] = 'Grupy wyświetlone na szaro znajdują się poza Twoim członkostwem. Mogą je zmieniać tylko administrator lub menedżer z uprawnieniem "Zarządzanie grupami dokumentów i użytkowników", dlatego tutaj są tylko do odczytu.';
$_lang["access_permissions_users_in_group"] = 'Użytkownicy w grupie: ';
$_lang["access_permissions_users_tab"] = 'Tutaj możesz zobaczyć jakie grupy użytkowników zostały stworzone. Możesz także stworzyć nowe grupy, zmieniać nazwy, usuwać i sprawdzać którzy użytkownicy należą do danej grupy. Aby dodać nowego użytkownika do grupy albo usunać go z grupy, edytuj tego użytkownika bezpośrednio. Administratorzy (ID Roli 1) zawsze mają dostęp do wszystkich dokumentów, więc nie muszą być przypisani do żadnej grupy.';
$_lang["account_email"] = 'Konto e-mail';
@@ -733,6 +734,7 @@
$_lang["preview"] = 'Podgląd';
$_lang["preview_msg"] = 'To jest podgląd twoich ostatnio zapisanych zmian. Kliknij tutaj aby zapisać i odświeżyć obecne zmiany';
$_lang["preview_resource"] = 'Podgląd dokumentu';
+$_lang["preview_unsupported_url"] = 'Tego linku nie można wyświetlić w podglądzie: z menedżera otwierane są tylko adresy http i https.';
$_lang["private"] = 'Prywatny';
$_lang["public"] = 'Publiczny';
$_lang["publish_date"] = 'Data publikacji';
@@ -853,6 +855,7 @@
$_lang["role_edit_plugin"] = 'Edytuj wtyczki';
$_lang["role_edit_role"] = 'Edytowanie ról';
$_lang["role_edit_settings"] = 'Zmiana ustawień witryny';
+$_lang["role_security_settings"] = 'Zmiana ustawień zabezpieczeń';
$_lang["role_edit_snippet"] = 'Edytowanie snippetów';
$_lang["role_edit_template"] = 'Edytowanie szablonów';
$_lang["role_edit_user"] = 'Edytuj użytkowników Menedżera';
@@ -867,6 +870,7 @@
$_lang["role_help"] = 'Przeglądanie stron pomocy';
$_lang["role_home"] = 'Pokaż stronę wprowadzającą menedżera';
$_lang["role_import_static"] = 'Import HTML';
+$_lang["role_in_use_message"] = 'Ta rola jest nadal przypisana do co najmniej jednego użytkownika. Przed jej usunięciem przypisz tych użytkowników do innej roli.';
$_lang["role_widget_recent_info"] = 'Wyświetl widżet Ostatnio edytowane dokumenty';
$_lang["role_manage_metatags"] = 'Zarządzaj META tagami i słowami kluczowymi witryny';
$_lang["role_management_msg"] = 'Tutaj możesz wybrać, którą rolę chcesz edytować.';
diff --git a/core/lang/pt/global.php b/core/lang/pt/global.php
index 05103053bd..3eddc822ad 100644
--- a/core/lang/pt/global.php
+++ b/core/lang/pt/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Aqui pode ver que grupos de documentos estão definidos. Pode criar novos grupos, alterar o nome dos grupos, apagar grupos e ver que documentos pertencem aos diferentes grupos. Para adicionar ou remover um documento de um grupo, edite-o directamente.';
$_lang["access_permissions_user_groups"] = 'Grupos de utilizadores';
$_lang["access_permissions_user_message"] = 'Aqui pode seleccionar a que grupos este utilizador pertence.';
+$_lang["access_permissions_user_group_locked"] = 'Os grupos apresentados a cinzento estão fora da sua associação. Apenas um administrador ou um gestor com a permissão "Gerir grupos de documentos e utilizadores" os pode alterar, pelo que aqui são apenas de leitura.';
$_lang["access_permissions_users_in_group"] = 'Utilizadores no grupo: ';
$_lang["access_permissions_users_tab"] = 'Aqui pode ver que grupos de utilizadores estão definidos. Pode criar novos grupos, alterar o nome dos grupos, apagar grupos e ver quem são os membros dos diferentes grupos. Para adicionar ou remover um utilizador de um grupo, edite o utilizador directamente. Administradores (utilizadores a quem foi atribuído o perfil com ID 1) têm sempre acesso a todos os documentos e não precisam de ser adicionados a nenhum grupo.';
$_lang["account_email"] = 'Conta de E-mail';
@@ -687,6 +688,7 @@
$_lang["preview"] = 'Prever Site';
$_lang["preview_msg"] = 'Esta é uma pré-visualização das últimas alterações guardadas. Clique aqui para Guardar e actualizar as alterações mais recentes';
$_lang["preview_resource"] = 'Prever documento';
+$_lang["preview_unsupported_url"] = 'Esta ligação não pode ser pré-visualizada: a partir do administrador só se abrem endereços http e https.';
$_lang["private"] = 'Privado';
$_lang["public"] = 'Público';
$_lang["publish_date"] = 'Data de Publicação';
@@ -801,6 +803,7 @@
$_lang["role_edit_plugin"] = 'Editar Plugins';
$_lang["role_edit_role"] = 'Editar Perfis';
$_lang["role_edit_settings"] = 'Alterar configurações do site';
+$_lang["role_security_settings"] = 'Alterar configurações de segurança';
$_lang["role_edit_snippet"] = 'Editar Snippets';
$_lang["role_edit_template"] = 'Editar Templates';
$_lang["role_edit_user"] = 'Editar utilizadores';
@@ -814,6 +817,7 @@
$_lang["role_help"] = 'Ver páginas de ajuda';
$_lang["role_home"] = 'Pedir página de início do Interface de Gestão';
$_lang["role_import_static"] = 'Importar HTML';
+$_lang["role_in_use_message"] = 'Esta função ainda está atribuída a um ou mais utilizadores. Reatribua esses utilizadores a outra função antes de a poder eliminar.';
$_lang["role_widget_recent_info"] = 'Ver widget Documentos editados/criados recentemente';
$_lang["role_manage_metatags"] = 'Gerir etiquetas (tags) META e palavras-chave do site';
$_lang["role_management_msg"] = 'Aqui pode escolher qual o perfil que deseja alterar.';
diff --git a/core/lang/ro/global.php b/core/lang/ro/global.php
index 56d3e7c113..92e190f4f1 100644
--- a/core/lang/ro/global.php
+++ b/core/lang/ro/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Vizualizați grupurile de resurse create. Puteți crea grupuri noi, redenumi sau șterge grupuri și vedea resursele din fiecare grup (treceți cursorul peste ID-ul resursei pentru a-i vedea numele). Pentru a adăuga o resursă într-un grup sau a o elimina dintr-un grup, editați direct resursa.';
$_lang["access_permissions_user_groups"] = 'Grupuri de utilizatori';
$_lang["access_permissions_user_message"] = 'Selectați grupurile de utilizatori din care face parte acest utilizator:';
+$_lang["access_permissions_user_group_locked"] = 'Grupurile afișate cu gri se află în afara apartenenței dumneavoastră. Doar un administrator sau un manager cu permisiunea "Gestionarea grupurilor de documente și utilizatori" le poate modifica, așa că aici sunt doar pentru citire.';
$_lang["access_permissions_users_in_group"] = 'Utilizatori în grup:';
$_lang["access_permissions_users_tab"] = 'Vizualizați grupurile de utilizatori create. Puteți crea grupuri noi, redenumi sau șterge grupuri și vedea utilizatorii din fiecare grup. Pentru a adăuga un utilizator într-un grup sau a-l elimina dintr-un grup, editați direct utilizatorul. Administratorii (utilizatorii cărora li s-a atribuit rolul cu ID-ul 1) au întotdeauna acces la toate resursele, deci nu trebuie adăugați în niciun grup.';
$_lang["account_email"] = 'Adresa de e-mail a contului';
@@ -758,6 +759,7 @@
$_lang["preview"] = 'Previzualizare';
$_lang["preview_msg"] = 'Aceasta este o previzualizare a ultimelor modificări salvate. Apăsați aici pentru a salva și reîmprospăta modificările curente';
$_lang["preview_resource"] = 'Previzualizează resursa';
+$_lang["preview_unsupported_url"] = 'Acest link nu poate fi previzualizat: din panoul de administrare se deschid doar adrese http și https.';
$_lang["private"] = 'Privat';
$_lang["public"] = 'Public';
$_lang["publish_date"] = 'Data publicării';
@@ -883,6 +885,7 @@
$_lang["role_edit_plugin"] = 'Editarea pluginurilor';
$_lang["role_edit_role"] = 'Editarea rolurilor';
$_lang["role_edit_settings"] = 'Modificarea setărilor site-ului';
+$_lang["role_security_settings"] = 'Modificarea setărilor de securitate';
$_lang["role_edit_snippet"] = 'Editarea fragmentelor de cod';
$_lang["role_edit_template"] = 'Editarea șabloanelor site-ului';
$_lang["role_edit_user"] = 'Editarea utilizatorilor';
@@ -897,6 +900,7 @@
$_lang["role_help"] = 'Vizualizarea paginilor de ajutor';
$_lang["role_home"] = 'Solicitarea paginii de întâmpinare a interfeței de administrare';
$_lang["role_import_static"] = 'Importarea HTML-ului';
+$_lang["role_in_use_message"] = 'Acest rol este încă atribuit unuia sau mai multor utilizatori. Reatribuiți acești utilizatori unui alt rol înainte de a-l putea șterge.';
$_lang["role_widget_recent_info"] = 'Vizualizarea widgetului Resurse editate/create recent';
$_lang["role_list_module"] = 'Listarea modulului';
$_lang["role_manage_metatags"] = 'Gestionarea etichetelor META și a cuvintelor-cheie ale site-ului';
diff --git a/core/lang/sk/global.php b/core/lang/sk/global.php
index 6c2f8482cb..a6705b8a29 100644
--- a/core/lang/sk/global.php
+++ b/core/lang/sk/global.php
@@ -42,6 +42,7 @@
$_lang["access_permissions_resources_tab"] = 'Tu môžete vidieť všetky skupiny zdrojov. Tiež tu môžete vytvoriť, premenovať, odstrániť skupinu a zobraziť zdroje v skupine. Ak chcete pridať alebo odstrániť zdroj zo skupiny, upravte priamo daný zdroj.';
$_lang["access_permissions_user_groups"] = 'Skupiny používateľov';
$_lang["access_permissions_user_message"] = 'Tu môžete vybrať, do ktorej skupiny používateľov tento používateľ patrí:';
+$_lang["access_permissions_user_group_locked"] = 'Skupiny zobrazené sivou farbou sú mimo vášho členstva. Zmeniť ich môže iba administrátor alebo správca s oprávnením "Správa skupín dokumentov a používateľov", preto sú tu len na čítanie.';
$_lang["access_permissions_users_in_group"] = 'Používatelia v skupine:';
$_lang["access_permissions_users_tab"] = 'Tu môžete vidieť všetky skupiny používateľov. Tiež tu môžete vytvoriť, premenovať, odstrániť skupinu a zobraziť členov skupiny. Ak chcete pridať alebo odstrániť používateľa zo skupiny, upravte priamo daného používateľa. Manažéri majú vždy prístup ku všetkým zdrojom.';
$_lang["account_email"] = 'E-mail účtu';
@@ -751,6 +752,7 @@
$_lang["preview"] = 'Náhľad';
$_lang["preview_msg"] = 'Náhľad posledných zmien. Uložiť a obnoviť posledné zmeny';
$_lang["preview_resource"] = 'Náhľad';
+$_lang["preview_unsupported_url"] = 'Tento odkaz nie je možné zobraziť v náhľade: z manažéra sa otvárajú iba adresy http a https.';
$_lang["private"] = 'Súkromný';
$_lang["public"] = 'Dostupný pre všetkých';
$_lang["publish_date"] = 'Dátum publikovania';
@@ -872,6 +874,7 @@
$_lang["role_edit_plugin"] = 'Úprava pluginov';
$_lang["role_edit_role"] = 'Úprava rolí';
$_lang["role_edit_settings"] = 'Zmena konfigurácie webu';
+$_lang["role_security_settings"] = 'Zmena nastavení zabezpečenia';
$_lang["role_edit_snippet"] = 'Úprava snippetov';
$_lang["role_edit_template"] = 'Úprava šablón';
$_lang["role_edit_user"] = 'Úprava používateľov';
@@ -886,6 +889,7 @@
$_lang["role_help"] = 'Zobrazenie stránok pomoci';
$_lang["role_home"] = 'Požiadavka na úvodnú stránku administrácie';
$_lang["role_import_static"] = 'Import HTML';
+$_lang["role_in_use_message"] = 'Táto rola je stále priradená jednému alebo viacerým používateľom. Pred jej odstránením priraďte týchto používateľov k inej role.';
$_lang["role_widget_recent_info"] = 'Zobrazenie widgetu „Nedávno vytvorené/upravené zdroje“';
$_lang["role_list_module"] = 'Zobrazenie zoznamu modulov';
$_lang["role_manage_metatags"] = 'META tagy a kľúčové slová';
diff --git a/core/lang/sv/global.php b/core/lang/sv/global.php
index ceb2e81481..73742990f5 100644
--- a/core/lang/sv/global.php
+++ b/core/lang/sv/global.php
@@ -42,6 +42,7 @@
$_lang["access_permissions_resources_tab"] = 'Se vilka resursgrupper som har skapats. Det går också att skapa nya eller byta namn på grupper samt se vilka resurser som tillhör de olika grupperna (håll musen över resursens ID för att se dess namn). För att lägga till eller ta bort en resurs från en grupp - redigera resursen direkt.';
$_lang["access_permissions_user_groups"] = 'Användargrupper';
$_lang["access_permissions_user_message"] = 'Ange vilka användargrupper denna användare ska tillhöra:';
+$_lang["access_permissions_user_group_locked"] = 'Grupper som visas i grått ligger utanför ditt eget medlemskap. Endast en administratör eller en förvaltare med behörigheten "Hantera dokument- och användargrupper" kan ändra dem, så de är skrivskyddade här.';
$_lang["access_permissions_users_in_group"] = 'Användare i gruppen:';
$_lang["access_permissions_users_tab"] = 'Se de användargrupper som har skapats. Du kan också skapa nya, byta namn på och ta bort grupper samt se vilka användare som är medlemmar i de olika grupperna. För att lägga till eller ta bort en användare från en grupp - redigera användaren direkt. Administratörer (som har roll-ID 1) har alltid tillgång till alla resurser, så de behöver inte läggas till i några grupper.';
$_lang["account_email"] = 'Kontots e-post';
@@ -688,6 +689,7 @@
$_lang["preview"] = 'Förhandsgranska';
$_lang["preview_msg"] = 'Detta är en förhandsgranskning av dina senast sparade ändringar. Klicka här för att spara och ladda om dina nuvarande ändringar';
$_lang["preview_resource"] = 'Förhandsgranska';
+$_lang["preview_unsupported_url"] = 'Den här länken kan inte förhandsgranskas: endast http- och https-adresser öppnas från hanteraren.';
$_lang["private"] = 'Privat';
$_lang["public"] = 'Offentlig';
$_lang["publish_date"] = 'Publiceringsdatum';
@@ -802,6 +804,7 @@
$_lang["role_edit_plugin"] = 'Redigera plugins';
$_lang["role_edit_role"] = 'Redigera roller';
$_lang["role_edit_settings"] = 'Ändra webbplatsens inställningar';
+$_lang["role_security_settings"] = 'Ändra säkerhetsinställningar';
$_lang["role_edit_snippet"] = 'Redigera snippets';
$_lang["role_edit_template"] = 'Redigera mallar';
$_lang["role_edit_user"] = 'Redigera användare';
@@ -815,6 +818,7 @@
$_lang["role_help"] = 'Visa hjälpsidor';
$_lang["role_home"] = 'Ladda introduktionssidan';
$_lang["role_import_static"] = 'Importera HTML';
+$_lang["role_in_use_message"] = 'Den här rollen är fortfarande tilldelad en eller flera användare. Tilldela dessa användare en annan roll innan den kan tas bort.';
$_lang["role_widget_recent_info"] = 'Visa widget Nyligen skapade/redigerade resurser';
$_lang["role_manage_metatags"] = 'Hantera webbplatsens META-taggar och nyckelord';
$_lang["role_management_msg"] = 'Skapa en ny roll eller välja en redan befintlig för redigering.';
diff --git a/core/lang/tg/global.php b/core/lang/tg/global.php
index 8fb742302f..9f97630c81 100644
--- a/core/lang/tg/global.php
+++ b/core/lang/tg/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Гурӯҳҳои сохташударо бинед, гурӯҳ созед, номашро иваз ё ҳазф кунед ва захираҳои гурӯҳҳоро бинед (барои дидани ном нишонаро болои ID гузоред). Барои илова ё хориҷ кардани захира аз гурӯҳ худи захираро таҳрир кунед.';
$_lang["access_permissions_user_groups"] = 'Гурӯҳҳои корбарон';
$_lang["access_permissions_user_message"] = 'Гурӯҳҳоеро интихоб кунед, ки корбар ба онҳо тааллуқ дорад:';
+$_lang["access_permissions_user_group_locked"] = 'Гурӯҳҳое, ки бо ранги хокистарӣ нишон дода шудаанд, берун аз узвияти шумо мебошанд. Онҳоро танҳо администратор ё менеҷере, ки иҷозати "Идоракунии гурӯҳҳои ҳуҷҷатҳо ва корбарон"-ро дорад, тағйир дода метавонад, бинобар ин дар ин ҷо онҳо танҳо барои хондан мебошанд.';
$_lang["access_permissions_users_in_group"] = 'Корбарони гурӯҳ:';
$_lang["access_permissions_users_tab"] = 'Гурӯҳҳои корбаронро бинед, созед, номашонро иваз ё ҳазф кунед ва аъзои гурӯҳҳоро бинед. Барои илова ё хориҷ кардани корбар аз гурӯҳ худи корбарро таҳрир кунед. Мудирон (нақши ID 1) ҳамеша ба ҳамаи захираҳо дастрасӣ доранд ва ба узвияти гурӯҳ ниёз надоранд.';
$_lang["account_email"] = 'Почтаи электронии ҳисоб';
@@ -753,6 +754,7 @@
$_lang["preview"] = 'Пешнамоиш';
$_lang["preview_msg"] = 'Ин пешнамоиши тағйироти охирини захирашуда аст. Барои тағйироти ҷорӣ Захира ва навсозӣ-ро пахш кунед';
$_lang["preview_resource"] = 'Пешнамоиши захира';
+$_lang["preview_unsupported_url"] = 'Ин истинодро пешнамоиш кардан ғайриимкон аст: аз панели идоракунӣ танҳо суроғаҳои http ва https кушода мешаванд.';
$_lang["private"] = 'Хусусӣ';
$_lang["public"] = 'Умумӣ';
$_lang["publish_date"] = 'Санаи нашр';
@@ -878,6 +880,7 @@
$_lang["role_edit_plugin"] = 'Таҳрири плагинҳо';
$_lang["role_edit_role"] = 'Таҳрири нақшҳо';
$_lang["role_edit_settings"] = 'Тағйири танзимоти сомона';
+$_lang["role_security_settings"] = 'Тағйири танзимоти амният';
$_lang["role_edit_snippet"] = 'Таҳрири сниппетҳо';
$_lang["role_edit_template"] = 'Таҳрири қолабҳои сомона';
$_lang["role_edit_user"] = 'Таҳрири корбарон';
@@ -892,6 +895,7 @@
$_lang["role_help"] = 'Дидани саҳифаҳои кумак';
$_lang["role_home"] = 'Дархости саҳифаи оғози панел';
$_lang["role_import_static"] = 'Воридоти HTML';
+$_lang["role_in_use_message"] = 'Ин нақш ҳоло ҳам ба як ё якчанд корбар таъин шудааст. Пеш аз нест кардан, ин корбаронро ба нақши дигар таъин кунед.';
$_lang["role_widget_recent_info"] = 'Дидани виҷети захираҳои ба наздикӣ таҳриршуда/сохташуда';
$_lang["role_list_module"] = 'Рӯйхати модулҳо';
$_lang["role_manage_metatags"] = 'Идоракунии META ва калимаҳои калидии сомона';
diff --git a/core/lang/tk/global.php b/core/lang/tk/global.php
index f8f92a5339..da97da4d98 100644
--- a/core/lang/tk/global.php
+++ b/core/lang/tk/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Döredilen resurs toparlaryny görüň, täze topar dörediň, adyny üýtgediň ýa-da pozuň. Resursyň adyny görmek üçin onuň ID-siniň üstüne syçanjygy eltiň. Resursy topara goşmak ýa-da aýyrmak üçin resursyň özüni redaktirläň.';
$_lang["access_permissions_user_groups"] = 'Ulanyjy toparlary';
$_lang["access_permissions_user_message"] = 'Bu ulanyjynyň degişli toparlaryny saýlaň:';
+$_lang["access_permissions_user_group_locked"] = 'Çal reňkde görkezilen toparlar siziň agzalygyňyzdan daşarda. Olary diňe administrator ýa-da "Resminama we ulanyjy toparlaryny dolandyrmak" rugsady bolan menejer üýtgedip biler, şonuň üçin bu ýerde olar diňe okamak üçindir.';
$_lang["access_permissions_users_in_group"] = 'Topardaky ulanyjylar:';
$_lang["access_permissions_users_tab"] = 'Döredilen ulanyjy toparlaryny we olaryň agzalaryny görüň. Toparlary döredip, atlaryny üýtgedip we pozup bilersiňiz. Ulanyjyny goşmak ýa-da aýyrmak üçin ulanyjynyň özüni redaktirläň. ID-si 1 bolan roldaky dolandyryjylar ähli resurslara girip bilýärler, olary toparlara goşmak gerek däl.';
$_lang["account_email"] = 'Hasabyň e-poçtasy';
@@ -758,6 +759,7 @@
$_lang["preview"] = 'Öňünden gör';
$_lang["preview_msg"] = 'Bu soňky ýatda saklanan üýtgetmeleriňiziň deslapky görnüşidir. Häzirki üýtgetmeleriňizi ýatda saklamak we täzelemek üçin şu ýere basyň';
$_lang["preview_resource"] = 'Resursy öňünden gör';
+$_lang["preview_unsupported_url"] = 'Bu baglanyşygy öňünden görüp bolmaýar: dolandyryş panelinden diňe http we https salgylary açylýar.';
$_lang["private"] = 'Hususy';
$_lang["public"] = 'Jemgyýetçilik';
$_lang["publish_date"] = 'Neşir senesi';
@@ -883,6 +885,7 @@
$_lang["role_edit_plugin"] = 'Plaginleri redaktirlemek';
$_lang["role_edit_role"] = 'Rollary redaktirlemek';
$_lang["role_edit_settings"] = 'Saýtyň sazlamalaryny üýtgetmek';
+$_lang["role_security_settings"] = 'Howpsuzlyk sazlamalaryny üýtgetmek';
$_lang["role_edit_snippet"] = 'Snippetleri redaktirlemek';
$_lang["role_edit_template"] = 'Saýtyň şablonlaryny redaktirlemek';
$_lang["role_edit_user"] = 'Ulanyjylary redaktirlemek';
@@ -897,6 +900,7 @@
$_lang["role_help"] = 'Kömek sahypalaryny görmek';
$_lang["role_home"] = 'Dolandyryşyň giriş sahypasyny soramak';
$_lang["role_import_static"] = 'HTML import etmek';
+$_lang["role_in_use_message"] = 'Bu rol entek bir ýa-da birnäçe ulanyja bellenen. Ony pozmazdan ozal şol ulanyjylary başga rola belläň.';
$_lang["role_widget_recent_info"] = 'Ýakynda redaktirlenen/döredilen resurslar widjetini görmek';
$_lang["role_list_module"] = 'Moduly sanawda görkezmek';
$_lang["role_manage_metatags"] = 'Saýtyň META teglerini we açar sözlerini dolandyrmak';
diff --git a/core/lang/uk/global.php b/core/lang/uk/global.php
index fdd06293d6..b0c2311678 100644
--- a/core/lang/uk/global.php
+++ b/core/lang/uk/global.php
@@ -42,6 +42,7 @@
$_lang["access_permissions_resources_tab"] = 'Тут ви можете побачити всі групи ресурсів. Також, тут ви можете створити, перейменувати, видалити групу, переглянути ресурси в групі. Для додавання або видалення ресурсу з групи перейдіть в редагування ресурсу.';
$_lang["access_permissions_user_groups"] = 'Групи користувачів';
$_lang["access_permissions_user_message"] = 'Тут ви можете вибрати, до якої групи користувачів належить цей користувач:';
+$_lang["access_permissions_user_group_locked"] = 'Групи, показані сірим, перебувають поза вашим членством. Змінювати їх може лише адміністратор або менеджер з дозволом "Керування групами документів і користувачів", тому тут вони доступні лише для читання.';
$_lang["access_permissions_users_in_group"] = 'Користувачі в групі:';
$_lang["access_permissions_users_tab"] = 'Тут ви можете побачити всі групи користувачів. Також, тут ви можете створити, перейменувати, видалити групу, переглянути членів групи. Щоб додати або видалити користувача з групи редагуйте користувача безпосередньо. Менеджери завжди мають доступ до всіх ресурсів.';
$_lang["account_email"] = 'E-mail облікового запису';
@@ -765,6 +766,7 @@
$_lang["preview"] = 'Перегляд';
$_lang["preview_msg"] = 'Перегляд останніх змін. Зберегти і оновити останні зміни';
$_lang["preview_resource"] = 'Перегляд';
+$_lang["preview_unsupported_url"] = 'Це посилання неможливо переглянути: з менеджера відкриваються лише адреси http і https.';
$_lang["private"] = 'Особистий';
$_lang["public"] = 'Доступний для всіх';
$_lang["publish_date"] = 'Дата публікації';
@@ -886,6 +888,7 @@
$_lang["role_edit_plugin"] = 'Редагування плагінів';
$_lang["role_edit_role"] = 'Редагування ролей';
$_lang["role_edit_settings"] = 'Змінювати конфігурацію сайта';
+$_lang["role_security_settings"] = 'Змінювати налаштування безпеки';
$_lang["role_edit_snippet"] = 'Редагування сниппетов';
$_lang["role_edit_template"] = 'Редагування шаблонов';
$_lang["role_edit_user"] = 'Редагування пользователей';
@@ -900,6 +903,7 @@
$_lang["role_help"] = 'Перегляд сторінок допомоги';
$_lang["role_home"] = 'Запит вхідної сторінки менеджера';
$_lang["role_import_static"] = 'Імпорт HTML';
+$_lang["role_in_use_message"] = 'Ця роль усе ще призначена одному або кільком користувачам. Перш ніж видалити її, призначте цих користувачів до іншої ролі.';
$_lang["role_widget_recent_info"] = 'Перегляд віджета «Нещодавно створені / відредаговані ресурси»';
$_lang["role_list_module"] = 'Перегляд списка модулів';
$_lang["role_manage_metatags"] = 'Управління META-тегами і ключовими словами';
diff --git a/core/lang/uz/global.php b/core/lang/uz/global.php
index eff75e6648..634ec5513b 100644
--- a/core/lang/uz/global.php
+++ b/core/lang/uz/global.php
@@ -41,6 +41,7 @@
$_lang["access_permissions_resources_tab"] = 'Yaratilgan resurslar guruhlarini ko\'ring. Shuningdek, yangi guruhlar yaratishingiz, ularni qayta nomlashingiz, o\'chirishingiz va turli guruhlardagi resurslarni ko\'rishingiz mumkin (resurs nomini ko\'rish uchun uning ID raqamiga kursorni olib boring). Resursni guruhga qo\'shish yoki undan chiqarish uchun resursning o\'zini tahrirlang.';
$_lang["access_permissions_user_groups"] = 'Foydalanuvchilar guruhlari';
$_lang["access_permissions_user_message"] = 'Bu foydalanuvchi a\'zo bo\'lgan foydalanuvchilar guruhlarini tanlang:';
+$_lang["access_permissions_user_group_locked"] = 'Kulrang ko\'rsatilgan guruhlar sizning a\'zoligingizdan tashqarida. Ularni faqat administrator yoki "Hujjatlar va foydalanuvchilar guruhlarini boshqarish" ruxsatiga ega menejer o\'zgartira oladi, shuning uchun ular bu yerda faqat o\'qish uchun.';
$_lang["access_permissions_users_in_group"] = 'Guruhdagi foydalanuvchilar:';
$_lang["access_permissions_users_tab"] = 'Yaratilgan foydalanuvchilar guruhlarini ko\'ring. Shuningdek, yangi guruhlar yaratishingiz, ularni qayta nomlashingiz, o\'chirishingiz va turli guruhlar a\'zolarini ko\'rishingiz mumkin. Foydalanuvchini guruhga qo\'shish yoki undan chiqarish uchun foydalanuvchining o\'zini tahrirlang. Administratorlar (ID raqami 1 bo\'lgan rol berilgan foydalanuvchilar) doimo barcha resurslarga kira oladi, shuning uchun ularni hech qanday guruhga qo\'shish shart emas.';
$_lang["account_email"] = 'Hisobning elektron pochta manzili';
@@ -758,6 +759,7 @@
$_lang["preview"] = 'Oldindan ko‘rish';
$_lang["preview_msg"] = 'Bu oxirgi saqlangan o‘zgarishlaringizning ko‘rinishi. Joriy o‘zgarishlarni saqlash va yangilash uchun shu yerni bosing';
$_lang["preview_resource"] = 'Resursni oldindan ko‘rish';
+$_lang["preview_unsupported_url"] = 'Bu havolani oldindan ko‘rib bo‘lmaydi: boshqaruv panelidan faqat http va https manzillari ochiladi.';
$_lang["private"] = 'Shaxsiy';
$_lang["public"] = 'Ommaviy';
$_lang["publish_date"] = 'Nashr sanasi';
@@ -883,6 +885,7 @@
$_lang["role_edit_plugin"] = 'Plaginlarni tahrirlash';
$_lang["role_edit_role"] = 'Rollarni tahrirlash';
$_lang["role_edit_settings"] = 'Sayt sozlamalarini o‘zgartirish';
+$_lang["role_security_settings"] = 'Xavfsizlik sozlamalarini o‘zgartirish';
$_lang["role_edit_snippet"] = 'Kod bo‘laklarini tahrirlash';
$_lang["role_edit_template"] = 'Sayt shablonlarini tahrirlash';
$_lang["role_edit_user"] = 'Foydalanuvchilarni tahrirlash';
@@ -897,6 +900,7 @@
$_lang["role_help"] = 'Yordam sahifalarini ko‘rish';
$_lang["role_home"] = 'Boshqaruv panelining kirish sahifasini so‘rash';
$_lang["role_import_static"] = 'HTML ni import qilish';
+$_lang["role_in_use_message"] = 'Bu rol hali ham bir yoki bir nechta foydalanuvchiga biriktirilgan. Uni o‘chirishdan oldin bu foydalanuvchilarni boshqa rolga biriktiring.';
$_lang["role_widget_recent_info"] = 'Yaqinda tahrirlangan/yaratilgan resurslar vidjetini ko‘rish';
$_lang["role_list_module"] = 'Modulni ro‘yxatda ko‘rsatish';
$_lang["role_manage_metatags"] = 'Saytning META teglari va kalit so‘zlarini boshqarish';
diff --git a/core/lang/zh/global.php b/core/lang/zh/global.php
index 74611cbbb4..a919b0415b 100644
--- a/core/lang/zh/global.php
+++ b/core/lang/zh/global.php
@@ -42,6 +42,7 @@
$_lang["access_permissions_resources_tab"] = '你可以在这里查看建立了哪些文档组。你也可以建立/重命名/删除组。查看哪些文档归属于哪个文档组,(把鼠标放在文件的ID上就可以看见它的名字). 要在阻力添加/移除一个用户,就直接编辑文档。';
$_lang["access_permissions_user_groups"] = '用户组';
$_lang["access_permissions_user_message"] = 'Select which User Groups this user belongs to:';
+$_lang["access_permissions_user_group_locked"] = '以灰色显示的组不在您的成员范围内。只有管理员或拥有"管理文档和用户组"权限的管理者才能更改它们,因此此处仅供查看。';
$_lang["access_permissions_users_in_group"] = '在本组的用户: ';
$_lang["access_permissions_users_tab"] = '你可以在这里查看建立了哪些用户组。你也可以建立/重命名/删除组。可以查看某个用户归属于哪些组。 要在组里添加/移除一个用户,就直接编辑用户。管理员 (users who have been assigned the role with ID 1)永远都拥有查看操作文件的权利,因此他不需要归属于任何组都可以。';
$_lang["account_email"] = 'Account email';
@@ -688,6 +689,7 @@
$_lang["preview"] = '预览';
$_lang["preview_msg"] = '这个是你最后一次改动的预览. 点击这里 保存刷新你现在的页面';
$_lang["preview_resource"] = 'Preview Resource';
+$_lang["preview_unsupported_url"] = '无法预览此链接:后台管理只能打开 http 和 https 地址。';
$_lang["private"] = '私有的';
$_lang["public"] = '公共的';
$_lang["publish_date"] = 'Publish Date';
@@ -802,6 +804,7 @@
$_lang["role_edit_plugin"] = '编辑插件';
$_lang["role_edit_role"] = '编辑角色';
$_lang["role_edit_settings"] = '改变站点设置';
+$_lang["role_security_settings"] = '改变安全设置';
$_lang["role_edit_snippet"] = '编辑切片snippets';
$_lang["role_edit_template"] = '编辑站点模板';
$_lang["role_edit_user"] = '编辑用户';
@@ -815,6 +818,7 @@
$_lang["role_help"] = '查看帮助文件';
$_lang["role_home"] = '管理主页面';
$_lang["role_import_static"] = 'Import HTML';
+$_lang["role_in_use_message"] = '此角色仍分配给一个或多个用户。删除前请先将这些用户重新分配到其他角色。';
$_lang["role_widget_recent_info"] = '查看“Recently edited/created Resources”小部件';
$_lang["role_manage_metatags"] = '管理站点的META标签和关键词';
$_lang["role_management_msg"] = '在这里你可以选择一需要编辑的角色。';
diff --git a/core/src/Console/SiteUpdateCommand.php b/core/src/Console/SiteUpdateCommand.php
index be31928f45..0479e309ba 100644
--- a/core/src/Console/SiteUpdateCommand.php
+++ b/core/src/Console/SiteUpdateCommand.php
@@ -4,6 +4,7 @@
use EvolutionCMS\Models\SiteModule;
use EvolutionCMS\Models\SiteModuleRole;
use EvolutionCMS\Models\SystemSetting;
+use EvolutionCMS\Services\ComposerEnvironment;
use EvolutionCMS\Services\ComposerVersionSynchronizer;
use EvolutionCMS\Services\CustomComposerPackages;
use EvolutionCMS\Services\Store\RemoteTransportService;
@@ -194,6 +195,25 @@ public function handle()
* @return bool False when the update failed before the site was changed.
*/
public function startUpdate()
+ {
+ // An installed archive can still have an authoritative classmap from before this helper existed.
+ if (!class_exists(ComposerEnvironment::class)) {
+ require_once dirname(__DIR__) . '/Services/ComposerEnvironment.php';
+ }
+
+ return (new ComposerEnvironment(EVO_CORE_PATH))->run(fn () => $this->applyUpdate());
+ }
+
+ /**
+ * Download and apply the update inside the installed site's Composer environment.
+ *
+ * Called by startUpdate() so the archive preflight and the later dependency restore
+ * inherit identical authentication, with the caller's environment restored afterwards.
+ *
+ * @return bool False when the update was refused before files were replaced.
+ * @since 3.6.0
+ */
+ protected function applyUpdate()
{
$evo = evo();
$currentVersion = $evo->getVersionData();
@@ -269,7 +289,6 @@ public function startUpdate()
return false;
}
- $this->configureComposerHome(EVO_CORE_PATH . 'composer');
$customPackageUpdate = $this->preflightComposer(
$temp_dir . '/' . $dir . '/core',
$customPackageNames,
@@ -1525,34 +1544,6 @@ protected function isCommitHash(string $ref): bool
return preg_match('/^[0-9a-f]{7,40}$/i', $ref) === 1;
}
- /**
- * Keep Composer's normal authentication lookup unless a legacy site auth file is needed.
- *
- * An explicit home or inline authentication belongs to the operator. Without
- * an explicit home, an existing site-local auth.json remains supported unless
- * inline authentication was supplied. Otherwise Composer resolves its own home,
- * including XDG_CONFIG_HOME, instead of losing global auth. A worker without HOME
- * (APPDATA on Windows) still needs the old site-local fallback, even with inline
- * authentication. No credentials are read, copied or printed by the updater.
- *
- * @since 3.6.1
- * @param string $legacyHome Site-local Composer home used by earlier updaters.
- * @return void
- */
- protected function configureComposerHome(string $legacyHome): void
- {
- if (getenv('COMPOSER_HOME') !== false) {
- return;
- }
-
- $authFile = rtrim($legacyHome, '/\\') . '/auth.json';
- $globalHome = getenv(windows_os() ? 'APPDATA' : 'HOME');
- if ((getenv('COMPOSER_AUTH') === false && is_file($authFile) && is_readable($authFile))
- || $globalHome === false || $globalHome === '') {
- putenv('COMPOSER_HOME=' . $legacyHome);
- }
- }
-
/**
* Overlay an update archive, routing only its top-level manager tree to the active Manager.
*
diff --git a/core/src/Controllers/RefreshSite.php b/core/src/Controllers/RefreshSite.php
index c027d3610d..3a34a291d4 100644
--- a/core/src/Controllers/RefreshSite.php
+++ b/core/src/Controllers/RefreshSite.php
@@ -32,7 +32,7 @@ public function checkLocked(): ?string
*/
public function canView(): bool
{
- return true;
+ return $this->managerTheme->getCore()->hasPermission('empty_cache') === 1;
}
/**
@@ -41,22 +41,32 @@ public function canView(): bool
*/
public function process(): bool
{
- // (un)publishing of documents, version 2!
- // first, publish document waiting to be published
- $time = $this->managerTheme->getCore()->timestamp();
-
- $this->parameters = [
- 'num_rows_pub' => $this->publishDocuments($time),
- 'num_rows_unpub' => $this->unPublishDocuments($time),
- ];
+ $core = $this->managerTheme->getCore();
+
+ // Running the due (un)publish changes document state, so it needs
+ // publish_document; empty_cache alone only clears the cache. Documents
+ // past their schedule are (un)published on the front end regardless
+ // (Core::runPubStatusSync()), so the cache clear below still runs.
+ $time = $core->timestamp();
+ if ($core->hasPermission('publish_document') === 1) {
+ $this->parameters = [
+ 'num_rows_pub' => $this->publishDocuments($time),
+ 'num_rows_unpub' => $this->unPublishDocuments($time),
+ ];
+ } else {
+ $this->parameters = [
+ 'num_rows_pub' => 0,
+ 'num_rows_unpub' => 0,
+ ];
+ }
ob_start();
- $this->managerTheme->getCore()->clearCache('full', true);
+ $core->clearCache('full', true);
$this->parameters['cache_log'] = ob_get_contents();
ob_end_clean();
// invoke OnSiteRefresh event
- $this->managerTheme->getCore()->invokeEvent("OnSiteRefresh");
+ $core->invokeEvent("OnSiteRefresh");
return true;
}
diff --git a/core/src/Controllers/Search.php b/core/src/Controllers/Search.php
index 7fcf65117d..5fbaf439ab 100644
--- a/core/src/Controllers/Search.php
+++ b/core/src/Controllers/Search.php
@@ -186,11 +186,10 @@ protected function getResults()
'application/vnd.ms-excel' => $this->managerTheme->getStyle('icon_excel'),
];
- if (!empty($contentIds)) {
- $searchQuery = $searchQuery->orWhere(function ($query) use ($contentIds) {
- $query->whereIn('site_content.id', $contentIds);
- });
- }
+ // The TV-matched ids are already OR'd into the search group above, where they
+ // stay subject to the document-group filter. Re-adding them here as a top-level
+ // orWhere would sit outside that filter and leak documents the manager may not
+ // reach, so it is intentionally not repeated.
$searchQuery = $searchQuery->groupBy('site_content.id');
diff --git a/core/src/Controllers/SystemSettings.php b/core/src/Controllers/SystemSettings.php
index ee90bfcf9e..ceadfe0d33 100644
--- a/core/src/Controllers/SystemSettings.php
+++ b/core/src/Controllers/SystemSettings.php
@@ -66,6 +66,7 @@ public function getParameters(array $params = []): array
'gdAvailable' => $this->parameterCheckGD(),
'settings' => $this->parameterSettings(),
'disabledSettings' => $this->disabledSettings(),
+ 'canEditSecuritySettings' => $this->managerTheme->getCore()->hasPermission('security_settings') === 1,
'displayStyle' => ($_SESSION['browser'] === 'modern') ? 'table-row' : 'block',
'fileBrowsers' => $this->parameterFileBrowsers(),
'themes' => $this->parameterThemes(),
diff --git a/core/src/Controllers/UserRoles/Permission.php b/core/src/Controllers/UserRoles/Permission.php
index fc3569a910..a50da56780 100644
--- a/core/src/Controllers/UserRoles/Permission.php
+++ b/core/src/Controllers/UserRoles/Permission.php
@@ -26,11 +26,18 @@ public function canView(): bool
public function process() : bool
{
+ $core = $this->managerTheme->getCore();
if(isset($_GET['action']) && $_GET['action'] == 'delete' ){
+ if (!$core->hasPermission('delete_role')) {
+ $core->webAlertAndQuit(\Lang::get('global.error_no_privileges'));
+ }
Models\Permissions::query()->where('id', $this->getElementId())->delete();
header('Location: index.php?a=86&tab=2');
}
if (isset($_POST['a'])) {
+ if (!$core->hasPermission('save_role')) {
+ $core->webAlertAndQuit(\Lang::get('global.error_no_privileges'));
+ }
$this->updateOrCreate();
return true;
}
diff --git a/core/src/Controllers/UserRoles/UserRole.php b/core/src/Controllers/UserRoles/UserRole.php
index b258df1709..07e6f0eb8d 100644
--- a/core/src/Controllers/UserRoles/UserRole.php
+++ b/core/src/Controllers/UserRoles/UserRole.php
@@ -3,6 +3,7 @@
use EvolutionCMS\Controllers\AbstractController;
use EvolutionCMS\Models;
use EvolutionCMS\Support\ModuleAccess;
+use EvolutionCMS\Support\RoleGuard;
use EvolutionCMS\Interfaces\ManagerTheme;
use Illuminate\Support\Collection;
use Illuminate\Database\Eloquent;
@@ -37,6 +38,15 @@ public function process(): bool
{
if(isset($_GET['action']) && $_GET['action'] == 'delete' ){
$id = $this->getElementId();
+ if (!$this->managerTheme->getCore()->hasPermission('delete_role')) {
+ $this->managerTheme->alertAndQuit('error_no_privileges');
+ }
+ $this->guardRoleReach($id);
+ // Refuse to delete a role still assigned to users: the delete does not reassign
+ // them, so they would be left pointing at a role that no longer exists.
+ if (Models\UserAttribute::where('role', $id)->exists()) {
+ $this->managerTheme->alertAndQuit('role_in_use_message');
+ }
Models\RolePermissions::query()->where('role_id', $id)->delete();
Models\SiteModuleRole::query()->where('role', $id)->delete();
Models\UserRoleVar::where('roleid', $id)->delete();
@@ -58,6 +68,9 @@ public function updateOrCreate()
if (!$this->managerTheme->getCore()->hasPermission('save_role')) {
$this->managerTheme->alertAndQuit('error_no_privileges');
}
+ $actorRole = (int) ($_SESSION['mgrRole'] ?? 0);
+ $actorPermissions = $this->actorPermissions();
+ $this->guardRoleReach($id, $actorRole, $actorPermissions);
if (!isset($_POST['name']) || $_POST['name'] == '') {
$this->managerTheme->getCore()->getManagerApi()->saveFormValues();
$this->managerTheme->getCore()->webAlertAndQuit("Please enter a name for this role!", "index.php?a={$mode}" . ($mode = 35 ? "&id={$id}" : ""));
@@ -77,8 +90,16 @@ public function updateOrCreate()
$requiredPermissions
);
+ // Store only permissions the actor itself holds, so a role cannot be lifted past its author.
+ $grantable = RoleGuard::grantablePermissions(
+ $actorRole,
+ $actorPermissions,
+ array_keys($selectedPermissions),
+ $requiredPermissions
+ );
+
Models\RolePermissions::query()->where('role_id', $role->getKey())->delete();
- foreach (array_keys($selectedPermissions) as $key) {
+ foreach ($grantable as $key) {
Models\RolePermissions::create(['role_id' => $role->getKey(), 'permission' => $key]);
}
@@ -216,6 +237,39 @@ protected function parameterModuleAccess(int $roleId): array
return $selected;
}
+ /**
+ * Refuse to create, edit or delete a role the actor may not reach: the administrator role, or
+ * a role already holding a permission the actor itself lacks.
+ *
+ * @param int|null $actorRole
+ * @param string[]|null $actorPermissions
+ */
+ protected function guardRoleReach(int $id, ?int $actorRole = null, ?array $actorPermissions = null): void
+ {
+ $actorRole = $actorRole ?? (int) ($_SESSION['mgrRole'] ?? 0);
+ $actorPermissions = $actorPermissions ?? $this->actorPermissions();
+ $targetPermissions = RoleGuard::reachOf($id);
+
+ if (!RoleGuard::mayManageRole($actorRole, $actorPermissions, $id, $targetPermissions)) {
+ $this->managerTheme->alertAndQuit('error_no_privileges');
+ }
+ }
+
+ /**
+ * The permission keys the acting manager holds.
+ *
+ * @return string[]
+ */
+ protected function actorPermissions(): array
+ {
+ $core = $this->managerTheme->getCore();
+
+ return array_values(array_filter(
+ Models\Permissions::query()->pluck('key')->all(),
+ static fn ($key) => (bool) $core->hasPermission($key)
+ ));
+ }
+
public static function normalizePermissionsPayload(array $permissions, array $requiredPermissions = []): array
{
foreach ($requiredPermissions as $permission) {
diff --git a/core/src/Controllers/Users/EditOrNewUser.php b/core/src/Controllers/Users/EditOrNewUser.php
index cadc02f05a..5e35bd0087 100644
--- a/core/src/Controllers/Users/EditOrNewUser.php
+++ b/core/src/Controllers/Users/EditOrNewUser.php
@@ -149,18 +149,30 @@ public function process(): bool
\UserManager::clearSettings($userData);
\UserManager::saveSettings($userData);
+ $newRole = (int) ($userData['role'] ?? 0);
+ $actorPermissions = array_values(array_filter(
+ \EvolutionCMS\Models\Permissions::query()->pluck('key')->all(),
+ static fn ($key) => (bool) EvolutionCMS()->hasPermission($key)
+ ));
+ $newRolePermissions = \EvolutionCMS\Support\RoleGuard::reachOf($newRole);
+
if (isset($userData['role'])
&& $userData['role'] != $user->attributes->role
&& EvolutionCMS()->hasPermission('save_role')
- && AdminAccountGuard::mayAssignRole($actorRole, (int) $userData['role'])) {
+ && \EvolutionCMS\Support\RoleGuard::mayManageRole($actorRole, $actorPermissions, $newRole, $newRolePermissions)) {
\UserManager::setRole(['id' => $user->getKey(), 'role' => $userData['role']]);
}
- if (isset($userData['user_groups']) && is_array($userData['user_groups'])) {
- \UserManager::setGroups(['id' => $user->getKey(), 'groups' => $userData['user_groups']]);
- } else {
- \UserManager::setGroups(['id' => $user->getKey(), 'groups' => []]);
- }
+ $postedGroups = (isset($userData['user_groups']) && is_array($userData['user_groups'])) ? $userData['user_groups'] : [];
+ $currentGroups = \EvolutionCMS\Models\MemberGroup::query()->where('member', $user->getKey())->pluck('user_group')->all();
+ $actorGroups = \EvolutionCMS\Models\MemberGroup::query()->where('member', (int) ($_SESSION['mgrInternalKey'] ?? 0))->pluck('user_group')->all();
+ $groups = \EvolutionCMS\Support\UserGroupGuard::authorizedGroups(
+ $actorGroups,
+ $currentGroups,
+ $postedGroups,
+ (bool) EvolutionCMS()->hasPermission('manage_groups')
+ );
+ \UserManager::setGroups(['id' => $user->getKey(), 'groups' => $groups]);
if ($userData['stay'] != '') {
$a = ($userData['stay'] == '2') ? "88&id={$user->getKey()}" : "87";
diff --git a/core/src/Core.php b/core/src/Core.php
index 00601b9cee..7b711f155a 100644
--- a/core/src/Core.php
+++ b/core/src/Core.php
@@ -1336,21 +1336,22 @@ public function updatePubStatus()
*/
protected function runPubStatusSync($timeNow)
{
- // documents that need publishing
- $where = "pub_date <= {$timeNow} AND pub_date != 0 AND published = 0";
- $resultPub = \EvolutionCMS\Models\SiteContent::select('id')->whereRaw($where)->get();
+ // documents that need publishing; a window whose unpub_date has passed too is over
+ // (the same scopes as Refresh site), so it is not published only to be unpublished
+ $resultPub = \EvolutionCMS\Models\SiteContent::publishDocuments($timeNow)->select('id')->get();
if ($resultPub->isNotEmpty()) {
- \EvolutionCMS\Models\SiteContent::whereRaw($where)->update(['published' => 1, 'publishedon' => $timeNow]);
+ \EvolutionCMS\Models\SiteContent::whereIn('id', $resultPub->pluck('id'))
+ ->update(['published' => 1, 'publishedon' => $timeNow]);
foreach ($resultPub as $rowPub) {
$this->invokeEvent('OnDocPublished', ['docid' => $rowPub->id]);
}
}
// documents that need un-publishing
- $where = "unpub_date <= {$timeNow} AND unpub_date != 0 AND published = 1";
- $resultUnpub = \EvolutionCMS\Models\SiteContent::select('id')->whereRaw($where)->get();
+ $resultUnpub = \EvolutionCMS\Models\SiteContent::unPublishDocuments($timeNow)->select('id')->get();
if ($resultUnpub->isNotEmpty()) {
- \EvolutionCMS\Models\SiteContent::whereRaw($where)->update(['published' => 0, 'publishedon' => 0]);
+ \EvolutionCMS\Models\SiteContent::whereIn('id', $resultUnpub->pluck('id'))
+ ->update(['published' => 0, 'publishedon' => 0]);
foreach ($resultUnpub as $rowUnpub) {
// drop only this page's cache instead of wiping the whole site
$this->clearCache((string) $rowUnpub->id);
@@ -6962,6 +6963,21 @@ public function getUserSettings()
str_replace('[(base_path)]', EVO_BASE_PATH, $this->getConfig('rb_base_dir'))
);
+ // A per-user filemanager_path may narrow the file manager root to a subfolder of the
+ // site-wide one, but never widen it past that root: the per-user value is attacker-reachable
+ // (it is an ordinary user setting), while the file manager keys its group ACL on the
+ // site-wide root (configGlobal). Drop an override that escapes the system root.
+ if (isset($this->configGlobal['filemanager_path'])) {
+ $systemPath = str_replace('[(base_path)]', EVO_BASE_PATH, (string) $this->configGlobal['filemanager_path']);
+ $allowed = Support\FileManagerAccess::userRootAllowed(
+ realpath($systemPath),
+ realpath((string) $this->getConfig('filemanager_path'))
+ );
+ if (!$allowed) {
+ $this->setConfig('filemanager_path', $systemPath);
+ }
+ }
+
return $usrSettings;
}
diff --git a/core/src/Legacy/Cache.php b/core/src/Legacy/Cache.php
index be876f3e61..4fe9636078 100644
--- a/core/src/Legacy/Cache.php
+++ b/core/src/Legacy/Cache.php
@@ -281,6 +281,22 @@ public function getCacheRefreshTime()
$timesArr[] = $this->cacheRefreshTime;
}
+ // A scheduled transition whose time has already passed but whose published flag is
+ // still unsynced (e.g. the cache was rebuilt without applying it) must keep triggering
+ // a front-end sync. The two queries above only look ahead, so without this an overdue
+ // transition would drop out of the threshold and Core::updatePubStatus() would never
+ // run the sync. Pin the threshold to now instead. Core::runPubStatusSync() applies the
+ // same scopes, so this pins only while that sync still has work to do: a publication
+ // window that has already ended (both dates past, not published) is complete.
+ $overduePending = Models\SiteContent::query()
+ ->where(fn ($query) => $query->publishDocuments($this->request_time))
+ ->orWhere(fn ($query) => $query->unPublishDocuments($this->request_time))
+ ->exists();
+
+ if ($overduePending) {
+ $timesArr[] = $this->request_time;
+ }
+
if (count($timesArr) > 0) {
$cacheRefreshTime = min($timesArr);
} else {
diff --git a/core/src/Middleware/VerifyCsrfToken.php b/core/src/Middleware/VerifyCsrfToken.php
index 9c194d174f..36e99bf2c9 100644
--- a/core/src/Middleware/VerifyCsrfToken.php
+++ b/core/src/Middleware/VerifyCsrfToken.php
@@ -74,6 +74,7 @@ class VerifyCsrfToken
* edit forms, so only requests carrying the triggering parameter are verified.
*/
protected const MUTATING_GET_PARAMETERS = [
+ 2 => 'b', // dashboard - ?b=resetSysfilesChecksum accepts the checked files as they are
35 => 'action', // UserRole - ?action=delete removes the role
36 => 'action', // UserRole
38 => 'action', // UserRole
diff --git a/core/src/Services/ComposerEnvironment.php b/core/src/Services/ComposerEnvironment.php
new file mode 100644
index 0000000000..aa01c771af
--- /dev/null
+++ b/core/src/Services/ComposerEnvironment.php
@@ -0,0 +1,130 @@
+corePath = rtrim($corePath, '/\\') . '/';
+ }
+
+ /**
+ * Run an update with the same credentials for its preflight and dependency restore.
+ *
+ * Precedence is legacy auth, normal global config/auth, project auth, then COMPOSER_AUTH.
+ * Restore the original process environment even when the update fails or throws.
+ * No credential file is written or copied into the update archive.
+ *
+ * @param callable(): mixed $update Update work whose Composer subprocesses inherit this environment.
+ * @return mixed The update's result, unchanged.
+ * @since 3.6.0
+ */
+ public function run(callable $update): mixed
+ {
+ $previous = [];
+ foreach (['COMPOSER_HOME', 'COMPOSER_AUTH'] as $name) {
+ $previous[$name] = [getenv($name), array_intersect_key($_SERVER, [$name => true]), array_intersect_key($_ENV, [$name => true])];
+ }
+
+ try {
+ $home = Platform::getEnv('COMPOSER_HOME');
+ if ($home) {
+ Platform::putEnv('COMPOSER_HOME', $home);
+ } elseif (!Platform::getEnv(Platform::isWindows() ? 'APPDATA' : 'HOME')) {
+ // FPM/cron may have no user home; keep the existing site-local fallback there.
+ Platform::putEnv('COMPOSER_HOME', $this->corePath . 'composer');
+ }
+
+ $config = new Config();
+ $config->merge(['config' => $this->readAuthentication($this->corePath . 'composer/auth.json')]);
+ try {
+ $globalConfig = Factory::createConfig(new NullIO(), $this->corePath);
+ $global = $globalConfig->raw()['config'];
+ } catch (\Throwable $exception) {
+ // Composer's JSON diagnostics may contain a line of credentials.
+ throw new \RuntimeException('Could not load the existing Composer configuration and authentication.');
+ }
+ // Resolve implicit/XDG/Windows homes once so every child sees the same directory.
+ Platform::putEnv('COMPOSER_HOME', $globalConfig->get('home'));
+ $config->merge(['config' => array_intersect_key($global, array_flip(self::AUTH_METHODS))]);
+ $config->merge(['config' => $this->readAuthentication($this->corePath . 'auth.json')]);
+
+ $auth = Platform::getEnv('COMPOSER_AUTH');
+ if ($auth !== false && $auth !== '') {
+ try {
+ $data = JsonFile::parseJson($auth);
+ JsonFile::validateJsonSchema('COMPOSER_AUTH', $data, JsonFile::AUTH_SCHEMA);
+ } catch (\Throwable $exception) {
+ throw new \RuntimeException('Invalid Composer authentication in COMPOSER_AUTH.');
+ }
+ $config->merge(['config' => $data]);
+ }
+
+ $authentication = array_filter(array_intersect_key($config->raw()['config'], array_flip(self::AUTH_METHODS)));
+ Platform::putEnv('COMPOSER_AUTH', json_encode((object) $authentication, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES));
+
+ return $update();
+ } finally {
+ foreach ($previous as $name => [$value, $server, $env]) {
+ $value === false ? putenv($name) : putenv($name . '=' . $value);
+ unset($_SERVER[$name], $_ENV[$name]);
+ $_SERVER += $server;
+ $_ENV += $env;
+ }
+ }
+ }
+
+ /**
+ * Read an optional auth file without exposing its contents in parse errors.
+ *
+ * Composer validates the schema and merges host entries, so one host's username
+ * and password are replaced together rather than recursively mixed with another source.
+ *
+ * @param string $path Installed site's authentication file; an absent file contributes nothing.
+ * @return array Composer authentication sections.
+ * @since 3.6.0
+ */
+ private function readAuthentication(string $path): array
+ {
+ if (!is_file($path)) {
+ return [];
+ }
+
+ try {
+ $file = new JsonFile($path);
+ $file->validateSchema(JsonFile::AUTH_SCHEMA);
+
+ return $file->read();
+ } catch (\Throwable $exception) {
+ throw new \RuntimeException('Could not read Composer authentication from ' . $path . '.');
+ }
+ }
+}
diff --git a/core/src/Support/DocumentCascadeGuard.php b/core/src/Support/DocumentCascadeGuard.php
new file mode 100644
index 0000000000..cdbe6e9460
--- /dev/null
+++ b/core/src/Support/DocumentCascadeGuard.php
@@ -0,0 +1,96 @@
+ privatemgr.
+ * An administrator, or an install with use_udperms off, reaches everything.
+ *
+ * @param array $documents document id => privatemgr
+ * @param int|string|null $role manager role of the actor
+ * @param mixed $usePermissions the use_udperms setting
+ * @param int[] $accessibleDocumentIds ids of the private documents the actor belongs to
+ * @return int[]
+ */
+ public static function deniedIds(
+ array $documents,
+ $role,
+ $usePermissions,
+ array $accessibleDocumentIds
+ ): array {
+ if (ResourceParentGuard::grantsWithoutLookup($role, $usePermissions)) {
+ return [];
+ }
+
+ $denied = [];
+ foreach ($documents as $id => $privatemgr) {
+ $accessible = ResourceParentGuard::documentIsAccessible(
+ $role,
+ $usePermissions,
+ $privatemgr,
+ $id,
+ $accessibleDocumentIds
+ );
+ if (!$accessible) {
+ $denied[] = (int) $id;
+ }
+ }
+
+ return $denied;
+ }
+
+ /**
+ * The ids among $ids the current manager session may not reach.
+ *
+ * Trashed documents are kept in the lookup, so restoring and moving one stays possible.
+ * An id with no document row is treated as unreachable rather than silently allowed.
+ *
+ * @param array $ids
+ * @return int[]
+ */
+ public static function deniedForSession(array $ids): array
+ {
+ $ids = array_values(array_unique(array_map('intval', array_filter($ids, 'is_numeric'))));
+ if ($ids === []) {
+ return [];
+ }
+
+ $role = $_SESSION['mgrRole'] ?? 0;
+ $usePermissions = evo()->getConfig('use_udperms');
+ if (ResourceParentGuard::grantsWithoutLookup($role, $usePermissions)) {
+ return [];
+ }
+
+ $documents = SiteContent::withTrashed()
+ ->whereIn('id', $ids)
+ ->pluck('privatemgr', 'id')
+ ->all();
+
+ // A posted id with no row cannot be reached either; mark it private.
+ foreach ($ids as $id) {
+ if (!array_key_exists($id, $documents)) {
+ $documents[$id] = 1;
+ }
+ }
+
+ return self::deniedIds($documents, $role, $usePermissions, Permissions::getAccessibleDocumentIds());
+ }
+}
diff --git a/core/src/Support/FileManagerAccess.php b/core/src/Support/FileManagerAccess.php
index 90a2618f30..9acb780328 100644
--- a/core/src/Support/FileManagerAccess.php
+++ b/core/src/Support/FileManagerAccess.php
@@ -33,6 +33,25 @@ public static function isWithin(string $root, string $path): bool
return $path === $root || strncmp($path, $root . '/', strlen($root) + 1) === 0;
}
+ /**
+ * Whether a per-user file manager root may be used instead of the site-wide one.
+ *
+ * A per-user filemanager_path may only narrow the root to a subfolder of the system root,
+ * never widen it past that root. Both arguments are realpath() results, so a path that does
+ * not resolve is passed as false and rejected: callers keep the system root in that case.
+ *
+ * @param string|false $systemRoot realpath of the site-wide filemanager_path
+ * @param string|false $userRoot realpath of the merged (possibly per-user) filemanager_path
+ */
+ public static function userRootAllowed($systemRoot, $userRoot): bool
+ {
+ if ($systemRoot === false || $userRoot === false) {
+ return false;
+ }
+
+ return self::isWithin($systemRoot, $userRoot);
+ }
+
private static function caseInsensitiveFileSystem(): bool
{
return PHP_OS_FAMILY === 'Windows';
diff --git a/core/src/Support/RoleGuard.php b/core/src/Support/RoleGuard.php
new file mode 100644
index 0000000000..2c759ae548
--- /dev/null
+++ b/core/src/Support/RoleGuard.php
@@ -0,0 +1,77 @@
+where('role_id', $roleId)
+ ->whereIn('permission', Permissions::query()->where('disabled', 0)->select('key'))
+ ->pluck('permission')
+ ->all();
+ }
+
+ /**
+ * The permission keys a manager may actually store on a role: the requested keys it also holds,
+ * plus the always-on baseline keys. An administrator keeps everything requested.
+ *
+ * @param int $actorRole
+ * @param string[] $actorPermissions
+ * @param string[] $requestedKeys
+ * @param string[] $requiredKeys always-on (disabled) permissions, stored for every role
+ * @return string[]
+ */
+ public static function grantablePermissions(int $actorRole, array $actorPermissions, array $requestedKeys, array $requiredKeys): array
+ {
+ $allowed = $actorRole === self::ADMIN_ROLE
+ ? $requestedKeys
+ : array_intersect($requestedKeys, $actorPermissions);
+
+ return array_values(array_unique(array_merge($allowed, $requiredKeys)));
+ }
+}
diff --git a/core/src/Support/SecuritySettings.php b/core/src/Support/SecuritySettings.php
new file mode 100644
index 0000000000..3816fae6d7
--- /dev/null
+++ b/core/src/Support/SecuritySettings.php
@@ -0,0 +1,51 @@
+
+ */
+ public const RELEASED = [
+ 'index.php' => [
+ '589eb44fffb0de5559311ada637bdfae', // 3.0RC
+ '2bb924454f81f4a2f90e1124d73d0d12', // 3.0RC2
+ '599511a98673763d066ea76b9e50fbb0', // 3.0RC3 - 3.2.5
+ '4452ae4b9b9c9aff195991f46c90f79e', // 3.2.6 - 3.5.0
+ '013001e96f9ac1031742413b7903641b',
+ 'f38ddec531ed48e89230818d2827ce26',
+ 'bb682f550e906c0b82d189e8f9ff70aa', // 3.5.1 - 3.5.2
+ '1e9e549bf7a22145e27c2b733d26d025',
+ '928f5b3af00fdf9847d1bc44754e9cd1',
+ 'e4eeeb5c1a78c2f03526360bfb0c3568',
+ 'ef67b3a006d8f9728c8d6a8eb7e7640c',
+ '726d910bdd90f0caef4507a7d65102b8',
+ '87c8b2cb7cbe7b98c831cd9c56531149', // 3.5.3
+ '949f5a6f1ddaef3ce8049b5b99a5481d',
+ '069f903824f213e84924cfe999e81ff1',
+ '46c2370a6df13f6438390dba94e10266', // 3.5.4
+ 'cf45d727110f24e2af97fe71c9f5669f',
+ '29bcd9deaa5255b271b06d8261ec5966', // 3.5.5
+ 'e845a31b74e249d433dc36badedfae29', // 3.5.6 - 3.5.7
+ '11727c69a7f8c012bccd41524ca57d6d', // 3.5.8
+ '73555b5a990998c9aa419a98150ca9f8', // 3.5.9
+ '553c79eefae71611e57072b54a603a67',
+ ],
+ 'manager/index.php' => [
+ '3b1215f626722408dd6ffd7a8f16da9a', // 3.0RC
+ '325d82eed384dcb95528d8ffb36d17a1', // 3.0RC2 - 3.1.3
+ 'e00ab1a8d239ad179954f5dbb0afb6e8',
+ 'b2e01536079e041f49b4faea638645e4',
+ '3d47b1a1cd56ebbaf67ba2a88fda41bc',
+ 'c467fb824dc6e2df167e8d45d1919b0b',
+ '19c60de055d5efeb3fdd9debde124cb7', // 3.1.4 - 3.2.7
+ '42ef80570fd86e06b10f8425239ac375', // 3.3.0 - 3.5.0
+ '45523481877a4160b742fd410f541903', // 3.5.1
+ '976e13c4c0174d70e91626b890be0548', // 3.5.2
+ 'a7cae3062ec74f8261fd9ddf5ab8b0e5',
+ 'ba436e70af1f674813ee01a6ab9a6247',
+ '343d20fc20f5f8e3abca27631242c588',
+ 'bccc8c37ddccf33590e125ec0bf67701', // 3.5.3
+ 'fa68033f07a081da54e32b16b89741dd',
+ 'ab14a72c71f97945e6d90792a950c4a5', // 3.5.4 - 3.5.8
+ 'c4da7b5495dc050db774a7f6171284c4',
+ 'a6be82138f1d5bf7ac980242a9d1b07f', // 3.5.9
+ '35f96ba44e454449aeef6ed9cc13a786',
+ ],
+ ];
+
+ /**
+ * The stored checksum with the release copies of this code adopted, or null when nothing
+ * changes (no stored checksum, or no listed file was replaced by an update).
+ *
+ * Entries are keyed as ManagerApi::getSystemChecksum() keys them: $basePath and the listed name.
+ *
+ * @param array|null $released defaults to RELEASED
+ */
+ public static function adopt(string $checkFiles, string $stored, string $basePath, ?array $released = null): ?string
+ {
+ $released ??= self::RELEASED;
+ $checksum = $stored === '' ? false : @unserialize($stored, ['allowed_classes' => false]);
+ if (!is_array($checksum) || $checksum === []) {
+ return null;
+ }
+
+ $changed = false;
+ foreach (explode("\n", trim($checkFiles)) as $file) {
+ $file = trim($file);
+ $copies = $released[ltrim(str_replace('\\', '/', $file), '/')] ?? [];
+ $path = $basePath . $file;
+ if ($copies === [] || !is_file($path) || !isset($checksum[$path])) {
+ continue;
+ }
+ $current = md5_file($path);
+ if ($current !== $checksum[$path]
+ && $current === end($copies)
+ && in_array($checksum[$path], $copies, true)
+ ) {
+ $checksum[$path] = $current;
+ $changed = true;
+ }
+ }
+
+ return $changed ? serialize($checksum) : null;
+ }
+}
diff --git a/core/src/Support/SystemSettingName.php b/core/src/Support/SystemSettingName.php
new file mode 100644
index 0000000000..ce5a5c84dc
--- /dev/null
+++ b/core/src/Support/SystemSettingName.php
@@ -0,0 +1,37 @@
+addConnection(['driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '']);
+ $capsule->setAsGlobal();
+ $capsule->bootEloquent();
+
+ $container = $capsule->getContainer();
+ $container->instance('db', $capsule->getDatabaseManager());
+ $container->bind('db.schema', fn () => $capsule->getConnection()->getSchemaBuilder());
+ Facade::clearResolvedInstances();
+ Facade::setFacadeApplication($container);
+ Model::setConnectionResolver($capsule->getDatabaseManager());
+
+ $schema = $capsule->getConnection()->getSchemaBuilder();
+ $schema->create('user_roles', function (Blueprint $table) {
+ $table->increments('id');
+ $table->string('name')->nullable();
+ $table->text('description')->nullable();
+ });
+ $schema->create('role_permissions', function (Blueprint $table) {
+ $table->increments('id');
+ $table->unsignedInteger('role_id')->default(0);
+ $table->string('permission')->default('');
+ $table->timestamps();
+ });
+
+ return $capsule;
+}
+
+/** @return string[] the permission keys granted to a role */
+function presetGrantsFor(Capsule $capsule, int $roleId): array
+{
+ return $capsule->getConnection()->table('role_permissions')
+ ->where('role_id', $roleId)->pluck('permission')->all();
+}
+
+it('grants a fresh install Publisher neither settings nor bk_manager, and the administrator both', function () {
+ $capsule = bootPublisherPresetDatabase();
+
+ (new UserRolesTableSeeder())->run();
+
+ $publisher = presetGrantsFor($capsule, 3);
+ $administrator = presetGrantsFor($capsule, 1);
+
+ expect($publisher)->not->toContain('settings')
+ ->and($publisher)->not->toContain('bk_manager')
+ // still a working content role
+ ->and($publisher)->toContain('save_document')
+ ->and($publisher)->toContain('delete_chunk')
+ // the administrator keeps both
+ ->and($administrator)->toContain('settings')
+ ->and($administrator)->toContain('bk_manager');
+});
+
+it('is idempotent: a repeated run adds no duplicate rows and keeps the preset', function () {
+ $capsule = bootPublisherPresetDatabase();
+ $db = $capsule->getConnection();
+
+ (new UserRolesTableSeeder())->run();
+ $afterFirst = $db->table('role_permissions')->count();
+
+ (new UserRolesTableSeeder())->run();
+
+ expect($db->table('role_permissions')->count())->toBe($afterFirst)
+ // no (role_id, permission) pair is duplicated
+ ->and(
+ $db->table('role_permissions')
+ ->selectRaw('role_id, permission, COUNT(*) as c')
+ ->groupBy('role_id', 'permission')
+ ->havingRaw('COUNT(*) > 1')->count()
+ )->toBe(0)
+ ->and(presetGrantsFor($capsule, 3))->not->toContain('settings')
+ ->and(presetGrantsFor($capsule, 3))->not->toContain('bk_manager');
+});
+
+it('preserves settings, bk_manager and custom grants on an existing site, while upserting the description', function () {
+ $capsule = bootPublisherPresetDatabase();
+ $db = $capsule->getConnection();
+
+ // an existing site: Publisher already holds settings + bk_manager and a custom grant,
+ // under an operator-renamed role
+ $db->table('user_roles')->insert(['id' => 3, 'name' => 'Senior Publisher', 'description' => 'our own wording']);
+ $db->table('role_permissions')->insert([
+ ['role_id' => 3, 'permission' => 'settings', 'created_at' => null, 'updated_at' => null],
+ ['role_id' => 3, 'permission' => 'bk_manager', 'created_at' => null, 'updated_at' => null],
+ ['role_id' => 3, 'permission' => 'my_custom_perm', 'created_at' => null, 'updated_at' => null],
+ ]);
+
+ (new UserRolesTableSeeder())->run();
+
+ $publisher = presetGrantsFor($capsule, 3);
+ // grants are preserved - the seeder never revokes
+ expect($publisher)->toContain('settings')
+ ->and($publisher)->toContain('bk_manager')
+ ->and($publisher)->toContain('my_custom_perm');
+
+ // the description (and name) are upserted, so they do change on an existing site
+ $role = $db->table('user_roles')->where('id', 3)->first();
+ expect($role->name)->toBe('Publisher')
+ ->and($role->description)->toBe('Editor with expanded permissions including manage users and update Elements');
+});
diff --git a/core/tests/Fixtures/file-manager-user-root.php b/core/tests/Fixtures/file-manager-user-root.php
new file mode 100644
index 0000000000..7c1e4ee191
--- /dev/null
+++ b/core/tests/Fixtures/file-manager-user-root.php
@@ -0,0 +1,81 @@
+addConnection(['driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '']);
+$capsule->setAsGlobal();
+$capsule->bootEloquent();
+$capsule->getConnection()->statement('CREATE TABLE user_settings (user INTEGER, setting_name TEXT, setting_value TEXT)');
+if (isset($options['own'])) {
+ $capsule->getConnection()->table('user_settings')->insert([
+ 'user' => 1, 'setting_name' => 'filemanager_path', 'setting_value' => $options['own'],
+ ]);
+}
+
+$core = evo();
+// getConfig() lets config/cms/settings override a setting; there are none here
+$core->instance('config', new Illuminate\Config\Repository([]));
+$core->config =['filemanager_path' => $options['system'], 'rb_base_dir' => '[(base_path)]assets/'];
+$_SESSION = [];
+$core->getUserSettings();
+
+echo json_encode(['root' => fileManagerRoot(), 'config' => $core->getConfig('filemanager_path')]);
diff --git a/core/tests/Fixtures/save-settings-security.php b/core/tests/Fixtures/save-settings-security.php
new file mode 100644
index 0000000000..32999c0d5c
--- /dev/null
+++ b/core/tests/Fixtures/save-settings-security.php
@@ -0,0 +1,155 @@
+ value, notices: PHP warnings/notices raised,
+ * folds: [spelling => whether a WHERE on it finds the use_udperms row]}
+ */
+
+[, $site, $payload] = $argv;
+$options = json_decode(base64_decode($payload), true);
+
+define('EVO_BASE_PATH', $site . '/');
+define('EVO_BASE_URL', '/');
+define('EVO_MANAGER_PATH', $site . '/manager/');
+define('EVO_CORE_PATH', $site . '/core/');
+define('IN_MANAGER_MODE', true);
+define('IN_INSTALL_MODE', false);
+define('EVO_API_MODE', false);
+
+require dirname(__DIR__, 2) . '/vendor/autoload.php';
+
+$capsule = new Illuminate\Database\Capsule\Manager();
+$mysql = $options['mysql'] ?? null;
+if (is_array($mysql)) {
+ $collation = (string) $mysql['collation'];
+ if (!preg_match('/^utf8mb4_[a-z0-9_]+$/', $collation)) {
+ exit('bad collation');
+ }
+ $capsule->addConnection([
+ 'driver' => 'mysql', 'host' => $mysql['host'], 'port' => $mysql['port'], 'database' => $mysql['database'],
+ 'username' => $mysql['username'], 'password' => $mysql['password'],
+ 'charset' => 'utf8mb4', 'collation' => $collation, 'prefix' => '',
+ ]);
+} else {
+ $capsule->addConnection(['driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '']);
+}
+$capsule->setAsGlobal();
+$capsule->bootEloquent();
+$db = $capsule->getConnection();
+if (is_array($mysql)) {
+ // the column as the schema migration creates it
+ $db->statement('DROP TABLE IF EXISTS system_settings');
+ $db->statement("CREATE TABLE system_settings (setting_name VARCHAR(50) NOT NULL DEFAULT '' PRIMARY KEY, setting_value TEXT) DEFAULT CHARSET=utf8mb4 COLLATE={$collation}");
+} else {
+ $db->statement('CREATE TABLE system_settings (setting_name TEXT COLLATE NOCASE PRIMARY KEY, setting_value TEXT)');
+}
+$db->table('system_settings')->insert([
+ ['setting_name' => 'site_name', 'setting_value' => 'Old name'],
+ ['setting_name' => 'use_udperms', 'setting_value' => '1'],
+ ['setting_name' => 'allow_eval', 'setting_value' => 'with_scan'],
+ ['setting_name' => 'check_files_onlogin', 'setting_value' => "index.php"],
+ ['setting_name' => 'sys_files_checksum', 'setting_value' => 'stored-checksum'],
+ ['setting_name' => 'jpegQuality', 'setting_value' => '85'],
+]);
+$folds = [];
+foreach (['USE_UDPERMS', 'use_udpérms', 'use_udperms '] as $spelling) {
+ $folds[$spelling] = $db->table('system_settings')->where('setting_name', $spelling)->exists();
+}
+
+final class SaveSettingsStubEvo
+{
+ public array $config;
+
+ public function __construct(private array $permissions)
+ {
+ $this->config = Illuminate\Database\Capsule\Manager::table('system_settings')
+ ->pluck('setting_value', 'setting_name')->all()
+ // factory defaults the Core merges in under the stored values
+ + ['manager_theme' => 'default', 'settings_version' => '0'];
+ }
+
+ public function hasPermission($key)
+ {
+ return in_array($key, $this->permissions, true) ? 1 : 0;
+ }
+
+ public function getConfig($key, $default = null)
+ {
+ return $this->config[$key] ?? $default;
+ }
+
+ public function getManagerApi()
+ {
+ // the real checksum, without the manager API's bootstrap
+ return (new ReflectionClass(EvolutionCMS\Legacy\ManagerApi::class))->newInstanceWithoutConstructor();
+ }
+
+ public function getVersionData($key = null)
+ {
+ return '3.5.9';
+ }
+
+ public function logEvent(...$args)
+ {
+ }
+
+ public function clearCache(...$args)
+ {
+ }
+
+ public function webAlertAndQuit($message, $url = '')
+ {
+ echo json_encode(['quit' => $message]);
+ exit;
+ }
+}
+
+function config($key = null, $default = null)
+{
+ return $key === 'cms.settings' ? [] : $default;
+}
+
+// the processor drops the theme's compiled stylesheet through the ManagerTheme facade
+$container = new Illuminate\Container\Container();
+$container->instance('ManagerTheme', new class ($site) {
+ public function __construct(private string $site)
+ {
+ }
+
+ public function getThemeDir(): string
+ {
+ return $this->site . '/manager/media/style/default/';
+ }
+});
+Illuminate\Support\Facades\Facade::setFacadeApplication($container);
+
+$notices = [];
+set_error_handler(static function ($severity, $message, $file, $line) use (&$notices) {
+ $notices[] = $message . ' @ ' . basename($file) . ':' . $line;
+
+ return true;
+});
+
+$modx = new SaveSettingsStubEvo($options['permissions']);
+$_lang = ['error_no_privileges' => 'error_no_privileges'];
+$_SERVER['SERVER_SOFTWARE'] = 'Apache';
+$_POST = $options['post'] + [
+ 'friendly_urls' => '0',
+ 'filemanager_path' => '[(base_path)]',
+ 'rb_base_dir' => '[(base_path)]assets/',
+ 'stay' => '',
+];
+
+include dirname(__DIR__, 3) . '/manager/processors/save_settings.processor.php';
+
+echo json_encode([
+ 'settings' => $db->table('system_settings')->pluck('setting_value', 'setting_name')->all(),
+ 'notices' => $notices,
+ 'folds' => $folds,
+]);
diff --git a/core/tests/Fixtures/shared-type-root-request.php b/core/tests/Fixtures/shared-type-root-request.php
index 16a56510a8..9dcc8b5ba2 100644
--- a/core/tests/Fixtures/shared-type-root-request.php
+++ b/core/tests/Fixtures/shared-type-root-request.php
@@ -241,7 +241,7 @@ function evolutionCMS()
];
// the classic file manager, as files.dynamic.php sets it up
- $root = rtrim(str_replace('\\', '/', fileManagerCanonicalPath(evo()->getConfig('filemanager_path')) ?: realpath(EVO_BASE_PATH)), '/');
+ $root = fileManagerRoot();
$relative = static fn ($request) => fileManagerResolvePath($root, $request)['relative'] ?? null;
$resolved = fileManagerResolvePath($root, 'assets/images/private/secret.png');
$output += [
diff --git a/core/tests/Fixtures/system-files-checksum.php b/core/tests/Fixtures/system-files-checksum.php
new file mode 100644
index 0000000000..fe782f2a78
--- /dev/null
+++ b/core/tests/Fixtures/system-files-checksum.php
@@ -0,0 +1,32 @@
+newInstanceWithoutConstructor();
+$adopted = EvolutionCMS\Support\SystemFilesChecksum::adopt(
+ $options['list'],
+ $options['stored'],
+ EVO_BASE_PATH,
+ $options['released'] ?? null
+);
+
+echo json_encode([
+ 'adopted' => $adopted,
+ 'current' => $api->getSystemChecksum($options['list']),
+ 'before' => $api->getModifiedSystemFilesList($options['list'], $options['stored']),
+ 'after' => $api->getModifiedSystemFilesList($options['list'], $adopted ?? $options['stored']),
+]);
diff --git a/core/tests/Unit/Console/SiteUpdateLayoutTest.php b/core/tests/Unit/Console/SiteUpdateLayoutTest.php
index 99332a8282..ed5884cf6d 100644
--- a/core/tests/Unit/Console/SiteUpdateLayoutTest.php
+++ b/core/tests/Unit/Console/SiteUpdateLayoutTest.php
@@ -131,56 +131,3 @@
expect($source)->toContain("self::moveFiles(\$temp_dir . '/' . \$dir, EVO_BASE_PATH, EVO_MANAGER_PATH);");
});
-
-test('Composer keeps global and explicit authentication while retaining legacy site auth', function (
- bool $legacyAuth,
- string|false $home,
- string|false $auth,
- string|false $expectedHome
-) {
- $legacy = $this->workspace . '/legacy-composer';
- mkdir($legacy, 0777, true);
- if ($legacyAuth) {
- file_put_contents($legacy . '/auth.json', '{}');
- }
- if ($home !== false) {
- putenv('COMPOSER_HOME=' . $home);
- }
- if ($auth !== false) {
- putenv('COMPOSER_AUTH=' . $auth);
- }
- $method = new ReflectionMethod(SiteUpdateCommand::class, 'configureComposerHome');
-
- $method->invoke(new SiteUpdateCommand(), $legacy);
-
- expect(getenv('COMPOSER_HOME'))->toBe($expectedHome === 'legacy' ? $legacy : $expectedHome)
- ->and(getenv('COMPOSER_AUTH'))->toBe($auth)
- ->and(is_file($legacy . '/auth.json'))->toBe($legacyAuth);
-})->with([
- 'normal global/XDG lookup' => [false, false, false, false],
- 'legacy site auth' => [true, false, false, 'legacy'],
- 'explicit home without legacy auth' => [false, '/operator/composer', false, '/operator/composer'],
- 'explicit home wins over legacy auth' => [true, '/operator/composer', false, '/operator/composer'],
- 'inline auth without legacy auth' => [false, false, '{}', false],
- 'inline auth wins over legacy home' => [true, false, '{}', false],
-]);
-
-test('a headless worker still has a Composer home without hiding inline auth', function (bool $inlineAuth) {
- $name = windows_os() ? 'APPDATA' : 'HOME';
- $home = getenv($name);
- $legacy = $this->workspace . '/worker-composer';
- putenv($name);
- if ($inlineAuth) {
- putenv('COMPOSER_AUTH={}');
- }
-
- try {
- (new ReflectionMethod(SiteUpdateCommand::class, 'configureComposerHome'))
- ->invoke(new SiteUpdateCommand(), $legacy);
-
- expect(getenv('COMPOSER_HOME'))->toBe($legacy)
- ->and(getenv('COMPOSER_AUTH'))->toBe($inlineAuth ? '{}' : false);
- } finally {
- $home === false ? putenv($name) : putenv($name . '=' . $home);
- }
-})->with([false, true]);
diff --git a/core/tests/Unit/Install/PublisherDefaultPresetTest.php b/core/tests/Unit/Install/PublisherDefaultPresetTest.php
new file mode 100644
index 0000000000..f5b3e8639f
--- /dev/null
+++ b/core/tests/Unit/Install/PublisherDefaultPresetTest.php
@@ -0,0 +1,94 @@
+ role id => sorted, unique permission keys it is granted
+ */
+function presetGrants(string $relative): array
+{
+ preg_match_all(
+ "/'permission'\\s*=>\\s*'([a-z0-9_]+)',\\s*'role_id'\\s*=>\\s*(\\d+)/",
+ presetSource($relative),
+ $matches,
+ PREG_SET_ORDER
+ );
+
+ $byRole = [];
+ foreach ($matches as $match) {
+ $byRole[(int) $match[2]][] = $match[1];
+ }
+ foreach ($byRole as &$permissions) {
+ $permissions = array_values(array_unique($permissions));
+ sort($permissions);
+ }
+
+ return $byRole;
+}
+
+const PRESET_SEEDERS = [
+ 'install/stubs/seeds/install/UserRolesTableSeeder.php',
+ 'core/database/seeders/UserRolesTableSeeder.php',
+];
+
+it('does not grant the Publisher role settings or bk_manager', function (string $seeder) {
+ $publisher = presetGrants($seeder)[3] ?? [];
+
+ expect($publisher)->not->toContain('settings')
+ ->and($publisher)->not->toContain('bk_manager')
+ // the role is still a working content role, not gutted
+ ->and($publisher)->toContain('save_document')
+ ->and($publisher)->toContain('delete_chunk')
+ ->and($publisher)->toContain('save_user');
+})->with(PRESET_SEEDERS);
+
+it('keeps settings and bk_manager with the administrator role', function (string $seeder) {
+ $administrator = presetGrants($seeder)[1] ?? [];
+
+ expect($administrator)->toContain('settings')
+ ->and($administrator)->toContain('bk_manager');
+})->with(PRESET_SEEDERS);
+
+test('the install stub and the core baseline-repair seeder agree on every role preset', function () {
+ $stub = presetGrants(PRESET_SEEDERS[0]);
+ $core = presetGrants(PRESET_SEEDERS[1]);
+
+ expect(array_keys($stub))->toBe(array_keys($core));
+ foreach ($stub as $roleId => $permissions) {
+ expect($core[$roleId])->toBe($permissions, "role {$roleId} preset drifted between the two seeders");
+ }
+});
diff --git a/core/tests/Unit/Install/SeederPermissionGrantsTest.php b/core/tests/Unit/Install/SeederPermissionGrantsTest.php
new file mode 100644
index 0000000000..2057d294c5
--- /dev/null
+++ b/core/tests/Unit/Install/SeederPermissionGrantsTest.php
@@ -0,0 +1,62 @@
+ 'core/database/seeders',
+ 'install stub' => 'install/stubs/seeds/install',
+];
+
+function seederSource(string $file, string $dir = 'core/database/seeders'): string
+{
+ return (string) file_get_contents(dirname(__DIR__, 4) . '/' . $dir . '/' . $file);
+}
+
+/** @return string[] */
+function seededPermissionKeys(string $dir = 'core/database/seeders'): array
+{
+ preg_match_all(
+ "/'key'\\s*=>\\s*'([^']+)'/",
+ seederSource('UserPermissionsTableSeeder.php', $dir),
+ $matches
+ );
+
+ return array_values(array_unique($matches[1]));
+}
+
+/** @return string[] */
+function grantedPermissionKeys(string $dir = 'core/database/seeders'): array
+{
+ preg_match_all(
+ "/'permission'\\s*=>\\s*'([^']+)'/",
+ seederSource('UserRolesTableSeeder.php', $dir),
+ $matches
+ );
+
+ return array_values(array_unique($matches[1]));
+}
+
+it('grants only permissions that are actually defined', function (string $dir) {
+ $undefined = array_diff(grantedPermissionKeys($dir), seededPermissionKeys($dir));
+
+ expect($undefined)->toBe([]);
+})->with(GRANT_SEEDER_DIRS);
+
+it('no longer defines or grants the dead action_ok permission', function (string $dir) {
+ expect(seededPermissionKeys($dir))->not->toContain('action_ok')
+ ->and(grantedPermissionKeys($dir))->not->toContain('role_actionok')
+ ->and(grantedPermissionKeys($dir))->not->toContain('action_ok');
+})->with(GRANT_SEEDER_DIRS);
diff --git a/core/tests/Unit/Legacy/PublishScheduleOverdueSyncTest.php b/core/tests/Unit/Legacy/PublishScheduleOverdueSyncTest.php
new file mode 100644
index 0000000000..8f6afacea6
--- /dev/null
+++ b/core/tests/Unit/Legacy/PublishScheduleOverdueSyncTest.php
@@ -0,0 +1,236 @@
+ Legacy\Cache::emptyCache() -> publishTimeConfig()).
+|
+| publishTimeConfig() recomputes the publish threshold from getCacheRefreshTime().
+| That method used to look only at FUTURE pub_date / unpub_date, so a transition
+| already past its time but not yet synced dropped out of the threshold, which
+| became the next future date or 0. Core::updatePubStatus() then never scheduled the
+| sync, and the stored published flag stayed stale indefinitely - the front-end
+| fallback relied on in the PR did not actually fire.
+|
+| getCacheRefreshTime() now also detects an overdue-but-unsynced transition - with
+| the scopes Core::runPubStatusSync() applies (SiteContent::publishDocuments() /
+| unPublishDocuments(), as Refresh site does) - and pins the threshold to "now" so
+| the next front-end request runs the sync. A publication window whose unpub_date
+| has passed as well is over: it is neither published only to be unpublished again
+| nor kept due, which would repeat those writes and events on every request. These
+| tests drive the real threshold calculation and the real Core::runPubStatusSync()
+| end to end.
+|
+*/
+
+/**
+ * Drives the real Core::runPubStatusSync() against the harness database without the heavy
+ * Core bootstrap: only the handful of methods that method calls are overridden.
+ */
+class OverduePubSyncCore extends Core
+{
+ /** @var array */
+ public array $firedEvents = [];
+ /** @var string[] */
+ public array $clearedCaches = [];
+ public string $cachePath = '';
+
+ public function __construct()
+ {
+ // bypass the full Core bootstrap; runPubStatusSync() needs none of it
+ }
+
+ public function invokeEvent($evtName, $extParams = [])
+ {
+ $this->firedEvents[] = [$evtName, isset($extParams['docid']) ? (int) $extParams['docid'] : null];
+ return false;
+ }
+
+ public function clearCache($type = '', $report = false)
+ {
+ $this->clearedCaches[] = (string) $type;
+ }
+
+ public function bootstrapPath($path = '')
+ {
+ return $this->cachePath;
+ }
+
+ public function getConfig($name = '', $default = null)
+ {
+ return $default;
+ }
+
+ public function syncNow(int $time): void
+ {
+ $this->runPubStatusSync($time);
+ }
+}
+
+function overdueMakeDoc(array $attrs): void
+{
+ $doc = new SiteContent();
+ $doc->forceFill($attrs + ['pagetitle' => $attrs['alias']]);
+ $doc->save();
+}
+
+function overdueThreshold(string $dir): int
+{
+ $src = (string) file_get_contents($dir . '/sitePublishing.idx.php');
+ return preg_match('/\\$cacheRefreshTime\\s*=\\s*\'?(\\d+)\'?/', $src, $m) ? (int) $m[1] : 0;
+}
+
+beforeEach(function () {
+ $this->dir = str_replace('\\', '/', sys_get_temp_dir()) . '/evo-overdue-' . uniqid();
+ mkdir($this->dir);
+ $capsule = CacheRefreshHarness::boot($this->dir . '/site.sqlite');
+ CacheRefreshHarness::schema($capsule);
+ // runPubStatusSync() writes publishedon; the harness schema does not ship it
+ $capsule->getConnection()->getSchemaBuilder()->table('site_content', function ($t) {
+ $t->unsignedInteger('publishedon')->default(0);
+ });
+
+ global $evo;
+ $evo = new CacheRefreshHarness();
+ $evo->dir = $this->dir;
+ $this->evo = $evo;
+ $this->now = time();
+ $_SERVER['REQUEST_TIME'] = $this->now;
+
+ $this->sync = new SyncCache();
+ $this->sync->setCachepath($this->dir);
+});
+
+afterEach(function () {
+ global $evo;
+ $evo = null;
+ Capsule::connection()->disconnect();
+ foreach (array_diff(scandir($this->dir), ['.', '..']) as $f) {
+ @unlink($this->dir . '/' . $f);
+ }
+ @rmdir($this->dir);
+});
+
+it('keeps an overdue transition in the refresh threshold so the front-end sync still applies it', function (string $kind, bool $withFuture) {
+ $now = $this->now;
+
+ // the overdue, not-yet-synced transition
+ if ($kind === 'publish') {
+ overdueMakeDoc(['id' => 1, 'alias' => 'overdue', 'published' => 0, 'pub_date' => $now - 100]);
+ } else {
+ overdueMakeDoc(['id' => 1, 'alias' => 'overdue', 'published' => 1, 'unpub_date' => $now - 100]);
+ }
+ // optionally a later schedule that is still in the future
+ if ($withFuture) {
+ overdueMakeDoc(['id' => 2, 'alias' => 'later', 'published' => 0, 'pub_date' => $now + 3600]);
+ }
+
+ $before = (int) SiteContent::find(1)->published;
+
+ // Refresh site without publish_document: the (un)publish is skipped, but the cache
+ // clear rebuilds the publishing threshold (clearCache('full') path).
+ $this->sync->publishTimeConfig();
+
+ // the manager action itself did not apply the transition
+ expect((int) SiteContent::find(1)->published)->toBe($before)
+ // the cache clear wrote the publishing file
+ ->and(is_file($this->dir . '/sitePublishing.idx.php'))->toBeTrue();
+
+ // the overdue transition kept the threshold at/<= now, so updatePubStatus()'s gate
+ // (timeNow >= cacheRefreshTime && cacheRefreshTime != 0) now passes
+ $threshold = overdueThreshold($this->dir);
+ expect($threshold)->not->toBe(0)
+ ->and($threshold)->toBeLessThanOrEqual($now)
+ ->and($now >= $threshold && $threshold !== 0)->toBeTrue();
+
+ // the front-end request then applies the transition and fires the matching event
+ $core = new OverduePubSyncCore();
+ $core->cachePath = $this->dir;
+ $core->syncNow($now);
+
+ $expectedFlag = $kind === 'publish' ? 1 : 0;
+ $expectedEvent = $kind === 'publish' ? 'OnDocPublished' : 'OnDocUnPublished';
+ expect((int) SiteContent::find(1)->published)->toBe($expectedFlag)
+ ->and($core->firedEvents)->toContain([$expectedEvent, 1]);
+ if ($kind === 'unpublish') {
+ // an unpublish drops only the affected page cache
+ expect($core->clearedCaches)->toContain('1');
+ }
+ if ($withFuture) {
+ // the still-future document is left for its own time
+ expect((int) SiteContent::find(2)->published)->toBe(0);
+ }
+
+ // the threshold self-heals: the next future date when one remains, else 0 - no
+ // sync-on-every-request loop once the overdue work is done
+ expect(overdueThreshold($this->dir))->toBe($withFuture ? $now + 3600 : 0);
+})->with([
+ 'overdue publish, no future schedule' => ['publish', false],
+ 'overdue publish, later future schedule' => ['publish', true],
+ 'overdue unpublish, no future schedule' => ['unpublish', false],
+ 'overdue unpublish, later future schedule' => ['unpublish', true],
+]);
+
+it('does not pin the threshold to now when nothing is actually overdue', function () {
+ $now = $this->now;
+ // a future schedule, plus a past date that has already been applied (published = 1)
+ overdueMakeDoc(['id' => 1, 'alias' => 'later', 'published' => 0, 'pub_date' => $now + 3600]);
+ overdueMakeDoc(['id' => 2, 'alias' => 'applied', 'published' => 1, 'pub_date' => $now - 100]);
+
+ $this->sync->publishTimeConfig();
+
+ // threshold is the future date, not pinned to now - an already-applied past date
+ // (published = 1) is not treated as overdue, so there is no sync-every-request loop
+ expect(overdueThreshold($this->dir))->toBe($now + 3600);
+});
+
+it('writes a zero threshold when there is no schedule at all', function () {
+ $this->sync->publishTimeConfig();
+
+ expect(overdueThreshold($this->dir))->toBe(0);
+});
+
+it('treats a publication window that has already ended as complete', function (int $published, int $pubOffset, int $unpubOffset) {
+ $now = $this->now;
+ overdueMakeDoc([
+ 'id' => 1, 'alias' => 'ended', 'published' => $published,
+ 'pub_date' => $now + $pubOffset, 'unpub_date' => $now + $unpubOffset,
+ ]);
+
+ $this->sync->publishTimeConfig();
+ // only a document still marked published has work left (the unpublish)
+ expect(overdueThreshold($this->dir))->toBe($published === 1 ? $now : 0);
+
+ $core = new OverduePubSyncCore();
+ $core->cachePath = $this->dir;
+ $core->syncNow($now);
+
+ // it ends unpublished; a published document is unpublished once, never re-published
+ expect((int) SiteContent::find(1)->published)->toBe(0)
+ ->and($core->firedEvents)->toBe($published === 1 ? [['OnDocUnPublished', 1]] : [])
+ ->and(overdueThreshold($this->dir))->toBe(0);
+
+ // a repeated sync (the next front-end request) writes and fires nothing
+ $core->firedEvents = [];
+ $core->clearedCaches = [];
+ $core->syncNow($now + 1);
+
+ expect((int) SiteContent::find(1)->published)->toBe(0)
+ ->and($core->firedEvents)->toBe([])
+ ->and($core->clearedCaches)->toBe([]);
+})->with([
+ 'never published, unpub after pub' => [0, -200, -100],
+ 'never published, unpub before pub' => [0, -100, -200],
+ 'still published, both dates past' => [1, -200, -100],
+]);
diff --git a/core/tests/Unit/Manager/ReferenceTypeSwitchFlowTest.php b/core/tests/Unit/Manager/ReferenceTypeSwitchFlowTest.php
index 383e2db8a0..8e3d011df7 100644
--- a/core/tests/Unit/Manager/ReferenceTypeSwitchFlowTest.php
+++ b/core/tests/Unit/Manager/ReferenceTypeSwitchFlowTest.php
@@ -48,6 +48,7 @@
it('previews a site-relative weblink or a document id on the site, not under the manager', function () {
$form = file_get_contents(dirname(__DIR__, 4) . '/manager/actions/mutate_content.dynamic.php');
- expect($form)->toContain("previewUrl = = js_json(EVO_SITE_URL . 'index.php?id=') ?> + rawUrl;");
- expect($form)->toContain('? rawUrl : = js_json(EVO_SITE_URL) ?> + rawUrl;');
+ // The weblink is resolved against the site URL; the cases live in preview-target-helper.test.js.
+ expect($form)->toContain('target = rawUrl;')
+ ->and($form)->toContain('evoPreviewTargetHelper.resolve(target, site, window.location.href)');
});
diff --git a/core/tests/Unit/Security/AdminAccountGuardTest.php b/core/tests/Unit/Security/AdminAccountGuardTest.php
index 8e81fb9d7f..930e35b33f 100644
--- a/core/tests/Unit/Security/AdminAccountGuardTest.php
+++ b/core/tests/Unit/Security/AdminAccountGuardTest.php
@@ -26,6 +26,7 @@
$delete = (string) file_get_contents($root . 'DeleteUser.php');
expect($edit)->toContain('AdminAccountGuard::mayManageAccount(')
- ->and($edit)->toContain('AdminAccountGuard::mayAssignRole(')
+ // role assignment now uses the stronger RoleGuard subset check, not the role-1-only guard
+ ->and($edit)->toContain('RoleGuard::mayManageRole(')
->and($delete)->toContain('AdminAccountGuard::mayManageAccount(');
});
diff --git a/core/tests/Unit/Security/DocumentAclCascadeTest.php b/core/tests/Unit/Security/DocumentAclCascadeTest.php
new file mode 100644
index 0000000000..70d5341761
--- /dev/null
+++ b/core/tests/Unit/Security/DocumentAclCascadeTest.php
@@ -0,0 +1,66 @@
+toContain('DocumentCascadeGuard::deniedForSession($documentDeleteIds)')
+ ->toContain("webAlertAndQuit(\$_lang[\"access_permission_denied\"]");
+});
+
+it('guards the full undelete branch, not only the posted id', function () {
+ $source = cascadeProcessorSource('undelete_content.processor.php');
+
+ expect($source)
+ ->toContain('DocumentCascadeGuard::deniedForSession($documentDeleteIds)')
+ ->toContain("webAlertAndQuit(\$_lang[\"access_permission_denied\"]");
+});
+
+it('checks every reordered document in the menuindex sort', function () {
+ $source = (string) file_get_contents(
+ dirname(__DIR__, 3) . '/../manager/actions/mutate_menuindex_sort.dynamic.php'
+ );
+
+ expect($source)
+ ->toContain('DocumentCascadeGuard::deniedForSession($docIds)')
+ ->toContain("webAlertAndQuit(\$_lang[\"access_permission_denied\"]");
+});
+
+it('checks the moved document ACL regardless of tree_show_protected', function () {
+ $source = (string) file_get_contents(
+ dirname(__DIR__, 3) . '/../manager/media/style/default/ajax.php'
+ );
+
+ $move = strpos($source, "case 'movedocument'");
+ expect($move)->not->toBeFalse();
+
+ // the moved document itself is checked, not only the destination parent
+ expect(strpos($source, 'MoveDocumentTargetGuard::deniedForUser($id)', $move))->not->toBeFalse();
+});
+
+it('does not leak TV-matched documents past the document-group filter in search', function () {
+ $source = (string) file_get_contents(dirname(__DIR__, 3) . '/src/Controllers/Search.php');
+
+ // the ACL-bypassing top-level orWhere on the TV ids must be gone
+ expect($source)->not->toContain('$searchQuery = $searchQuery->orWhere(function ($query) use ($contentIds)');
+});
diff --git a/core/tests/Unit/Security/DocumentCascadeGuardTest.php b/core/tests/Unit/Security/DocumentCascadeGuardTest.php
new file mode 100644
index 0000000000..9638a3a343
--- /dev/null
+++ b/core/tests/Unit/Security/DocumentCascadeGuardTest.php
@@ -0,0 +1,51 @@
+ 1, 11 => 1, 12 => 0];
+
+ expect(DocumentCascadeGuard::deniedIds($documents, 1, 1, []))->toBe([]);
+});
+
+test('with permissions off every document is reachable', function () {
+ $documents = [10 => 1, 11 => 1];
+
+ expect(DocumentCascadeGuard::deniedIds($documents, 2, 0, []))->toBe([]);
+});
+
+test('a non-admin is refused private documents outside its groups', function () {
+ // 10 public, 11 private but a member, 12 private and not a member
+ $documents = [10 => 0, 11 => 1, 12 => 1];
+
+ expect(DocumentCascadeGuard::deniedIds($documents, 2, 1, [11]))
+ ->toBe([12]);
+});
+
+test('a restricted descendant is caught even when the parent is public', function () {
+ // parent 10 public and reachable, child 11 private to a group the actor lacks
+ $documents = [10 => 0, 11 => 1];
+
+ expect(DocumentCascadeGuard::deniedIds($documents, 2, 1, []))
+ ->toBe([11]);
+});
+
+test('public documents alone leave nothing denied', function () {
+ $documents = [10 => 0, 11 => 0, 12 => 0];
+
+ expect(DocumentCascadeGuard::deniedIds($documents, 2, 1, []))->toBe([]);
+});
diff --git a/core/tests/Unit/Security/DocumentPreviewSandboxTest.php b/core/tests/Unit/Security/DocumentPreviewSandboxTest.php
index 6e8d0a721c..eaa1efd11f 100644
--- a/core/tests/Unit/Security/DocumentPreviewSandboxTest.php
+++ b/core/tests/Unit/Security/DocumentPreviewSandboxTest.php
@@ -19,3 +19,51 @@
expect($tokens)->not->toContain('allow-same-origin')
->and($tokens)->toContain('allow-scripts');
});
+
+/*
+| The editor's "Preview" button used to window.open() the raw same-origin front-end URL in a new
+| window, where an editor's inline content ran with the previewing manager's session. It now opens
+| same-origin content in the same opaque-origin sandbox the info page uses; only external reference
+| targets still open a plain window. Whether a target is same-origin is decided on the parsed URL by
+| manager/media/script/preview-target-helper.js, whose cases (relative and absolute same-origin
+| links, a subdirectory installation, external links, rejected schemes) live in
+| manager/media/script/tests/preview-target-helper.test.js. These pin the button to the helper.
+*/
+
+it('routes the editor preview through the origin helper, not a text prefix', function () {
+ $source = file_get_contents(dirname(__DIR__, 4) . '/manager/actions/mutate_content.dynamic.php');
+
+ expect($source)->toContain("media/script/preview-target-helper.js')")
+ ->and($source)->toContain('var preview = evoPreviewTargetHelper.resolve(target, site, window.location.href);')
+ ->and($source)->toContain("if (preview.mode === 'sandbox') {\n openSandboxedPreview(preview.url);")
+ ->and($source)->toContain("window.open(preview.url, 'previeWin', 'noopener,noreferrer');")
+ ->and($source)->not->toContain('lastIndexOf(site')
+ ->and($source)->not->toContain('window.open(previewUrl');
+});
+
+it('opens only http(s) targets and sandboxes every same-origin one', function () {
+ $node = (new Symfony\Component\Process\ExecutableFinder())->find('node');
+ if ($node === null) {
+ $this->markTestSkipped('Node.js is not installed.');
+ }
+
+ $test = dirname(__DIR__, 4) . '/manager/media/script/tests/preview-target-helper.test.js';
+ $output = evoRunCommand(escapeshellarg($node) . ' --test ' . escapeshellarg($test) . ' 2>&1', $exitCode);
+
+ expect($exitCode)->toBe(0, $output);
+});
+
+it('sandboxes the editor preview iframe without allow-same-origin or allow-top-navigation', function () {
+ $source = file_get_contents(dirname(__DIR__, 4) . '/manager/actions/mutate_content.dynamic.php');
+
+ expect($source)->toMatch('/