Author: Derick Rethans (derickr)
Date: 2025-01-28T10:43:23Z

Commit: 
https://github.com/php/web-analytics/commit/295337bed842de426a08483bdd6779fc7ea8adb0
Raw diff: 
https://github.com/php/web-analytics/commit/295337bed842de426a08483bdd6779fc7ea8adb0.diff

Upgrade Matomo to 5.2.2

Changed paths:
  M  www/core/Updates/5.2.0-b6.php
  M  www/plugins/API/API.php
  M  www/plugins/CoreAdminHome/OptOutManager.php
  M  www/plugins/CorePluginsAdmin/SettingsMetadata.php
  M  www/plugins/CorePluginsAdmin/vue/dist/CorePluginsAdmin.umd.js
  M  www/plugins/CorePluginsAdmin/vue/dist/CorePluginsAdmin.umd.min.js
  M  www/plugins/CorePluginsAdmin/vue/src/PluginSettings/PluginSettings.vue
  M  www/plugins/Monolog/Formatter/LineMessageFormatter.php
  M  www/plugins/Overlay/templates/startOverlaySession.twig
  M  www/plugins/UsersManager/API.php
  M  www/vendor/composer/InstalledVersions.php


Diff:

diff --git a/www/core/Updates/5.2.0-b6.php b/www/core/Updates/5.2.0-b6.php
index c2b8dba..4394792 100644
--- a/www/core/Updates/5.2.0-b6.php
+++ b/www/core/Updates/5.2.0-b6.php
@@ -31,17 +31,17 @@ public function __construct(MigrationFactory $factory)
 
     public function getMigrations(Updater $updater)
     {
-        $startOfCurrentYear = Date::now()->toString('Y') . '-01-01';
+        $startOfCurrentMonth = Date::now()->toString('Y-m') . '-01';
 
         $commandToExecute = sprintf(
             './console core:invalidate-report-data --dates=%s,today 
--plugin=Actions.Actions_hits',
-            $startOfCurrentYear
+            $startOfCurrentMonth
         );
 
         $migrations = [
-            new CustomMigration(function () use ($startOfCurrentYear) {
+            new CustomMigration(function () use ($startOfCurrentMonth) {
                 $invalidator = StaticContainer::get(ArchiveInvalidator::class);
-                $invalidator->scheduleReArchiving('all', 'Actions', 
'Actions_hits', Date::factory($startOfCurrentYear));
+                $invalidator->scheduleReArchiving('all', 'Actions', 
'Actions_hits', Date::factory($startOfCurrentMonth));
             }, $commandToExecute)
         ];
 
diff --git a/www/plugins/API/API.php b/www/plugins/API/API.php
index b2f4ec4..f8c7ada 100644
--- a/www/plugins/API/API.php
+++ b/www/plugins/API/API.php
@@ -525,7 +525,7 @@ public function getBulkRequest($urls)
 
             $params += $queryParameters;
 
-            if (!empty($params['method']) && $params['method'] === 
'API.getBulkRequest') {
+            if (!empty($params['method']) && is_string($params['method']) && 
trim($params['method']) === 'API.getBulkRequest') {
                 continue;
             }
 
diff --git a/www/plugins/CoreAdminHome/OptOutManager.php 
b/www/plugins/CoreAdminHome/OptOutManager.php
index 72154f7..81c1a4f 100644
--- a/www/plugins/CoreAdminHome/OptOutManager.php
+++ b/www/plugins/CoreAdminHome/OptOutManager.php
@@ -18,6 +18,7 @@
 use Piwik\Request;
 use Piwik\Tracker\IgnoreCookie;
 use Piwik\Url;
+use Piwik\UrlHelper;
 use Piwik\View;
 
 /*
@@ -205,6 +206,20 @@ public function getOptOutJSEmbedCode(
         bool $applyStyling,
         bool $showIntro
     ): string {
+        $parsedUrl = parse_url($matomoUrl);
+
+        if (
+            (!empty($matomoUrl) && false === $parsedUrl)
+            || (!empty($parsedUrl['scheme']) && 
!in_array(strtolower($parsedUrl['scheme']), ['http', 'https']))
+            || (empty($parsedUrl['host']) || 
!Url::isValidHost($parsedUrl['host']))
+        ) {
+            throw new \Piwik\Exception\Exception('The provided URL is 
invalid.');
+        }
+
+        // We put together the url based on the parsed parameters manually to 
ensure it might not include unexpected values
+        // for protocol less urls starting with //, we need to prepend the 
double slash again
+        $matomoUrl = (strpos($matomoUrl, '//') === 0 ? '//' : '') . 
UrlHelper::getParseUrlReverse($parsedUrl);
+
         return '<div id="matomo-opt-out"></div>
 <script src="' . rtrim($matomoUrl, '/') . 
'/index.php?module=CoreAdminHome&action=optOutJS&divId=matomo-opt-out&language='
 . $language . ($applyStyling ? '&backgroundColor=' . $backgroundColor . 
'&fontColor=' . $fontColor . '&fontSize=' . $fontSize . '&fontFamily=' . 
$fontFamily : '') . '&showIntro=' . ($showIntro ? '1' : '0') . '"></script>';
     }
diff --git a/www/plugins/CorePluginsAdmin/SettingsMetadata.php 
b/www/plugins/CorePluginsAdmin/SettingsMetadata.php
index 96907b7..6afe240 100644
--- a/www/plugins/CorePluginsAdmin/SettingsMetadata.php
+++ b/www/plugins/CorePluginsAdmin/SettingsMetadata.php
@@ -20,6 +20,8 @@ class SettingsMetadata
 {
     public const PASSWORD_PLACEHOLDER = '******';
 
+    public const EMPTY_ARRAY = '__empty__';
+
     /**
      * @param Settings[]  $settingsInstances
      * @param array $settingValues   array('pluginName' => array('settingName' 
=> 'settingValue'))
@@ -34,6 +36,11 @@ public function setPluginSettings($settingsInstances, 
$settingValues)
 
                     $fieldConfig = $setting->configureField();
 
+                    // empty arrays are sent as __empty__ value, so we need to 
convert it here back to an array
+                    if ($setting->getType() === FieldConfig::TYPE_ARRAY && 
$value === self::EMPTY_ARRAY) {
+                        $value = [];
+                    }
+
                     if (
                         isset($value) && (
                         $fieldConfig->uiControl !== 
FieldConfig::UI_CONTROL_PASSWORD ||
diff --git a/www/plugins/CorePluginsAdmin/vue/dist/CorePluginsAdmin.umd.js 
b/www/plugins/CorePluginsAdmin/vue/dist/CorePluginsAdmin.umd.js
index de81481..8a5a535 100644
--- a/www/plugins/CorePluginsAdmin/vue/dist/CorePluginsAdmin.umd.js
+++ b/www/plugins/CorePluginsAdmin/vue/dist/CorePluginsAdmin.umd.js
@@ -17267,34 +17267,34 @@ var UI_CONTROLS_TO_TYPE = {
 Fieldvue_type_script_lang_ts.render = Fieldvue_type_template_id_5f883444_render
 
 /* harmony default export */ var Field = (Fieldvue_type_script_lang_ts);
-// CONCATENATED MODULE: 
./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CorePluginsAdmin/vue/src/PluginSettings/PluginSettings.vue?vue&type=template&id=919e3cb4
+// CONCATENATED MODULE: 
./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CorePluginsAdmin/vue/src/PluginSettings/PluginSettings.vue?vue&type=template&id=601e4fc6
 
-var PluginSettingsvue_type_template_id_919e3cb4_hoisted_1 = {
+var PluginSettingsvue_type_template_id_601e4fc6_hoisted_1 = {
   class: "pluginSettings",
   ref: "root"
 };
-var PluginSettingsvue_type_template_id_919e3cb4_hoisted_2 = ["id"];
-var PluginSettingsvue_type_template_id_919e3cb4_hoisted_3 = {
+var PluginSettingsvue_type_template_id_601e4fc6_hoisted_2 = ["id"];
+var PluginSettingsvue_type_template_id_601e4fc6_hoisted_3 = {
   class: "card-content"
 };
-var PluginSettingsvue_type_template_id_919e3cb4_hoisted_4 = ["id"];
-var PluginSettingsvue_type_template_id_919e3cb4_hoisted_5 = ["onClick", 
"disabled", "value"];
-function PluginSettingsvue_type_template_id_919e3cb4_render(_ctx, _cache, 
$props, $setup, $data, $options) {
+var PluginSettingsvue_type_template_id_601e4fc6_hoisted_4 = ["id"];
+var PluginSettingsvue_type_template_id_601e4fc6_hoisted_5 = ["onClick", 
"disabled", "value"];
+function PluginSettingsvue_type_template_id_601e4fc6_render(_ctx, _cache, 
$props, $setup, $data, $options) {
   var _component_GroupedSettings = 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("GroupedSettings");
 
   var _component_ActivityIndicator = 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("ActivityIndicator");
 
   var _component_PasswordConfirmation = 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("PasswordConfirmation");
 
-  return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div",
 PluginSettingsvue_type_template_id_919e3cb4_hoisted_1, 
[(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"],
 null, 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.settingsPerPlugin,
 function (settings) {
+  return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div",
 PluginSettingsvue_type_template_id_601e4fc6_hoisted_1, 
[(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"],
 null, 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.settingsPerPlugin,
 function (settings) {
     return 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div",
 {
       class: "card",
       id: "".concat(settings.pluginName, "PluginSettings"),
       key: "".concat(settings.pluginName, "PluginSettings")
-    }, 
[Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div",
 PluginSettingsvue_type_template_id_919e3cb4_hoisted_3, 
[Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("h2",
 {
+    }, 
[Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div",
 PluginSettingsvue_type_template_id_601e4fc6_hoisted_3, 
[Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("h2",
 {
       class: "card-title",
       id: settings.pluginName
-    }, 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(settings.title),
 9, PluginSettingsvue_type_template_id_919e3cb4_hoisted_4), 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_GroupedSettings,
 {
+    }, 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(settings.title),
 9, PluginSettingsvue_type_template_id_601e4fc6_hoisted_4), 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_GroupedSettings,
 {
       "group-name": settings.pluginName,
       settings: settings.settings,
       "all-setting-values": _ctx.settingValues,
@@ -17309,9 +17309,9 @@ function 
PluginSettingsvue_type_template_id_919e3cb4_render(_ctx, _cache, $props
       disabled: _ctx.isLoading,
       class: "pluginsSettingsSubmit btn",
       value: _ctx.translate('General_Save')
-    }, null, 8, PluginSettingsvue_type_template_id_919e3cb4_hoisted_5), 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_ActivityIndicator,
 {
+    }, null, 8, PluginSettingsvue_type_template_id_601e4fc6_hoisted_5), 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_ActivityIndicator,
 {
       loading: _ctx.isLoading || _ctx.isSaving[settings.pluginName]
-    }, null, 8, ["loading"])])], 8, 
PluginSettingsvue_type_template_id_919e3cb4_hoisted_2);
+    }, null, 8, ["loading"])])], 8, 
PluginSettingsvue_type_template_id_601e4fc6_hoisted_2);
   }), 128)), 
Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_PasswordConfirmation,
 {
     modelValue: _ctx.showPasswordConfirmModal,
     "onUpdate:modelValue": _cache[0] || (_cache[0] = function ($event) {
@@ -17320,7 +17320,7 @@ function 
PluginSettingsvue_type_template_id_919e3cb4_render(_ctx, _cache, $props
     onConfirmed: _ctx.confirmPassword
   }, null, 8, ["modelValue", "onConfirmed"])], 512);
 }
-// CONCATENATED MODULE: 
./plugins/CorePluginsAdmin/vue/src/PluginSettings/PluginSettings.vue?vue&type=template&id=919e3cb4
+// CONCATENATED MODULE: 
./plugins/CorePluginsAdmin/vue/src/PluginSettings/PluginSettings.vue?vue&type=template&id=601e4fc6
 
 // CONCATENATED MODULE: 
./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CorePluginsAdmin/vue/src/GroupedSettings/GroupedSettings.vue?vue&type=template&id=566a93cc
 
@@ -17807,6 +17807,10 @@ var PluginSettingsvue_type_script_lang_ts_window = 
window,
           postValue = '1';
         }
 
+        if (Array.isArray(postValue) && postValue.length === 0) {
+          postValue = '__empty__';
+        }
+
         values[pluginName].push({
           name: settingName,
           value: postValue
@@ -17822,7 +17826,7 @@ var PluginSettingsvue_type_script_lang_ts_window = 
window,
 
 
 
-PluginSettingsvue_type_script_lang_ts.render = 
PluginSettingsvue_type_template_id_919e3cb4_render
+PluginSettingsvue_type_script_lang_ts.render = 
PluginSettingsvue_type_template_id_601e4fc6_render
 
 /* harmony default export */ var PluginSettings = 
(PluginSettingsvue_type_script_lang_ts);
 // CONCATENATED MODULE: 
./plugins/CorePluginsAdmin/vue/src/Plugins/PluginFilter.ts
diff --git a/www/plugins/CorePluginsAdmin/vue/dist/CorePluginsAdmin.umd.min.js 
b/www/plugins/CorePluginsAdmin/vue/dist/CorePluginsAdmin.umd.min.js
index 67cd177..e0380c6 100644
--- a/www/plugins/CorePluginsAdmin/vue/dist/CorePluginsAdmin.umd.min.js
+++ b/www/plugins/CorePluginsAdmin/vue/dist/CorePluginsAdmin.umd.min.js
@@ -4,7 +4,7 @@
  *
  * @link    https://matomo.org
  * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */function Xs(e,t){if(!e)return[];var n=[];return 
Object.entries(e).forEach((function(e){var 
r=Ws(e,2),o=r[0],i=r[1];if(i&&"object"===zs(i)&&"undefined"!==typeof 
i.key)n.push(i);else{var a=o;"integer"===t&&"string"===typeof 
o&&(a=parseInt(a,10)),n.push({key:a,value:i})}})),n}function Qs(e){return 
Qs="function"===typeof Symbol&&"symbol"===typeof 
Symbol.iterator?function(e){return typeof e}:function(e){return 
e&&"function"===typeof 
Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof 
e},Qs(e)}var 
el=["password","url","search","email"],tl=["textarea","checkbox","text"],nl={checkbox:"FieldCheckbox","expandable-select":"FieldExpandableSelect","field-array":"FieldFieldArray",file:"FieldFile",hidden:"FieldHidden",multiselect:"FieldSelect",multituple:"FieldMultituple",number:"FieldNumber",radio:"FieldRadio",select:"FieldSelect",site:"FieldSite",text:"FieldText",textarea:"FieldTextarea"},rl={FieldSelect:ms,FieldCheckboxArray:Xs,FieldRadio:Xs,FieldExpandableSelect:oa},ol=Object(Oi["defineComponent"])({props:{modelValue:null,modelModifiers:Object,formField:{type:Object,required:!0}},emits:["update:modelValue"],components:{FieldCheckbox:Mi,FieldCheckboxArray:zi,FieldExpandableSelect:aa,FieldFieldArray:ua,FieldFile:ya,FieldHidden:wa,FieldMultituple:Ca,FieldNumber:Pa,FieldRadio:Fa,FieldSelect:bs,FieldSite:ws,FieldText:Cs,FieldTextArray:Ps,FieldTextarea:Fs,FieldTextareaArray:Rs},setup:function(e){var
 t=Object(Oi["ref"])(null),n=function(e){var n;e&&t.value&&"function"!==typeof 
e.render&&(n="string"===typeof 
e?0===e.indexOf("#")?window.$(e):window.vueSanitize(e):e,window.$(t.value).html("").append(n))};return
 Object(Oi["watch"])((function(){return 
e.formField.inlineHelp}),n),Object(Oi["onMounted"])((function(){n(e.formField.inlineHelp)})),{inlineHelp:t}},computed:{inlineHelpComponent:function(){var
 e=this.formField,t=e.inlineHelp;if(t&&"function"===typeof t.render)return 
e.inlineHelp},inlineHelpBind:function(){return 
this.inlineHelpComponent?this.formField.inlineHelpBind:void 
0},childComponent:function(){var e=this.formField;if(e.component){var 
t=e.component;if(e.component.plugin){var 
n=e.component,r=n.plugin,o=n.name;if(!r||!o)throw new Error("Invalid component 
property given to FormField directive, must be {plugin: '...',name: 
'...'}");t=Object(Ci["useExternalPluginComponent"])(r,o)}return 
Object(Oi["markRaw"])(t)}var 
i=e.uiControl,a=nl[i];return-1!==el.indexOf(i)&&(a="FieldText"),"array"===this.formField.type&&-1!==tl.indexOf(i)&&(a="".concat(a,"Array")),a},extraChildComponentParams:function(){return"multiselect"===this.formField.uiControl?{multiple:!0}:{}},showFormHelp:function(){return
 
this.formField.description||this.formField.inlineHelp||this.showDefaultValue||this.hasInlineHelpSlot},showDefaultValue:function(){return
 
this.defaultValuePretty&&"checkbox"!==this.formField.uiControl&&"radio"!==this.formField.uiControl},processedModelValue:function(){var
 e=this.formField;if("boolean"===e.type){var 
t=this.modelValue&&this.modelValue>0&&"0"!==this.modelValue;if("checkbox"===e.uiControl)return
 t;if("radio"===e.uiControl)return t?"1":"0"}return 
this.modelValue},defaultValue:function(){var 
e=this.formField.defaultValue;return 
Array.isArray(e)?e.join(","):e},availableOptions:function(){var 
e=this.childComponent;if("string"!==typeof e)return null;var 
t=this.formField;return 
t.availableValues&&rl[e]?rl[e](t.availableValues,t.type,t.uiControlAttributes):null},defaultValuePretty:function(){var
 e=this.formField,t=e.defaultValue,n=this.availableOptions;if("string"===typeof 
t&&t){var 
r=null;try{r=JSON.parse(t)}catch(i){}if(null!==r&&"object"===Qs(r))return""}if(!Array.isArray(n))return
 Array.isArray(t)?"":t?"".concat(t):"";var o=[];return 
Array.isArray(t)||(t=[t]),(n||[]).forEach((function(e){"undefined"!==typeof 
e.value&&-1!==t.indexOf(e.key)&&o.push(e.value)})),o.join(", 
")},defaultValuePrettyTruncated:function(){return 
this.defaultValuePretty.substring(0,50)},hasInlineHelpSlot:function(){var 
e,t;if(!this.$slots["inline-help"])return!1;var 
n=this.$slots["inline-help"]();return!(null===n||void 
0===n||null===(e=n[0])||void 0===e||null===(t=e.children)||void 
0===t||!t.length)}},methods:{onChange:function(e){this.$emit("update:modelValue",e)}}});ol.render=Ei;var
 il=ol;function al(e,t,n,r,o,i){var 
a=Object(Oi["resolveComponent"])("FormField");return 
Object(Oi["openBlock"])(),Object(Oi["createBlock"])(a,{"form-field":e.field,"model-value":e.modelValue,"onUpdate:modelValue":t[0]||(t[0]=function(t){return
 
e.onChange(t)}),"model-modifiers":e.modelModifiers},{"inline-help":Object(Oi["withCtx"])((function(){return[Object(Oi["renderSlot"])(e.$slots,"inline-help")]})),_:3},8,["form-field","model-value","model-modifiers"])}var
 
sl={multiselect:"array",checkbox:"boolean",site:"object",number:"integer"},ll=Object(Oi["defineComponent"])({props:{modelValue:null,modelModifiers:Object,uicontrol:String,name:String,defaultValue:null,options:[Object,Array],description:String,introduction:String,title:String,inlineHelp:[String,Object],inlineHelpBind:Object,disabled:Boolean,uiControlAttributes:{type:Object,default:function(){return{}}},uiControlOptions:{type:Object,default:function(){return{}}},autocomplete:String,varType:String,autofocus:Boolean,tabindex:Number,fullWidth:Boolean,maxlength:Number,required:Boolean,placeholder:String,rows:Number,min:Number,max:Number,component:null},emits:["update:modelValue"],components:{FormField:il},computed:{type:function(){if(this.varType)return
 this.varType;var e=this.uicontrol;return 
e&&sl[e]?sl[e]:"string"},field:function(){return{uiControl:this.uicontrol,type:this.type,name:this.name,defaultValue:this.defaultValue,availableValues:this.options,description:this.description,introduction:this.introduction,inlineHelp:this.inlineHelp,inlineHelpBind:this.inlineHelpBind,title:this.title,component:this.component,uiControlAttributes:Object.assign(Object.assign({},this.uiControlAttributes),{},{disabled:this.disabled,autocomplete:this.autocomplete,tabindex:this.tabindex,autofocus:this.autofocus,rows:this.rows,required:this.required,maxlength:this.maxlength,placeholder:this.placeholder,min:this.min,max:this.max}),fullWidth:this.fullWidth,uiControlOptions:this.uiControlOptions}}},methods:{onChange:function(e){this.$emit("update:modelValue",e)}}});ll.render=al;var
 
cl=ll,ul={class:"pluginSettings",ref:"root"},pl=["id"],dl={class:"card-content"},fl=["id"],ml=["onClick","disabled","value"];function
 hl(e,t,n,r,o,i){var 
a=Object(Oi["resolveComponent"])("GroupedSettings"),s=Object(Oi["resolveComponent"])("ActivityIndicator"),l=Object(Oi["resolveComponent"])("PasswordConfirmation");return
 
Object(Oi["openBlock"])(),Object(Oi["createElementBlock"])("div",ul,[(Object(Oi["openBlock"])(!0),Object(Oi["createElementBlock"])(Oi["Fragment"],null,Object(Oi["renderList"])(e.settingsPerPlugin,(function(t){return
 
Object(Oi["openBlock"])(),Object(Oi["createElementBlock"])("div",{class:"card",id:"".concat(t.pluginName,"PluginSettings"),key:"".concat(t.pluginName,"PluginSettings")},[Object(Oi["createElementVNode"])("div",dl,[Object(Oi["createElementVNode"])("h2",{class:"card-title",id:t.pluginName},Object(Oi["toDisplayString"])(t.title),9,fl),Object(Oi["createVNode"])(a,{"group-name":t.pluginName,settings:t.settings,"all-setting-values":e.settingValues,onChange:function(n){return
 
e.settingValues["".concat(t.pluginName,".").concat(n.name)]=n.value}},null,8,["group-name","settings","all-setting-values","onChange"]),Object(Oi["createElementVNode"])("input",{type:"button",onClick:function(n){return
 e.saveSetting(t.pluginName)},disabled:e.isLoading,class:"pluginsSettingsSubmit 
btn",value:e.translate("General_Save")},null,8,ml),Object(Oi["createVNode"])(s,{loading:e.isLoading||e.isSaving[t.pluginName]},null,8,["loading"])])],8,pl)})),128)),Object(Oi["createVNode"])(l,{modelValue:e.showPasswordConfirmModal,"onUpdate:modelValue":t[0]||(t[0]=function(t){return
 
e.showPasswordConfirmModal=t}),onConfirmed:e.confirmPassword},null,8,["modelValue","onConfirmed"])],512)}function
 gl(e,t,n,r,o,i){var a=Object(Oi["resolveComponent"])("GroupedSetting");return 
Object(Oi["openBlock"])(!0),Object(Oi["createElementBlock"])(Oi["Fragment"],null,Object(Oi["renderList"])(e.settings,(function(t){return
 
Object(Oi["openBlock"])(),Object(Oi["createElementBlock"])("div",{key:"".concat(e.groupPrefix).concat(t.name)},[Object(Oi["createVNode"])(a,{"model-value":e.allSettingValues["".concat(e.groupPrefix).concat(t.name)],"onUpdate:modelValue":function(n){return
 
e.$emit("change",{name:t.name,value:n})},setting:t,"condition-values":e.settingValues},null,8,["model-value","onUpdate:modelValue","setting","condition-values"])])})),128)}function
 bl(e,t,n,r,o,i){var a=Object(Oi["resolveComponent"])("FormField");return 
Object(Oi["withDirectives"])((Object(Oi["openBlock"])(),Object(Oi["createElementBlock"])("div",null,[Object(Oi["createVNode"])(a,{"model-value":e.modelValue,"onUpdate:modelValue":t[0]||(t[0]=function(t){return
 
e.changeValue(t)}),"form-field":e.setting},null,8,["model-value","form-field"])],512)),[[Oi["vShow"],e.showField]])}var
 
yl=Object(Oi["defineComponent"])({props:{setting:{type:Object,required:!0},modelValue:null,conditionValues:{type:Object,required:!0}},components:{FormField:il},emits:["update:modelValue"],computed:{showField:function(){var
 e=this.setting.condition;if(!e)return!0;e=e.replace(/&&/g," and 
"),e=e.replace(/\|\|/g," or "),e=e.replace(/!/g," not ");try{return 
vi.evaluate(e,this.conditionValues)}catch(t){return console.log("failed to 
parse setting condition '".concat(e,"': 
").concat(t.message)),console.log(this.conditionValues),!1}}},methods:{changeValue:function(e){this.$emit("update:modelValue",e)}}});yl.render=bl;var
 vl=yl;function Ol(e,t){return kl(e)||xl(e,t)||wl(e,t)||jl()}function 
jl(){throw new TypeError("Invalid attempt to destructure non-iterable 
instance.\nIn order to be iterable, non-array objects must have a 
[Symbol.iterator]() method.")}function wl(e,t){if(e){if("string"===typeof 
e)return Nl(e,t);var 
n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Nl(e,t):void
 0}}function Nl(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new 
Array(t);n<t;n++)r[n]=e[n];return r}function xl(e,t){var 
n=null==e?null:"undefined"!==typeof 
Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var 
r,o,i=[],a=!0,s=!1;try{for(n=n.call(e);!(a=(r=n.next()).done);a=!0)if(i.push(r.value),t&&i.length===t)break}catch(l){s=!0,o=l}finally{try{a||null==n["return"]||n["return"]()}finally{if(s)throw
 o}}return i}}function kl(e){if(Array.isArray(e))return e}var 
El=Object(Oi["defineComponent"])({props:{groupName:String,settings:{type:Array,required:!0},allSettingValues:{type:Object,required:!0}},emits:["change"],components:{GroupedSetting:vl},computed:{settingValues:function(){var
 e=this,t=Object.entries(this.allSettingValues).filter((function(t){var 
n=Ol(t,1),r=n[0];if(e.groupName){var 
o=r.split("."),i=Ol(o,1),a=i[0];if(a!==e.groupName)return!1}return!0})).map((function(t){var
 n=Ol(t,2),r=n[0],o=n[1];return e.groupName?[r.split(".")[1],o]:[r,o]}));return 
Object.fromEntries(t)},groupPrefix:function(){return 
this.groupName?"".concat(this.groupName,"."):""}}});El.render=gl;var 
Cl=El,Sl={class:"confirm-password-modal 
modal",ref:"root"},Al={class:"modal-content"},Vl={class:"modal-text"},Tl={ref:"content"},Pl={key:0},Ml={key:1},Bl={key:2},Dl={class:"modal-footer"},$l=["disabled"];function
 Fl(e,t,n,r,o,i){var a=Object(Oi["resolveComponent"])("Field");return 
Object(Oi["openBlock"])(),Object(Oi["createElementBlock"])("div",Sl,[Object(Oi["createElementVNode"])("div",Al,[Object(Oi["createElementVNode"])("div",Vl,[Object(Oi["createElementVNode"])("div",Tl,[Object(Oi["renderSlot"])(e.$slots,"default")],512),e.requiresPasswordConfirmation||e.slotHasContent?Object(Oi["createCommentVNode"])("",!0):(Object(Oi["openBlock"])(),Object(Oi["createElementBlock"])("h2",Pl,Object(Oi["toDisplayString"])(e.translate("UsersManager_ConfirmThisChange")),1)),e.requiresPasswordConfirmation&&!e.slotHasContent?(Object(Oi["openBlock"])(),Object(Oi["createElementBlock"])("h2",Ml,Object(Oi["toDisplayString"])(e.translate("UsersManager_ConfirmWithPassword")),1)):Object(Oi["createCommentVNode"])("",!0),e.requiresPasswordConfirmation&&e.slotHasContent?(Object(Oi["openBlock"])(),Object(Oi["createElementBlock"])("div",Bl,Object(Oi["toDisplayString"])(e.translate("UsersManager_ConfirmWithPassword")),1)):Object(Oi["createCommentVNode"])("",!0)]),Object(Oi["withDirectives"])(Object(Oi["createElementVNode"])("div",null,[Object(Oi["createVNode"])(a,{modelValue:e.passwordConfirmation,"onUpdate:modelValue":t[0]||(t[0]=function(t){return
 
e.passwordConfirmation=t}),uicontrol:"password",disabled:e.requiresPasswordConfirmation?void
 
0:"disabled",name:"currentUserPassword",autocomplete:"off","full-width":!0,title:e.translate("UsersManager_YourCurrentPassword")},null,8,["modelValue","disabled","title"])],512),[[Oi["vShow"],e.requiresPasswordConfirmation]])]),Object(Oi["createElementVNode"])("div",Dl,[Object(Oi["createElementVNode"])("a",{href:"",class:"modal-action
 modal-close 
btn",disabled:e.requiresPasswordConfirmation&&!e.passwordConfirmation?"disabled":void
 0,onClick:t[1]||(t[1]=function(t){return 
e.onClickConfirm(t)})},Object(Oi["toDisplayString"])(e.translate("General_Confirm")),9,$l),Object(Oi["createElementVNode"])("a",{href:"",class:"modal-action
 modal-close modal-no btn-flat",onClick:t[2]||(t[2]=function(t){return 
e.onClickCancel(t)})},Object(Oi["toDisplayString"])(e.translate("General_Cancel")),1)])],512)}var
 
_l=window,Il=_l.$,Ll=Object(Oi["defineComponent"])({props:{modelValue:{type:Boolean,required:!0}},data:function(){return{passwordConfirmation:"",slotHasContent:!0}},emits:["confirmed","aborted","update:modelValue"],components:{Field:cl},activated:function(){this.$emit("update:modelValue",!1)},methods:{onClickConfirm:function(e){e.preventDefault(),this.$emit("confirmed",this.passwordConfirmation),this.passwordConfirmation=""},onClickCancel:function(e){e.preventDefault(),this.$emit("aborted"),this.passwordConfirmation=""},showPasswordConfirmModal:function(){var
 e=this;this.slotHasContent=!this.$refs.content.matches(":empty");var 
t=this.$refs.root,n=Il(t),r=function(t){var 
r=t.keyCode?t.keyCode:t.which;13===r&&(n.modal("close"),e.$emit("confirmed",e.passwordConfirmation),e.passwordConfirmation="")};n.modal({dismissible:!1,onOpenEnd:function(){var
 e=".modal.open 
#currentUserPassword";Il(e).focus(),Il(e).off("keypress").keypress(r)},onCloseEnd:function(){e.$emit("update:modelValue",!1)}}).modal("open")}},computed:{requiresPasswordConfirmation:function(){return!!Ci["Matomo"].requiresPasswordConfirmation}},watch:{modelValue:function(e){e&&this.showPasswordConfirmModal()}}});Ll.render=Fl;var
 Ul=Ll;function Hl(e,t){return Jl(e)||Wl(e,t)||Rl(e,t)||ql()}function 
ql(){throw new TypeError("Invalid attempt to destructure non-iterable 
instance.\nIn order to be iterable, non-array objects must have a 
[Symbol.iterator]() method.")}function Rl(e,t){if(e){if("string"===typeof 
e)return zl(e,t);var 
n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?zl(e,t):void
 0}}function zl(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new 
Array(t);n<t;n++)r[n]=e[n];return r}function Wl(e,t){var 
n=null==e?null:"undefined"!==typeof 
Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var 
r,o,i=[],a=!0,s=!1;try{for(n=n.call(e);!(a=(r=n.next()).done);a=!0)if(i.push(r.value),t&&i.length===t)break}catch(l){s=!0,o=l}finally{try{a||null==n["return"]||n["return"]()}finally{if(s)throw
 o}}return i}}function Jl(e){if(Array.isArray(e))return e}var 
Gl=window,Kl=Gl.$,Yl=Object(Oi["defineComponent"])({props:{mode:String},components:{PasswordConfirmation:Ul,ActivityIndicator:Ci["ActivityIndicator"],GroupedSettings:Cl},data:function(){return{isLoading:!0,isSaving:{},showPasswordConfirmModal:!1,settingsToSave:null,settingsPerPlugin:[],settingValues:{}}},created:function(){var
 
e=this;Ci["AjaxHelper"].fetch({method:this.apiMethod}).then((function(t){e.isLoading=!1,e.settingsPerPlugin=t,t.forEach((function(t){t.settings.forEach((function(n){e.settingValues["".concat(t.pluginName,".").concat(n.name)]=n.value}))})),Object(Ci["scrollToAnchorInUrl"])(),e.addSectionsToTableOfContents()})).catch((function(){e.isLoading=!1}))},computed:{apiMethod:function(){return"admin"===this.mode?"CorePluginsAdmin.getSystemSettings":"CorePluginsAdmin.getUserSettings"},saveApiMethod:function(){return"admin"===this.mode?"CorePluginsAdmin.setSystemSettings":"CorePluginsAdmin.setUserSettings"}},methods:{addSectionsToTableOfContents:function(){var
 e=Kl("#generalSettingsTOC");if(e.length){var 
t=this.settingsPerPlugin;t.forEach((function(t){var 
n=t.pluginName,r=t.settings;n&&("CoreAdminHome"===n&&r?r.filter((function(e){return
 e.introduction})).forEach((function(t){e.append('<a 
href="#/'.concat(n,'PluginSettings">').concat(t.introduction,"</a> 
"))})):e.append('<a href="#/'.concat(n,'">').concat(n.replace(/([A-Z])/g," 
$1").trim(),"</a> 
")))}))}},confirmPassword:function(e){this.showPasswordConfirmModal=!1,this.save(this.settingsToSave,e)},saveSetting:function(e){"admin"===this.mode?(this.settingsToSave=e,this.showPasswordConfirmModal=!0):this.save(e)},save:function(e,t){var
 n=this,r=this.saveApiMethod;this.isSaving[e]=!0;var 
o=this.getValuesForPlugin(e);Ci["AjaxHelper"].post({method:r},{settingValues:o,passwordConfirmation:t}).then((function(){n.isSaving[e]=!1;var
 
t=Ci["NotificationsStore"].show({message:Object(Ci["translate"])("CoreAdminHome_PluginSettingsSaveSuccess"),id:"generalSettings",context:"success",type:"transient"});Ci["NotificationsStore"].scrollToNotification(t)})).catch((function(){n.isSaving[e]=!1})),this.settingsToSave=null},getValuesForPlugin:function(e){var
 t={};return 
t[e]||(t[e]=[]),Object.entries(this.settingValues).forEach((function(n){var 
r=Hl(n,2),o=r[0],i=r[1],a=o.split("."),s=Hl(a,2),l=s[0],c=s[1];if(l===e){var 
u=i;!1===u?u="0":!0===u&&(u="1"),t[l].push({name:c,value:u})}})),t}}});Yl.render=hl;var
 Zl=Yl,Xl=window,Ql=Xl.$;
+ */function Xs(e,t){if(!e)return[];var n=[];return 
Object.entries(e).forEach((function(e){var 
r=Ws(e,2),o=r[0],i=r[1];if(i&&"object"===zs(i)&&"undefined"!==typeof 
i.key)n.push(i);else{var a=o;"integer"===t&&"string"===typeof 
o&&(a=parseInt(a,10)),n.push({key:a,value:i})}})),n}function Qs(e){return 
Qs="function"===typeof Symbol&&"symbol"===typeof 
Symbol.iterator?function(e){return typeof e}:function(e){return 
e&&"function"===typeof 
Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof 
e},Qs(e)}var 
el=["password","url","search","email"],tl=["textarea","checkbox","text"],nl={checkbox:"FieldCheckbox","expandable-select":"FieldExpandableSelect","field-array":"FieldFieldArray",file:"FieldFile",hidden:"FieldHidden",multiselect:"FieldSelect",multituple:"FieldMultituple",number:"FieldNumber",radio:"FieldRadio",select:"FieldSelect",site:"FieldSite",text:"FieldText",textarea:"FieldTextarea"},rl={FieldSelect:ms,FieldCheckboxArray:Xs,FieldRadio:Xs,FieldExpandableSelect:oa},ol=Object(Oi["defineComponent"])({props:{modelValue:null,modelModifiers:Object,formField:{type:Object,required:!0}},emits:["update:modelValue"],components:{FieldCheckbox:Mi,FieldCheckboxArray:zi,FieldExpandableSelect:aa,FieldFieldArray:ua,FieldFile:ya,FieldHidden:wa,FieldMultituple:Ca,FieldNumber:Pa,FieldRadio:Fa,FieldSelect:bs,FieldSite:ws,FieldText:Cs,FieldTextArray:Ps,FieldTextarea:Fs,FieldTextareaArray:Rs},setup:function(e){var
 t=Object(Oi["ref"])(null),n=function(e){var n;e&&t.value&&"function"!==typeof 
e.render&&(n="string"===typeof 
e?0===e.indexOf("#")?window.$(e):window.vueSanitize(e):e,window.$(t.value).html("").append(n))};return
 Object(Oi["watch"])((function(){return 
e.formField.inlineHelp}),n),Object(Oi["onMounted"])((function(){n(e.formField.inlineHelp)})),{inlineHelp:t}},computed:{inlineHelpComponent:function(){var
 e=this.formField,t=e.inlineHelp;if(t&&"function"===typeof t.render)return 
e.inlineHelp},inlineHelpBind:function(){return 
this.inlineHelpComponent?this.formField.inlineHelpBind:void 
0},childComponent:function(){var e=this.formField;if(e.component){var 
t=e.component;if(e.component.plugin){var 
n=e.component,r=n.plugin,o=n.name;if(!r||!o)throw new Error("Invalid component 
property given to FormField directive, must be {plugin: '...',name: 
'...'}");t=Object(Ci["useExternalPluginComponent"])(r,o)}return 
Object(Oi["markRaw"])(t)}var 
i=e.uiControl,a=nl[i];return-1!==el.indexOf(i)&&(a="FieldText"),"array"===this.formField.type&&-1!==tl.indexOf(i)&&(a="".concat(a,"Array")),a},extraChildComponentParams:function(){return"multiselect"===this.formField.uiControl?{multiple:!0}:{}},showFormHelp:function(){return
 
this.formField.description||this.formField.inlineHelp||this.showDefaultValue||this.hasInlineHelpSlot},showDefaultValue:function(){return
 
this.defaultValuePretty&&"checkbox"!==this.formField.uiControl&&"radio"!==this.formField.uiControl},processedModelValue:function(){var
 e=this.formField;if("boolean"===e.type){var 
t=this.modelValue&&this.modelValue>0&&"0"!==this.modelValue;if("checkbox"===e.uiControl)return
 t;if("radio"===e.uiControl)return t?"1":"0"}return 
this.modelValue},defaultValue:function(){var 
e=this.formField.defaultValue;return 
Array.isArray(e)?e.join(","):e},availableOptions:function(){var 
e=this.childComponent;if("string"!==typeof e)return null;var 
t=this.formField;return 
t.availableValues&&rl[e]?rl[e](t.availableValues,t.type,t.uiControlAttributes):null},defaultValuePretty:function(){var
 e=this.formField,t=e.defaultValue,n=this.availableOptions;if("string"===typeof 
t&&t){var 
r=null;try{r=JSON.parse(t)}catch(i){}if(null!==r&&"object"===Qs(r))return""}if(!Array.isArray(n))return
 Array.isArray(t)?"":t?"".concat(t):"";var o=[];return 
Array.isArray(t)||(t=[t]),(n||[]).forEach((function(e){"undefined"!==typeof 
e.value&&-1!==t.indexOf(e.key)&&o.push(e.value)})),o.join(", 
")},defaultValuePrettyTruncated:function(){return 
this.defaultValuePretty.substring(0,50)},hasInlineHelpSlot:function(){var 
e,t;if(!this.$slots["inline-help"])return!1;var 
n=this.$slots["inline-help"]();return!(null===n||void 
0===n||null===(e=n[0])||void 0===e||null===(t=e.children)||void 
0===t||!t.length)}},methods:{onChange:function(e){this.$emit("update:modelValue",e)}}});ol.render=Ei;var
 il=ol;function al(e,t,n,r,o,i){var 
a=Object(Oi["resolveComponent"])("FormField");return 
Object(Oi["openBlock"])(),Object(Oi["createBlock"])(a,{"form-field":e.field,"model-value":e.modelValue,"onUpdate:modelValue":t[0]||(t[0]=function(t){return
 
e.onChange(t)}),"model-modifiers":e.modelModifiers},{"inline-help":Object(Oi["withCtx"])((function(){return[Object(Oi["renderSlot"])(e.$slots,"inline-help")]})),_:3},8,["form-field","model-value","model-modifiers"])}var
 
sl={multiselect:"array",checkbox:"boolean",site:"object",number:"integer"},ll=Object(Oi["defineComponent"])({props:{modelValue:null,modelModifiers:Object,uicontrol:String,name:String,defaultValue:null,options:[Object,Array],description:String,introduction:String,title:String,inlineHelp:[String,Object],inlineHelpBind:Object,disabled:Boolean,uiControlAttributes:{type:Object,default:function(){return{}}},uiControlOptions:{type:Object,default:function(){return{}}},autocomplete:String,varType:String,autofocus:Boolean,tabindex:Number,fullWidth:Boolean,maxlength:Number,required:Boolean,placeholder:String,rows:Number,min:Number,max:Number,component:null},emits:["update:modelValue"],components:{FormField:il},computed:{type:function(){if(this.varType)return
 this.varType;var e=this.uicontrol;return 
e&&sl[e]?sl[e]:"string"},field:function(){return{uiControl:this.uicontrol,type:this.type,name:this.name,defaultValue:this.defaultValue,availableValues:this.options,description:this.description,introduction:this.introduction,inlineHelp:this.inlineHelp,inlineHelpBind:this.inlineHelpBind,title:this.title,component:this.component,uiControlAttributes:Object.assign(Object.assign({},this.uiControlAttributes),{},{disabled:this.disabled,autocomplete:this.autocomplete,tabindex:this.tabindex,autofocus:this.autofocus,rows:this.rows,required:this.required,maxlength:this.maxlength,placeholder:this.placeholder,min:this.min,max:this.max}),fullWidth:this.fullWidth,uiControlOptions:this.uiControlOptions}}},methods:{onChange:function(e){this.$emit("update:modelValue",e)}}});ll.render=al;var
 
cl=ll,ul={class:"pluginSettings",ref:"root"},pl=["id"],dl={class:"card-content"},fl=["id"],ml=["onClick","disabled","value"];function
 hl(e,t,n,r,o,i){var 
a=Object(Oi["resolveComponent"])("GroupedSettings"),s=Object(Oi["resolveComponent"])("ActivityIndicator"),l=Object(Oi["resolveComponent"])("PasswordConfirmation");return
 
Object(Oi["openBlock"])(),Object(Oi["createElementBlock"])("div",ul,[(Object(Oi["openBlock"])(!0),Object(Oi["createElementBlock"])(Oi["Fragment"],null,Object(Oi["renderList"])(e.settingsPerPlugin,(function(t){return
 
Object(Oi["openBlock"])(),Object(Oi["createElementBlock"])("div",{class:"card",id:"".concat(t.pluginName,"PluginSettings"),key:"".concat(t.pluginName,"PluginSettings")},[Object(Oi["createElementVNode"])("div",dl,[Object(Oi["createElementVNode"])("h2",{class:"card-title",id:t.pluginName},Object(Oi["toDisplayString"])(t.title),9,fl),Object(Oi["createVNode"])(a,{"group-name":t.pluginName,settings:t.settings,"all-setting-values":e.settingValues,onChange:function(n){return
 
e.settingValues["".concat(t.pluginName,".").concat(n.name)]=n.value}},null,8,["group-name","settings","all-setting-values","onChange"]),Object(Oi["createElementVNode"])("input",{type:"button",onClick:function(n){return
 e.saveSetting(t.pluginName)},disabled:e.isLoading,class:"pluginsSettingsSubmit 
btn",value:e.translate("General_Save")},null,8,ml),Object(Oi["createVNode"])(s,{loading:e.isLoading||e.isSaving[t.pluginName]},null,8,["loading"])])],8,pl)})),128)),Object(Oi["createVNode"])(l,{modelValue:e.showPasswordConfirmModal,"onUpdate:modelValue":t[0]||(t[0]=function(t){return
 
e.showPasswordConfirmModal=t}),onConfirmed:e.confirmPassword},null,8,["modelValue","onConfirmed"])],512)}function
 gl(e,t,n,r,o,i){var a=Object(Oi["resolveComponent"])("GroupedSetting");return 
Object(Oi["openBlock"])(!0),Object(Oi["createElementBlock"])(Oi["Fragment"],null,Object(Oi["renderList"])(e.settings,(function(t){return
 
Object(Oi["openBlock"])(),Object(Oi["createElementBlock"])("div",{key:"".concat(e.groupPrefix).concat(t.name)},[Object(Oi["createVNode"])(a,{"model-value":e.allSettingValues["".concat(e.groupPrefix).concat(t.name)],"onUpdate:modelValue":function(n){return
 
e.$emit("change",{name:t.name,value:n})},setting:t,"condition-values":e.settingValues},null,8,["model-value","onUpdate:modelValue","setting","condition-values"])])})),128)}function
 bl(e,t,n,r,o,i){var a=Object(Oi["resolveComponent"])("FormField");return 
Object(Oi["withDirectives"])((Object(Oi["openBlock"])(),Object(Oi["createElementBlock"])("div",null,[Object(Oi["createVNode"])(a,{"model-value":e.modelValue,"onUpdate:modelValue":t[0]||(t[0]=function(t){return
 
e.changeValue(t)}),"form-field":e.setting},null,8,["model-value","form-field"])],512)),[[Oi["vShow"],e.showField]])}var
 
yl=Object(Oi["defineComponent"])({props:{setting:{type:Object,required:!0},modelValue:null,conditionValues:{type:Object,required:!0}},components:{FormField:il},emits:["update:modelValue"],computed:{showField:function(){var
 e=this.setting.condition;if(!e)return!0;e=e.replace(/&&/g," and 
"),e=e.replace(/\|\|/g," or "),e=e.replace(/!/g," not ");try{return 
vi.evaluate(e,this.conditionValues)}catch(t){return console.log("failed to 
parse setting condition '".concat(e,"': 
").concat(t.message)),console.log(this.conditionValues),!1}}},methods:{changeValue:function(e){this.$emit("update:modelValue",e)}}});yl.render=bl;var
 vl=yl;function Ol(e,t){return kl(e)||xl(e,t)||wl(e,t)||jl()}function 
jl(){throw new TypeError("Invalid attempt to destructure non-iterable 
instance.\nIn order to be iterable, non-array objects must have a 
[Symbol.iterator]() method.")}function wl(e,t){if(e){if("string"===typeof 
e)return Nl(e,t);var 
n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Nl(e,t):void
 0}}function Nl(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new 
Array(t);n<t;n++)r[n]=e[n];return r}function xl(e,t){var 
n=null==e?null:"undefined"!==typeof 
Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var 
r,o,i=[],a=!0,s=!1;try{for(n=n.call(e);!(a=(r=n.next()).done);a=!0)if(i.push(r.value),t&&i.length===t)break}catch(l){s=!0,o=l}finally{try{a||null==n["return"]||n["return"]()}finally{if(s)throw
 o}}return i}}function kl(e){if(Array.isArray(e))return e}var 
El=Object(Oi["defineComponent"])({props:{groupName:String,settings:{type:Array,required:!0},allSettingValues:{type:Object,required:!0}},emits:["change"],components:{GroupedSetting:vl},computed:{settingValues:function(){var
 e=this,t=Object.entries(this.allSettingValues).filter((function(t){var 
n=Ol(t,1),r=n[0];if(e.groupName){var 
o=r.split("."),i=Ol(o,1),a=i[0];if(a!==e.groupName)return!1}return!0})).map((function(t){var
 n=Ol(t,2),r=n[0],o=n[1];return e.groupName?[r.split(".")[1],o]:[r,o]}));return 
Object.fromEntries(t)},groupPrefix:function(){return 
this.groupName?"".concat(this.groupName,"."):""}}});El.render=gl;var 
Cl=El,Sl={class:"confirm-password-modal 
modal",ref:"root"},Al={class:"modal-content"},Vl={class:"modal-text"},Tl={ref:"content"},Pl={key:0},Ml={key:1},Bl={key:2},Dl={class:"modal-footer"},$l=["disabled"];function
 Fl(e,t,n,r,o,i){var a=Object(Oi["resolveComponent"])("Field");return 
Object(Oi["openBlock"])(),Object(Oi["createElementBlock"])("div",Sl,[Object(Oi["createElementVNode"])("div",Al,[Object(Oi["createElementVNode"])("div",Vl,[Object(Oi["createElementVNode"])("div",Tl,[Object(Oi["renderSlot"])(e.$slots,"default")],512),e.requiresPasswordConfirmation||e.slotHasContent?Object(Oi["createCommentVNode"])("",!0):(Object(Oi["openBlock"])(),Object(Oi["createElementBlock"])("h2",Pl,Object(Oi["toDisplayString"])(e.translate("UsersManager_ConfirmThisChange")),1)),e.requiresPasswordConfirmation&&!e.slotHasContent?(Object(Oi["openBlock"])(),Object(Oi["createElementBlock"])("h2",Ml,Object(Oi["toDisplayString"])(e.translate("UsersManager_ConfirmWithPassword")),1)):Object(Oi["createCommentVNode"])("",!0),e.requiresPasswordConfirmation&&e.slotHasContent?(Object(Oi["openBlock"])(),Object(Oi["createElementBlock"])("div",Bl,Object(Oi["toDisplayString"])(e.translate("UsersManager_ConfirmWithPassword")),1)):Object(Oi["createCommentVNode"])("",!0)]),Object(Oi["withDirectives"])(Object(Oi["createElementVNode"])("div",null,[Object(Oi["createVNode"])(a,{modelValue:e.passwordConfirmation,"onUpdate:modelValue":t[0]||(t[0]=function(t){return
 
e.passwordConfirmation=t}),uicontrol:"password",disabled:e.requiresPasswordConfirmation?void
 
0:"disabled",name:"currentUserPassword",autocomplete:"off","full-width":!0,title:e.translate("UsersManager_YourCurrentPassword")},null,8,["modelValue","disabled","title"])],512),[[Oi["vShow"],e.requiresPasswordConfirmation]])]),Object(Oi["createElementVNode"])("div",Dl,[Object(Oi["createElementVNode"])("a",{href:"",class:"modal-action
 modal-close 
btn",disabled:e.requiresPasswordConfirmation&&!e.passwordConfirmation?"disabled":void
 0,onClick:t[1]||(t[1]=function(t){return 
e.onClickConfirm(t)})},Object(Oi["toDisplayString"])(e.translate("General_Confirm")),9,$l),Object(Oi["createElementVNode"])("a",{href:"",class:"modal-action
 modal-close modal-no btn-flat",onClick:t[2]||(t[2]=function(t){return 
e.onClickCancel(t)})},Object(Oi["toDisplayString"])(e.translate("General_Cancel")),1)])],512)}var
 
_l=window,Il=_l.$,Ll=Object(Oi["defineComponent"])({props:{modelValue:{type:Boolean,required:!0}},data:function(){return{passwordConfirmation:"",slotHasContent:!0}},emits:["confirmed","aborted","update:modelValue"],components:{Field:cl},activated:function(){this.$emit("update:modelValue",!1)},methods:{onClickConfirm:function(e){e.preventDefault(),this.$emit("confirmed",this.passwordConfirmation),this.passwordConfirmation=""},onClickCancel:function(e){e.preventDefault(),this.$emit("aborted"),this.passwordConfirmation=""},showPasswordConfirmModal:function(){var
 e=this;this.slotHasContent=!this.$refs.content.matches(":empty");var 
t=this.$refs.root,n=Il(t),r=function(t){var 
r=t.keyCode?t.keyCode:t.which;13===r&&(n.modal("close"),e.$emit("confirmed",e.passwordConfirmation),e.passwordConfirmation="")};n.modal({dismissible:!1,onOpenEnd:function(){var
 e=".modal.open 
#currentUserPassword";Il(e).focus(),Il(e).off("keypress").keypress(r)},onCloseEnd:function(){e.$emit("update:modelValue",!1)}}).modal("open")}},computed:{requiresPasswordConfirmation:function(){return!!Ci["Matomo"].requiresPasswordConfirmation}},watch:{modelValue:function(e){e&&this.showPasswordConfirmModal()}}});Ll.render=Fl;var
 Ul=Ll;function Hl(e,t){return Jl(e)||Wl(e,t)||Rl(e,t)||ql()}function 
ql(){throw new TypeError("Invalid attempt to destructure non-iterable 
instance.\nIn order to be iterable, non-array objects must have a 
[Symbol.iterator]() method.")}function Rl(e,t){if(e){if("string"===typeof 
e)return zl(e,t);var 
n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?zl(e,t):void
 0}}function zl(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new 
Array(t);n<t;n++)r[n]=e[n];return r}function Wl(e,t){var 
n=null==e?null:"undefined"!==typeof 
Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var 
r,o,i=[],a=!0,s=!1;try{for(n=n.call(e);!(a=(r=n.next()).done);a=!0)if(i.push(r.value),t&&i.length===t)break}catch(l){s=!0,o=l}finally{try{a||null==n["return"]||n["return"]()}finally{if(s)throw
 o}}return i}}function Jl(e){if(Array.isArray(e))return e}var 
Gl=window,Kl=Gl.$,Yl=Object(Oi["defineComponent"])({props:{mode:String},components:{PasswordConfirmation:Ul,ActivityIndicator:Ci["ActivityIndicator"],GroupedSettings:Cl},data:function(){return{isLoading:!0,isSaving:{},showPasswordConfirmModal:!1,settingsToSave:null,settingsPerPlugin:[],settingValues:{}}},created:function(){var
 
e=this;Ci["AjaxHelper"].fetch({method:this.apiMethod}).then((function(t){e.isLoading=!1,e.settingsPerPlugin=t,t.forEach((function(t){t.settings.forEach((function(n){e.settingValues["".concat(t.pluginName,".").concat(n.name)]=n.value}))})),Object(Ci["scrollToAnchorInUrl"])(),e.addSectionsToTableOfContents()})).catch((function(){e.isLoading=!1}))},computed:{apiMethod:function(){return"admin"===this.mode?"CorePluginsAdmin.getSystemSettings":"CorePluginsAdmin.getUserSettings"},saveApiMethod:function(){return"admin"===this.mode?"CorePluginsAdmin.setSystemSettings":"CorePluginsAdmin.setUserSettings"}},methods:{addSectionsToTableOfContents:function(){var
 e=Kl("#generalSettingsTOC");if(e.length){var 
t=this.settingsPerPlugin;t.forEach((function(t){var 
n=t.pluginName,r=t.settings;n&&("CoreAdminHome"===n&&r?r.filter((function(e){return
 e.introduction})).forEach((function(t){e.append('<a 
href="#/'.concat(n,'PluginSettings">').concat(t.introduction,"</a> 
"))})):e.append('<a href="#/'.concat(n,'">').concat(n.replace(/([A-Z])/g," 
$1").trim(),"</a> 
")))}))}},confirmPassword:function(e){this.showPasswordConfirmModal=!1,this.save(this.settingsToSave,e)},saveSetting:function(e){"admin"===this.mode?(this.settingsToSave=e,this.showPasswordConfirmModal=!0):this.save(e)},save:function(e,t){var
 n=this,r=this.saveApiMethod;this.isSaving[e]=!0;var 
o=this.getValuesForPlugin(e);Ci["AjaxHelper"].post({method:r},{settingValues:o,passwordConfirmation:t}).then((function(){n.isSaving[e]=!1;var
 
t=Ci["NotificationsStore"].show({message:Object(Ci["translate"])("CoreAdminHome_PluginSettingsSaveSuccess"),id:"generalSettings",context:"success",type:"transient"});Ci["NotificationsStore"].scrollToNotification(t)})).catch((function(){n.isSaving[e]=!1})),this.settingsToSave=null},getValuesForPlugin:function(e){var
 t={};return 
t[e]||(t[e]=[]),Object.entries(this.settingValues).forEach((function(n){var 
r=Hl(n,2),o=r[0],i=r[1],a=o.split("."),s=Hl(a,2),l=s[0],c=s[1];if(l===e){var 
u=i;!1===u?u="0":!0===u&&(u="1"),Array.isArray(u)&&0===u.length&&(u="__empty__"),t[l].push({name:c,value:u})}})),t}}});Yl.render=hl;var
 Zl=Yl,Xl=window,Ql=Xl.$;
 /*!
  * Matomo - free/libre analytics platform
  *
diff --git 
a/www/plugins/CorePluginsAdmin/vue/src/PluginSettings/PluginSettings.vue 
b/www/plugins/CorePluginsAdmin/vue/src/PluginSettings/PluginSettings.vue
index 0e47b71..914f68c 100644
--- a/www/plugins/CorePluginsAdmin/vue/src/PluginSettings/PluginSettings.vue
+++ b/www/plugins/CorePluginsAdmin/vue/src/PluginSettings/PluginSettings.vue
@@ -199,6 +199,10 @@ export default defineComponent({
           postValue = '1';
         }
 
+        if (Array.isArray(postValue) && postValue.length === 0) {
+          postValue = '__empty__';
+        }
+
         values[pluginName].push({
           name: settingName,
           value: postValue,
diff --git a/www/plugins/Monolog/Formatter/LineMessageFormatter.php 
b/www/plugins/Monolog/Formatter/LineMessageFormatter.php
index 6639b56..0468820 100644
--- a/www/plugins/Monolog/Formatter/LineMessageFormatter.php
+++ b/www/plugins/Monolog/Formatter/LineMessageFormatter.php
@@ -53,6 +53,8 @@ public function format(array $record)
         $total = '';
 
         foreach ($messages as $message) {
+            // escape control characters
+            $message = addcslashes($message, "\x00..\x09\x0B..\x1F\x7F");
             $message = $this->prefixMessageWithRequestId($record, $message);
             $total  .= $this->formatMessage($class, $message, $date, $record);
         }
diff --git a/www/plugins/Overlay/templates/startOverlaySession.twig 
b/www/plugins/Overlay/templates/startOverlaySession.twig
index 7e8b4d5..ce1cfb3 100644
--- a/www/plugins/Overlay/templates/startOverlaySession.twig
+++ b/www/plugins/Overlay/templates/startOverlaySession.twig
@@ -16,22 +16,24 @@
         parser.href = urlToRedirect;
         var hostToRedirect = parser.hostname;
 
-        var knownUrls = {{ knownUrls|raw }};
-        for (var i = 0; i < knownUrls.length; i++) {
-            parser.href = knownUrls[i];
-            var testHost = parser.hostname;
-            if (hostToRedirect === testHost && testHost) {
-                match = true;
-                if (navigator.appName == "Microsoft Internet Explorer") {
-                    // internet explorer loses the referrer if we use 
window.location.href=X
-                    var referLink = document.createElement("a");
-                    referLink.href = handleProtocol(urlToRedirect);
-                    document.body.appendChild(referLink);
-                    referLink.click();
-                } else {
-                    window.location.href = handleProtocol(urlToRedirect);
+        if (parser.protocol === 'http:' || parser.protocol === 'https:') {
+            var knownUrls = {{ knownUrls|raw }};
+            for (var i = 0; i < knownUrls.length; i++) {
+                parser.href = knownUrls[i];
+                var testHost = parser.hostname;
+                if (hostToRedirect === testHost && testHost) {
+                    match = true;
+                    if (navigator.appName == "Microsoft Internet Explorer") {
+                        // internet explorer loses the referrer if we use 
window.location.href=X
+                        var referLink = document.createElement("a");
+                        referLink.href = handleProtocol(urlToRedirect);
+                        document.body.appendChild(referLink);
+                        referLink.click();
+                    } else {
+                        window.location.href = handleProtocol(urlToRedirect);
+                    }
+                    break;
                 }
-                break;
             }
         }
 
diff --git a/www/plugins/UsersManager/API.php b/www/plugins/UsersManager/API.php
index 999604f..ae16801 100644
--- a/www/plugins/UsersManager/API.php
+++ b/www/plugins/UsersManager/API.php
@@ -16,6 +16,8 @@
 use Piwik\Access\RolesProvider;
 use Piwik\Auth\Password;
 use Piwik\Common;
+use Piwik\Concurrency\Lock;
+use Piwik\Concurrency\LockBackend;
 use Piwik\Config;
 use Piwik\Container\StaticContainer;
 use Piwik\Date;
@@ -825,33 +827,35 @@ public function inviteUser($userLogin, $email, 
$initialIdSite = null, $expiryInD
      */
     public function setSuperUserAccess($userLogin, $hasSuperUserAccess, 
$passwordConfirmation = null)
     {
-        Piwik::checkUserHasSuperUserAccess();
-        $this->checkUserIsNotAnonymous($userLogin);
-        UsersManager::dieIfUsersAdminIsDisabled();
-
-        $requirePasswordConfirmation = 
self::$SET_SUPERUSER_ACCESS_REQUIRE_PASSWORD_CONFIRMATION;
-        self::$SET_SUPERUSER_ACCESS_REQUIRE_PASSWORD_CONFIRMATION = true;
-
-        $isCliMode = Common::isPhpCliMode() && !(defined('PIWIK_TEST_MODE') && 
PIWIK_TEST_MODE);
-        if (
-            !$isCliMode
-            && $requirePasswordConfirmation
-        ) {
-            $this->confirmCurrentUserPassword($passwordConfirmation);
-        }
-        $this->checkUserExists($userLogin);
+        $this->executeConcurrencySafe($userLogin, function () use ($userLogin, 
$hasSuperUserAccess, $passwordConfirmation) {
+            Piwik::checkUserHasSuperUserAccess();
+            $this->checkUserIsNotAnonymous($userLogin);
+            UsersManager::dieIfUsersAdminIsDisabled();
+
+            $requirePasswordConfirmation = 
self::$SET_SUPERUSER_ACCESS_REQUIRE_PASSWORD_CONFIRMATION;
+            self::$SET_SUPERUSER_ACCESS_REQUIRE_PASSWORD_CONFIRMATION = true;
+
+            $isCliMode = Common::isPhpCliMode() && 
!(defined('PIWIK_TEST_MODE') && PIWIK_TEST_MODE);
+            if (
+                !$isCliMode
+                && $requirePasswordConfirmation
+            ) {
+                $this->confirmCurrentUserPassword($passwordConfirmation);
+            }
+            $this->checkUserExists($userLogin);
 
-        if (!$hasSuperUserAccess && 
$this->isUserTheOnlyUserHavingSuperUserAccess($userLogin)) {
-            $message = 
Piwik::translate("UsersManager_ExceptionRemoveSuperUserAccessOnlySuperUser", 
$userLogin)
-              . " "
-              . 
Piwik::translate("UsersManager_ExceptionYouMustGrantSuperUserAccessFirst");
-            throw new Exception($message);
-        }
+            if (!$hasSuperUserAccess && 
$this->isUserTheOnlyUserHavingSuperUserAccess($userLogin)) {
+                $message = 
Piwik::translate("UsersManager_ExceptionRemoveSuperUserAccessOnlySuperUser", 
$userLogin)
+                    . " "
+                    . 
Piwik::translate("UsersManager_ExceptionYouMustGrantSuperUserAccessFirst");
+                throw new Exception($message);
+            }
 
-        $this->model->deleteUserAccess($userLogin);
-        $this->model->setSuperUserAccess($userLogin, $hasSuperUserAccess);
+            $this->model->deleteUserAccess($userLogin);
+            $this->model->setSuperUserAccess($userLogin, $hasSuperUserAccess);
 
-        Cache::deleteTrackerCache();
+            Cache::deleteTrackerCache();
+        });
     }
 
     /**
@@ -1152,46 +1156,50 @@ public function setUserAccess($userLogin, $access, 
$idSites, $passwordConfirmati
         }
 
         $this->checkUserExist($userLogin);
-        $this->checkUsersHasNotSuperUserAccess($userLogin);
 
-        $this->model->deleteUserAccess($userLogin, $idSites);
+        $this->executeConcurrencySafe($userLogin, function () use ($userLogin, 
$access, $idSites, $roles, $capabilities) {
+            $idSites = $this->getIdSitesCheckAdminAccess($idSites);
+            $this->checkUsersHasNotSuperUserAccess($userLogin);
 
-        if ($access === 'noaccess') {
-            // if the access is noaccess then we don't save it as this is the 
default value
-            // when no access are specified
-            Piwik::postEvent('UsersManager.removeSiteAccess', [$userLogin, 
$idSites]);
-        } else {
-            $role = array_shift($roles);
-            $this->model->addUserAccess($userLogin, $role, $idSites);
-        }
+            $this->model->deleteUserAccess($userLogin, $idSites);
 
-        if (!empty($capabilities)) {
-            $this->addCapabilities($userLogin, $capabilities, $idSites);
-        }
+            if ($access === 'noaccess') {
+                // if the access is noaccess then we don't save it as this is 
the default value
+                // when no access are specified
+                Piwik::postEvent('UsersManager.removeSiteAccess', [$userLogin, 
$idSites]);
+            } else {
+                $role = array_shift($roles);
+                $this->model->addUserAccess($userLogin, $role, $idSites);
+            }
 
-        // Send notification to all super users if anonymous access is set for 
a site
-        if ($userLogin === 'anonymous' && $access === 'view') {
-            $container = StaticContainer::getContainer();
+            if (!empty($capabilities)) {
+                $this->addCapabilitesToUser($userLogin, $capabilities, 
$idSites);
+            }
 
-            $siteNames = [];
+            // Send notification to all super users if anonymous access is set 
for a site
+            if ($userLogin === 'anonymous' && $access === 'view') {
+                $container = StaticContainer::getContainer();
 
-            foreach ($idSites as $idSite) {
-                $siteNames[] = Site::getNameFor($idSite);
-            }
+                $siteNames = [];
 
-            $superUsers = Piwik::getAllSuperUserAccessEmailAddresses();
-            foreach ($superUsers as $login => $email) {
-                $email = $container->make(AnonymousAccessEnabledEmail::class, 
array(
-                    'login' => $login,
-                    'emailAddress' => $email,
-                    'siteName' => implode(', ', $siteNames)
-                ));
-                $email->safeSend();
+                foreach ($idSites as $idSite) {
+                    $siteNames[] = Site::getNameFor($idSite);
+                }
+
+                $superUsers = Piwik::getAllSuperUserAccessEmailAddresses();
+                foreach ($superUsers as $login => $email) {
+                    $email = 
$container->make(AnonymousAccessEnabledEmail::class, array(
+                        'login' => $login,
+                        'emailAddress' => $email,
+                        'siteName' => implode(', ', $siteNames)
+                    ));
+                    $email->safeSend();
+                }
             }
-        }
 
-        // we reload the access list which doesn't yet take in consideration 
this new user access
-        $this->reloadPermissions();
+            // we reload the access list which doesn't yet take in 
consideration this new user access
+            $this->reloadPermissions();
+        });
     }
 
     /**
@@ -1208,28 +1216,40 @@ public function setUserAccess($userLogin, $access, 
$idSites, $passwordConfirmati
      */
     public function addCapabilities($userLogin, $capabilities, $idSites)
     {
-        $idSites = $this->getIdSitesCheckAdminAccess($idSites);
+        $this->executeConcurrencySafe($userLogin, function () use ($userLogin, 
$capabilities, $idSites) {
+            $idSites = $this->getIdSitesCheckAdminAccess($idSites);
 
-        if ($userLogin == 'anonymous') {
-            throw new 
Exception(Piwik::translate("UsersManager_ExceptionAnonymousNoCapabilities"));
-        }
+            if ($userLogin == 'anonymous') {
+                throw new 
Exception(Piwik::translate("UsersManager_ExceptionAnonymousNoCapabilities"));
+            }
 
-        $this->checkUserExists($userLogin);
-        $this->checkUsersHasNotSuperUserAccess([$userLogin]);
+            $this->checkUserExists($userLogin);
+            $this->checkUsersHasNotSuperUserAccess([$userLogin]);
 
-        if (!is_array($capabilities)) {
-            $capabilities = [$capabilities];
-        }
+            if (!is_array($capabilities)) {
+                $capabilities = [$capabilities];
+            }
 
-        foreach ($capabilities as $entry) {
-            $this->capabilityProvider->checkValidCapability($entry);
-        }
+            foreach ($capabilities as $entry) {
+                $this->capabilityProvider->checkValidCapability($entry);
+            }
+
+            $this->addCapabilitesToUser($userLogin, $capabilities, $idSites);
 
+            // we reload the access list which doesn't yet take in 
consideration this new user access
+            $this->reloadPermissions();
+        });
+    }
+
+    private function addCapabilitesToUser(string $userLogin, array 
$capabilities, $idSites)
+    {
         [$sitesIdWithRole, $sitesIdWithCapability] = 
$this->getRolesAndCapabilitiesForLogin($userLogin);
 
         foreach ($idSites as $idSite) {
             if (!array_key_exists($idSite, $sitesIdWithRole)) {
-                throw new 
Exception(Piwik::translate('UsersManager_ExceptionNoCapabilitiesWithoutRole', 
[$userLogin, $idSite]));
+                throw new Exception(
+                    
Piwik::translate('UsersManager_ExceptionNoCapabilitiesWithoutRole', 
[$userLogin, $idSite])
+                );
             }
         }
 
@@ -1252,9 +1272,6 @@ public function addCapabilities($userLogin, 
$capabilities, $idSites)
                 }
             }
         }
-
-        // we reload the access list which doesn't yet take in consideration 
this new user access
-        $this->reloadPermissions();
     }
 
     private function getRolesAndCapabilitiesForLogin($userLogin)
@@ -1290,24 +1307,26 @@ private function 
getRolesAndCapabilitiesForLogin($userLogin)
      */
     public function removeCapabilities($userLogin, $capabilities, $idSites)
     {
-        $idSites = $this->getIdSitesCheckAdminAccess($idSites);
+        $this->executeConcurrencySafe($userLogin, function () use ($userLogin, 
$capabilities, $idSites) {
+            $idSites = $this->getIdSitesCheckAdminAccess($idSites);
 
-        $this->checkUserExists($userLogin);
+            $this->checkUserExists($userLogin);
 
-        if (!is_array($capabilities)) {
-            $capabilities = [$capabilities];
-        }
+            if (!is_array($capabilities)) {
+                $capabilities = [$capabilities];
+            }
 
-        foreach ($capabilities as $capability) {
-            $this->capabilityProvider->checkValidCapability($capability);
-        }
+            foreach ($capabilities as $capability) {
+                $this->capabilityProvider->checkValidCapability($capability);
+            }
 
-        foreach ($capabilities as $capability) {
-            $this->model->removeUserAccess($userLogin, $capability, $idSites);
-        }
+            foreach ($capabilities as $capability) {
+                $this->model->removeUserAccess($userLogin, $capability, 
$idSites);
+            }
 
-        // we reload the access list which doesn't yet take in consideration 
this removed capability
-        $this->reloadPermissions();
+            // we reload the access list which doesn't yet take in 
consideration this removed capability
+            $this->reloadPermissions();
+        });
     }
 
     private function reloadPermissions()
@@ -1318,6 +1337,9 @@ private function reloadPermissions()
 
     private function getIdSitesCheckAdminAccess($idSites)
     {
+        // reload access to ensure we're not working with cached entries that 
might have been changed in between
+        Access::getInstance()->reloadAccess();
+
         if ($idSites === 'all') {
             // in case idSites is all we grant access to all the websites on 
which the current connected user has an 'admin' access
             $idSites = 
\Piwik\Plugins\SitesManager\API::getInstance()->getSitesIdWithAdminAccess();
@@ -1658,4 +1680,10 @@ public function generateInviteLink($userLogin, 
$expiryInDays = 7, $passwordConfi
                 'token'  => $token,
             ]);
     }
+
+    private function executeConcurrencySafe(string $userLogin, callable 
$callback = null)
+    {
+        $lock = new Lock(StaticContainer::get(LockBackend::class), 
'UsersManager.changePermissions');
+        $lock->execute($userLogin, $callback);
+    }
 }
diff --git a/www/vendor/composer/InstalledVersions.php 
b/www/vendor/composer/InstalledVersions.php
index 07b32ed..6d29bff 100644
--- a/www/vendor/composer/InstalledVersions.php
+++ b/www/vendor/composer/InstalledVersions.php
@@ -32,6 +32,11 @@ class InstalledVersions
      */
     private static $installed;
 
+    /**
+     * @var bool
+     */
+    private static $installedIsLocalDir;
+
     /**
      * @var bool|null
      */
@@ -309,6 +314,12 @@ public static function reload($data)
     {
         self::$installed = $data;
         self::$installedByVendor = array();
+
+        // when using reload, we disable the duplicate protection to ensure 
that self::$installed data is
+        // always returned, but we cannot know whether it comes from the 
installed.php in __DIR__ or not,
+        // so we have to assume it does not, and that may result in duplicate 
data being returned when listing
+        // all installed packages for example
+        self::$installedIsLocalDir = false;
     }
 
     /**
@@ -325,7 +336,9 @@ private static function getInstalled()
         $copiedLocalDir = false;
 
         if (self::$canGetVendors) {
+            $selfDir = strtr(__DIR__, '\\', '/');
             foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => 
$loader) {
+                $vendorDir = strtr($vendorDir, '\\', '/');
                 if (isset(self::$installedByVendor[$vendorDir])) {
                     $installed[] = self::$installedByVendor[$vendorDir];
                 } elseif (is_file($vendorDir.'/composer/installed.php')) {
@@ -333,11 +346,14 @@ private static function getInstalled()
                     $required = require $vendorDir.'/composer/installed.php';
                     self::$installedByVendor[$vendorDir] = $required;
                     $installed[] = $required;
-                    if (strtr($vendorDir.'/composer', '\\', '/') === 
strtr(__DIR__, '\\', '/')) {
+                    if (self::$installed === null && $vendorDir.'/composer' 
=== $selfDir) {
                         self::$installed = $required;
-                        $copiedLocalDir = true;
+                        self::$installedIsLocalDir = true;
                     }
                 }
+                if (self::$installedIsLocalDir && $vendorDir.'/composer' === 
$selfDir) {
+                    $copiedLocalDir = true;
+                }
             }
         }
 

Reply via email to