diff --git a/config/cms.php b/config/cms.php
index 4c838a026..790355414 100644
--- a/config/cms.php
+++ b/config/cms.php
@@ -298,6 +298,16 @@ return [
| in cloud storage (ex. AWS, RackSpace) are valid for by setting
| temporaryUrlTTL to a value in seconds to define a validity period. This
| is only used for the 'uploads' config when using a supported cloud disk
+ |
+ | NOTE: If you have installed October in a subfolder, are using local
+ | storage and are not using a linkPolicy of 'force' you should include
+ | the path to the subfolder in the `path` option for these storage
+ | configurations.
+ |
+ | Example: October is installed under https://localhost/projects/october.
+ | You should then specify `/projects/october/storage/app/uploads` as the
+ | path for the uploads disk and `/projects/october/storage/app/media` as
+ | the path for the media disk.
*/
'storage' => [
diff --git a/modules/backend/widgets/Filter.php b/modules/backend/widgets/Filter.php
index 5e97706ac..7b3a8b40e 100644
--- a/modules/backend/widgets/Filter.php
+++ b/modules/backend/widgets/Filter.php
@@ -208,7 +208,8 @@ class Filter extends WidgetBase
switch ($scope->type) {
case 'group':
- $active = $this->optionsFromAjax(post('options.active'));
+ $data = json_decode(post('options'), true);
+ $active = $this->optionsFromAjax($data ?: null);
$this->setScopeValue($scope, $active);
break;
@@ -223,7 +224,8 @@ class Filter extends WidgetBase
break;
case 'date':
- $dates = $this->datesFromAjax(post('options.dates'));
+ $data = json_decode(post('options'), true);
+ $dates = $this->datesFromAjax($data['dates'] ?? null);
if (!empty($dates)) {
list($date) = $dates;
@@ -236,7 +238,8 @@ class Filter extends WidgetBase
break;
case 'daterange':
- $dates = $this->datesFromAjax(post('options.dates'));
+ $data = json_decode(post('options'), true);
+ $dates = $this->datesFromAjax($data['dates'] ?? null);
if (!empty($dates)) {
list($after, $before) = $dates;
@@ -251,7 +254,8 @@ class Filter extends WidgetBase
break;
case 'number':
- $numbers = $this->numbersFromAjax(post('options.numbers'));
+ $data = json_decode(post('options'), true);
+ $numbers = $this->numbersFromAjax($data['numbers'] ?? null);
if (!empty($numbers)) {
list($number) = $numbers;
@@ -264,7 +268,8 @@ class Filter extends WidgetBase
break;
case 'numberrange':
- $numbers = $this->numbersFromAjax(post('options.numbers'));
+ $data = json_decode(post('options'), true);
+ $numbers = $this->numbersFromAjax($data['numbers'] ?? null);
if (!empty($numbers)) {
list($min, $max) = $numbers;
diff --git a/modules/backend/widgets/filter/partials/_scope_date.htm b/modules/backend/widgets/filter/partials/_scope_date.htm
index 64701fd13..b8a019178 100644
--- a/modules/backend/widgets/filter/partials/_scope_date.htm
+++ b/modules/backend/widgets/filter/partials/_scope_date.htm
@@ -10,7 +10,7 @@
'firstDay' => $scope->firstDay,
'yearRange' => $scope->yearRange,
])) ?>"
- ignoreTimezone ? 'data-ignore-timezone' : ''; ?>
+ = $scope->ignoreTimezone ? 'data-ignore-timezone' : ''; ?>
>
= e(trans($scope->label)) ?>:
= isset($dateStr) ? $dateStr : e(trans('backend::lang.filter.date_all')) ?>
diff --git a/modules/backend/widgets/filter/partials/_scope_daterange.htm b/modules/backend/widgets/filter/partials/_scope_daterange.htm
index 00cc7c77d..9c336385b 100644
--- a/modules/backend/widgets/filter/partials/_scope_daterange.htm
+++ b/modules/backend/widgets/filter/partials/_scope_daterange.htm
@@ -10,7 +10,7 @@
'firstDay' => $scope->firstDay,
'yearRange' => $scope->yearRange,
])) ?>"
- ignoreTimezone ? 'data-ignore-timezone' : ''; ?>
+ = $scope->ignoreTimezone ? 'data-ignore-timezone' : ''; ?>
>
= e(trans($scope->label)) ?>:
= isset($afterStr) && isset($beforeStr) ? ($afterStr . ' → ' . $beforeStr) : e(trans('backend::lang.filter.date_all')) ?>
diff --git a/modules/system/assets/js/lang/lang.ar.js b/modules/system/assets/js/lang/lang.ar.js
index bc6151182..cc1f2223e 100644
--- a/modules/system/assets/js/lang/lang.ar.js
+++ b/modules/system/assets/js/lang/lang.ar.js
@@ -9,6 +9,7 @@ $.oc.langMessages['ar'] = $.extend(
);
//! moment.js locale configuration v2.22.2
+//!!! IMPORTANT - modified from default - see https://github.com/octobercms/october/issues/5213
;(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined'
@@ -19,27 +20,16 @@ $.oc.langMessages['ar'] = $.extend(
var symbolMap = {
- '1': '١',
- '2': '٢',
- '3': '٣',
- '4': '٤',
- '5': '٥',
- '6': '٦',
- '7': '٧',
- '8': '٨',
- '9': '٩',
- '0': '٠'
- }, numberMap = {
- '١': '1',
- '٢': '2',
- '٣': '3',
- '٤': '4',
- '٥': '5',
- '٦': '6',
- '٧': '7',
- '٨': '8',
- '٩': '9',
- '٠': '0'
+ '1': '1',
+ '2': '2',
+ '3': '3',
+ '4': '4',
+ '5': '5',
+ '6': '6',
+ '7': '7',
+ '8': '8',
+ '9': '9',
+ '0': '0'
}, pluralForm = function (n) {
return n === 0 ? 0 : n === 1 ? 1 : n === 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5;
}, plurals = {
@@ -124,9 +114,7 @@ $.oc.langMessages['ar'] = $.extend(
yy : pluralize('y')
},
preparse: function (string) {
- return string.replace(/[١٢٣٤٥٦٧٨٩٠]/g, function (match) {
- return numberMap[match];
- }).replace(/،/g, ',');
+ return string.replace(/،/g, ',');
},
postformat: function (string) {
return string.replace(/\d/g, function (match) {
diff --git a/modules/system/assets/ui/js/filter.js b/modules/system/assets/ui/js/filter.js
index 708efa294..8c9563365 100644
--- a/modules/system/assets/ui/js/filter.js
+++ b/modules/system/assets/ui/js/filter.js
@@ -23,6 +23,7 @@
this.options = options || {}
this.scopeValues = {}
+ this.scopeAvailable = {}
this.$activeScope = null
this.activeScopeName = null
this.isActiveScopeDirty = false
@@ -286,23 +287,24 @@
var
itemId = $item.data('item-id'),
- items = this.scopeValues[this.activeScopeName],
- fromItems = isDeselect ? items.active : items.available,
- toItems = isDeselect ? items.available : items.active,
- testFunc = function(item){ return item.id == itemId },
+ active = this.scopeValues[this.activeScopeName],
+ available = this.scopeAvailable[this.activeScopeName],
+ fromItems = isDeselect ? active : available,
+ toItems = isDeselect ? available : active,
+ testFunc = function(active){ return active.id == itemId },
item = $.grep(fromItems, testFunc).pop(),
filtered = $.grep(fromItems, testFunc, true)
if (isDeselect)
- this.scopeValues[this.activeScopeName].active = filtered
+ this.scopeValues[this.activeScopeName] = filtered
else
- this.scopeValues[this.activeScopeName].available = filtered
+ this.scopeAvailable[this.activeScopeName] = filtered
if (item)
toItems.push(item)
- this.toggleFilterButtons(items)
- this.updateScopeSetting(this.$activeScope, items.active.length)
+ this.toggleFilterButtons(active)
+ this.updateScopeSetting(this.$activeScope, isDeselect ? filtered.length : active.length)
this.isActiveScopeDirty = true
this.focusSearch()
}
@@ -310,10 +312,17 @@
FilterWidget.prototype.displayPopover = function($scope) {
var self = this,
scopeName = $scope.data('scope-name'),
- data = this.scopeValues[scopeName],
+ data = null,
isLoaded = true,
container = false
+ if (typeof this.scopeAvailable[scopeName] !== "undefined" && this.scopeAvailable[scopeName]) {
+ data = $.extend({}, data, {
+ available: this.scopeAvailable[scopeName],
+ active: this.scopeValues[scopeName]
+ })
+ }
+
// If the filter is running in a modal, popovers should be
// attached to the modal container. This prevents z-index issues.
var modalParent = $scope.parents('.modal-dialog')
@@ -390,7 +399,8 @@
if (!data.active) data.active = []
if (!data.available) data.available = []
- this.scopeValues[scopeName] = data
+ this.scopeValues[scopeName] = data.active;
+ this.scopeAvailable[scopeName] = data.available;
// Do not render if scope has changed
if (scopeName != this.activeScopeName)
@@ -424,9 +434,9 @@
/*
* Ensure any active items do not appear in the search results
*/
- if (items.active.length) {
+ if (items.length) {
var activeIds = []
- $.each(items.active, function (key, obj) {
+ $.each(items, function (key, obj) {
activeIds.push(obj.id)
})
@@ -457,7 +467,7 @@
buttonContainer = $('#controlFilterPopover .filter-buttons')
if (data) {
- data.active.length > 0 ? buttonContainer.show() : buttonContainer.hide()
+ data.length > 0 ? buttonContainer.show() : buttonContainer.hide()
} else {
items.children().length > 0 ? buttonContainer.show() : buttonContainer.hide()
}
@@ -473,7 +483,7 @@
var self = this,
data = {
scopeName: scopeName,
- options: this.scopeValues[scopeName]
+ options: JSON.stringify(this.scopeValues[scopeName])
}
$.oc.stripeLoadIndicator.show()
@@ -541,6 +551,7 @@
if (isReset) {
this.scopeValues[scopeName] = null
+ this.scopeAvailable[scopeName] = null
this.updateScopeSetting(this.$activeScope, 0)
}
diff --git a/modules/system/assets/ui/storm-min.js b/modules/system/assets/ui/storm-min.js
index 8803506a3..632ec830d 100644
--- a/modules/system/assets/ui/storm-min.js
+++ b/modules/system/assets/ui/storm-min.js
@@ -3044,6 +3044,7 @@ $.fn.toolbar.noConflict=function(){$.fn.toolbar=old
return this}
$(document).on('render',function(){$('[data-control=toolbar]').toolbar()})}(window.jQuery);+function($){"use strict";var FilterWidget=function(element,options){this.$el=$(element);this.options=options||{}
this.scopeValues={}
+this.scopeAvailable={}
this.$activeScope=null
this.activeScopeName=null
this.isActiveScopeDirty=false
@@ -3148,18 +3149,19 @@ $item.addClass('animate-enter').prependTo($otherContainer).one('webkitAnimationE
if(!this.scopeValues[this.activeScopeName])
return
var
-itemId=$item.data('item-id'),items=this.scopeValues[this.activeScopeName],fromItems=isDeselect?items.active:items.available,toItems=isDeselect?items.available:items.active,testFunc=function(item){return item.id==itemId},item=$.grep(fromItems,testFunc).pop(),filtered=$.grep(fromItems,testFunc,true)
+itemId=$item.data('item-id'),active=this.scopeValues[this.activeScopeName],available=this.scopeAvailable[this.activeScopeName],fromItems=isDeselect?active:available,toItems=isDeselect?available:active,testFunc=function(active){return active.id==itemId},item=$.grep(fromItems,testFunc).pop(),filtered=$.grep(fromItems,testFunc,true)
if(isDeselect)
-this.scopeValues[this.activeScopeName].active=filtered
+this.scopeValues[this.activeScopeName]=filtered
else
-this.scopeValues[this.activeScopeName].available=filtered
+this.scopeAvailable[this.activeScopeName]=filtered
if(item)
toItems.push(item)
-this.toggleFilterButtons(items)
-this.updateScopeSetting(this.$activeScope,items.active.length)
+this.toggleFilterButtons(active)
+this.updateScopeSetting(this.$activeScope,isDeselect?filtered.length:active.length)
this.isActiveScopeDirty=true
this.focusSearch()}
-FilterWidget.prototype.displayPopover=function($scope){var self=this,scopeName=$scope.data('scope-name'),data=this.scopeValues[scopeName],isLoaded=true,container=false
+FilterWidget.prototype.displayPopover=function($scope){var self=this,scopeName=$scope.data('scope-name'),data=null,isLoaded=true,container=false
+if(typeof this.scopeAvailable[scopeName]!=="undefined"&&this.scopeAvailable[scopeName]){data=$.extend({},data,{available:this.scopeAvailable[scopeName],active:this.scopeValues[scopeName]})}
var modalParent=$scope.parents('.modal-dialog')
if(modalParent.length>0){container=modalParent[0]}
if(!data){data={loading:true}
@@ -3181,8 +3183,7 @@ FilterWidget.prototype.fillOptions=function(scopeName,data){if(this.scopeValues[
return
if(!data.active)data.active=[]
if(!data.available)data.available=[]
-this.scopeValues[scopeName]=data
-if(scopeName!=this.activeScopeName)
+this.scopeValues[scopeName]=data.active;this.scopeAvailable[scopeName]=data.available;if(scopeName!=this.activeScopeName)
return
var container=$('#controlFilterPopover .filter-items > ul').empty()
this.addItemsToListElement(container,data.available)
@@ -3194,8 +3195,8 @@ if(!this.scopeValues[this.activeScopeName])
return
var
self=this,filtered=[],items=this.scopeValues[scopeName]
-if(items.active.length){var activeIds=[]
-$.each(items.active,function(key,obj){activeIds.push(obj.id)})
+if(items.length){var activeIds=[]
+$.each(items,function(key,obj){activeIds.push(obj.id)})
filtered=$.grep(available,function(item){return $.inArray(item.id,activeIds)===-1})}
else{filtered=available}
var container=$('#controlFilterPopover .filter-items > ul').empty()
@@ -3204,10 +3205,10 @@ FilterWidget.prototype.addItemsToListElement=function($ul,items){$.each(items,fu
$ul.append(item)})}
FilterWidget.prototype.toggleFilterButtons=function(data)
{var items=$('#controlFilterPopover .filter-active-items > ul'),buttonContainer=$('#controlFilterPopover .filter-buttons')
-if(data){data.active.length>0?buttonContainer.show():buttonContainer.hide()}else{items.children().length>0?buttonContainer.show():buttonContainer.hide()}}
+if(data){data.length>0?buttonContainer.show():buttonContainer.hide()}else{items.children().length>0?buttonContainer.show():buttonContainer.hide()}}
FilterWidget.prototype.pushOptions=function(scopeName){if(!this.isActiveScopeDirty||!this.options.updateHandler)
return
-var self=this,data={scopeName:scopeName,options:this.scopeValues[scopeName]}
+var self=this,data={scopeName:scopeName,options:JSON.stringify(this.scopeValues[scopeName])}
$.oc.stripeLoadIndicator.show()
this.$el.request(this.options.updateHandler,{data:data}).always(function(){$.oc.stripeLoadIndicator.hide()}).done(function(){self.$el.find('[data-scope-name="'+scopeName+'"]').trigger('change.oc.filterScope')})}
FilterWidget.prototype.checkboxToggle=function($el){var isChecked=$el.is(':checked'),$scope=$el.closest('.filter-scope'),scopeName=$scope.data('scope-name')
@@ -3224,6 +3225,7 @@ this.$el.request(this.options.updateHandler,{data:data}).always(function(){$.oc.
$scope.toggleClass('active',!!switchValue)}
FilterWidget.prototype.filterScope=function(isReset){var scopeName=this.$activeScope.data('scope-name')
if(isReset){this.scopeValues[scopeName]=null
+this.scopeAvailable[scopeName]=null
this.updateScopeSetting(this.$activeScope,0)}
this.pushOptions(scopeName);this.isActiveScopeDirty=true;this.$activeScope.data('oc.popover').hide()}
FilterWidget.prototype.getLang=function(name,defaultValue){if($.oc===undefined||$.oc.lang===undefined){return defaultValue}
diff --git a/modules/system/assets/ui/vendor/moment/locale/ar.js b/modules/system/assets/ui/vendor/moment/locale/ar.js
index e0d3c98f1..fed1c9cdc 100644
--- a/modules/system/assets/ui/vendor/moment/locale/ar.js
+++ b/modules/system/assets/ui/vendor/moment/locale/ar.js
@@ -1,4 +1,5 @@
//! moment.js locale configuration v2.22.2
+//!!! IMPORTANT - modified from default - see https://github.com/octobercms/october/issues/5213
;(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined'
@@ -9,27 +10,16 @@
var symbolMap = {
- '1': '١',
- '2': '٢',
- '3': '٣',
- '4': '٤',
- '5': '٥',
- '6': '٦',
- '7': '٧',
- '8': '٨',
- '9': '٩',
- '0': '٠'
- }, numberMap = {
- '١': '1',
- '٢': '2',
- '٣': '3',
- '٤': '4',
- '٥': '5',
- '٦': '6',
- '٧': '7',
- '٨': '8',
- '٩': '9',
- '٠': '0'
+ '1': '1',
+ '2': '2',
+ '3': '3',
+ '4': '4',
+ '5': '5',
+ '6': '6',
+ '7': '7',
+ '8': '8',
+ '9': '9',
+ '0': '0'
}, pluralForm = function (n) {
return n === 0 ? 0 : n === 1 ? 1 : n === 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5;
}, plurals = {
@@ -114,9 +104,7 @@
yy : pluralize('y')
},
preparse: function (string) {
- return string.replace(/[١٢٣٤٥٦٧٨٩٠]/g, function (match) {
- return numberMap[match];
- }).replace(/،/g, ',');
+ return string.replace(/،/g, ',');
},
postformat: function (string) {
return string.replace(/\d/g, function (match) {