diff --git a/README.md b/README.md
index 545c45b8d..a5eab05a3 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,13 @@ Please follow the following guides and code standards:
The CMS uses [Laravel](http://laravel.com) as a foundation PHP framework.
+### Using LESS
+
+The theme is based on the Twitter Bootstrap framework and uses LESS language for generating the CSS. Please use a suitable LESS application for compiling the **assets/less/*.less** file to **assets/css/*.css**. The following applications are recommended:
+
+* [LESS.app](http://incident57.com/less/) for Mac
+* [WinLess](http://winless.org/) for Windows
+
### Contact
You can communicate with us using the following mediums:
diff --git a/modules/backend/ServiceProvider.php b/modules/backend/ServiceProvider.php
index 5bed5d86b..0ee23b327 100644
--- a/modules/backend/ServiceProvider.php
+++ b/modules/backend/ServiceProvider.php
@@ -121,8 +121,8 @@ class ServiceProvider extends ModuleServiceProvider
*/
BackendAuth::registerCallback(function($manager) {
$manager->registerPermissions('October.Backend', [
- 'backend.access_dashboard' => ['label' => 'View the dashboard', 'tab' => 'System'],
- 'backend.manage_users' => ['label' => 'Manage other administrators', 'tab' => 'System'],
+ 'backend.access_dashboard' => ['label' => 'system::lang.permissions.view_the_dashboard', 'tab' => 'System'],
+ 'backend.manage_users' => ['label' => 'system::lang.permissions.manage_other_administrators', 'tab' => 'System'],
]);
});
diff --git a/modules/backend/assets/css/october.css b/modules/backend/assets/css/october.css
index 3b7b92a5e..036561942 100644
--- a/modules/backend/assets/css/october.css
+++ b/modules/backend/assets/css/october.css
@@ -6613,13 +6613,20 @@ a .icon-flip-vertical:before {
}
[class^="flag-"],
[class*=" flag-"] {
- background: url(../images/flag-icons-small.png) no-repeat;
+ background-image: url("../images/flag-icons-small.png");
width: 16px;
height: 16px;
line-height: 16px;
vertical-align: middle;
display: inline-block;
- margin: -1px 2px 0 2px;
+ margin: -3px 2px 0 2px;
+}
+@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) {
+ [class^="flag-"],
+ [class*=" flag-"] {
+ background-image: url("../images/flag-icons-large.png");
+ background-size: 16px 3952px;
+ }
}
.flag-AfricanUnion {
background-position: 0 -16px;
@@ -7490,6 +7497,278 @@ a .icon-flip-vertical:before {
font-style: italic;
font-weight: 600;
}
+@-webkit-keyframes fadeIn {
+ 0% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
+}
+@keyframes fadeIn {
+ 0% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
+}
+.fadeIn {
+ -webkit-animation-name: fadeIn;
+ animation-name: fadeIn;
+}
+@-webkit-keyframes fadeInDown {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ }
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+@keyframes fadeInDown {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -100%, 0);
+ -ms-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ }
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ -ms-transform: none;
+ transform: none;
+ }
+}
+.fadeInDown {
+ -webkit-animation-name: fadeInDown;
+ animation-name: fadeInDown;
+}
+@-webkit-keyframes fadeInLeft {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ }
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+@keyframes fadeInLeft {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ -ms-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ }
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ -ms-transform: none;
+ transform: none;
+ }
+}
+.fadeInLeft {
+ -webkit-animation-name: fadeInLeft;
+ animation-name: fadeInLeft;
+}
+@-webkit-keyframes fadeInRight {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ }
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+@keyframes fadeInRight {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(100%, 0, 0);
+ -ms-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ }
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ -ms-transform: none;
+ transform: none;
+ }
+}
+.fadeInRight {
+ -webkit-animation-name: fadeInRight;
+ animation-name: fadeInRight;
+}
+@-webkit-keyframes fadeInUp {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ }
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+@keyframes fadeInUp {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 100%, 0);
+ -ms-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ }
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ -ms-transform: none;
+ transform: none;
+ }
+}
+.fadeInUp {
+ -webkit-animation-name: fadeInUp;
+ animation-name: fadeInUp;
+}
+@-webkit-keyframes fadeInUpBig {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 2000px, 0);
+ transform: translate3d(0, 2000px, 0);
+ }
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+@-webkit-keyframes fadeOut {
+ 0% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ }
+}
+@keyframes fadeOut {
+ 0% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ }
+}
+.fadeOut {
+ -webkit-animation-name: fadeOut;
+ animation-name: fadeOut;
+}
+@-webkit-keyframes fadeOutDown {
+ 0% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ }
+}
+@keyframes fadeOutDown {
+ 0% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 100%, 0);
+ -ms-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ }
+}
+.fadeOutDown {
+ -webkit-animation-name: fadeOutDown;
+ animation-name: fadeOutDown;
+}
+@-webkit-keyframes fadeOutLeft {
+ 0% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ }
+}
+@keyframes fadeOutLeft {
+ 0% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ -ms-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ }
+}
+.fadeOutLeft {
+ -webkit-animation-name: fadeOutLeft;
+ animation-name: fadeOutLeft;
+}
+@-webkit-keyframes fadeOutRight {
+ 0% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ }
+}
+@keyframes fadeOutRight {
+ 0% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(100%, 0, 0);
+ -ms-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ }
+}
+.fadeOutRight {
+ -webkit-animation-name: fadeOutRight;
+ animation-name: fadeOutRight;
+}
+@-webkit-keyframes fadeOutUp {
+ 0% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ }
+}
+@keyframes fadeOutUp {
+ 0% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -100%, 0);
+ -ms-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ }
+}
+.fadeOutUp {
+ -webkit-animation-name: fadeOutUp;
+ animation-name: fadeOutUp;
+}
body.drag * {
cursor: drag !important;
cursor: -webkit-grab !important;
@@ -9592,17 +9871,19 @@ table.table.data tr.list-tree-level-25 td.list-cell-index-1 {
border-bottom: 1px solid #949ea6;
}
.control-filter a {
- color: #949ea6;
text-decoration: none;
+ color: #949ea6;
}
-.control-filter > .filter-set {
+.control-filter > .filter-scope {
display: inline-block;
padding: 15px;
}
-.control-filter > .filter-set .filter-setting {
+.control-filter > .filter-scope .filter-setting {
display: inline-block;
+ -webkit-transition: color 0.6s;
+ transition: color 0.6s;
}
-.control-filter > .filter-set:after {
+.control-filter > .filter-scope:after {
font-size: 14px;
font-family: FontAwesome;
font-weight: normal;
@@ -9612,7 +9893,7 @@ table.table.data tr.list-tree-level-25 td.list-cell-index-1 {
*margin-right: .3em;
content: "\f107";
}
-.control-filter > .filter-set.active .filter-setting {
+.control-filter > .filter-scope.active .filter-setting {
padding-left: 5px;
padding-right: 5px;
color: #FFF;
@@ -9620,14 +9901,26 @@ table.table.data tr.list-tree-level-25 td.list-cell-index-1 {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
+ -webkit-transition: color 1s, background-color 1s;
+ transition: color 1s, background-color 1s;
}
-.control-filter > .filter-set:hover {
+.control-filter > .filter-scope.checkbox {
+ padding-left: 35px;
+}
+.control-filter > .filter-scope.checkbox,
+.control-filter > .filter-scope.checkbox label {
+ margin-bottom: 0;
+}
+.control-filter > .filter-scope.checkbox:after {
+ content: '';
+}
+.control-filter > .filter-scope:hover {
color: #000;
}
-.control-filter > .filter-set:hover .filter-label {
+.control-filter > .filter-scope:hover .filter-label {
color: #949ea6;
}
-.control-filter > .filter-set:hover.active .filter-setting {
+.control-filter > .filter-scope:hover.active .filter-setting {
background-color: #b32d00;
}
.control-filter-popover {
@@ -9640,10 +9933,15 @@ table.table.data tr.list-tree-level-25 td.list-cell-index-1 {
min-height: 36px;
border: none;
border-bottom: 1px solid #949ea6;
- background: transparent url(../images/bitmap-icons.png) no-repeat 100% -82px;
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- border-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+ background-color: transparent;
+}
+.control-filter-popover .filter-search .form-control.icon.search {
+ background-position: right -81px;
+}
+.control-filter-popover .filter-search .close {
+ display: none;
}
.control-filter-popover .filter-items,
.control-filter-popover .filter-active-items {
@@ -9658,6 +9956,11 @@ table.table.data tr.list-tree-level-25 td.list-cell-index-1 {
margin: 0;
padding: 0;
}
+.control-filter-popover .filter-items li,
+.control-filter-popover .filter-active-items li {
+ -webkit-transition: color 0.6s, background-color 0.3s;
+ transition: color 0.6s, background-color 0.3s;
+}
.control-filter-popover .filter-items a,
.control-filter-popover .filter-active-items a {
text-decoration: none;
@@ -9677,6 +9980,8 @@ table.table.data tr.list-tree-level-25 td.list-cell-index-1 {
color: #FFFFFF;
}
.control-filter-popover .filter-items {
+ height: 100px;
+ overflow: auto;
background-color: #fafafa;
border-bottom: 1px solid #949ea6;
}
@@ -9689,6 +9994,23 @@ table.table.data tr.list-tree-level-25 td.list-cell-index-1 {
*margin-right: .3em;
content: "\f067";
}
+.control-filter-popover .filter-items li.loading {
+ padding: 7px;
+}
+.control-filter-popover .filter-items li.loading > span {
+ display: block;
+ height: 20px;
+ width: 20px;
+ background-image: url(../images/loading-indicator.svg);
+ background-size: 20px 20px;
+ background-position: 50% 50%;
+ -webkit-animation: spin 1s linear infinite;
+ animation: spin 1s linear infinite;
+}
+.control-filter-popover .filter-items li.animate-enter {
+ -webkit-animation: fadeInUp 0.5s;
+ animation: fadeInUp 0.5s;
+}
.control-filter-popover .filter-active-items a:before {
font-family: FontAwesome;
font-weight: normal;
@@ -9698,6 +10020,31 @@ table.table.data tr.list-tree-level-25 td.list-cell-index-1 {
*margin-right: .3em;
content: "\f00d";
}
+.control-filter-popover .filter-active-items li.animate-enter {
+ -webkit-animation: fadeInDown 0.5s;
+ animation: fadeInDown 0.5s;
+}
+@media (max-width: 480px) {
+ .control-filter-popover .filter-items {
+ height: 200px;
+ }
+ .control-filter-popover .filter-search input {
+ padding-left: 36px;
+ padding-right: 36px;
+ }
+ .control-filter-popover .filter-search .form-control.icon.search {
+ background-position: 0 -81px;
+ }
+ .control-filter-popover .filter-search .close {
+ width: 30px;
+ display: block;
+ position: absolute;
+ top: 5px;
+ right: 5px;
+ font-size: 28px;
+ z-index: 2;
+ }
+}
.modal-content {
-webkit-box-shadow: none;
box-shadow: none;
diff --git a/modules/backend/assets/js/october.dragscroll.js b/modules/backend/assets/js/october.dragscroll.js
index 38af4b4a2..c7436e705 100644
--- a/modules/backend/assets/js/october.dragscroll.js
+++ b/modules/backend/assets/js/october.dragscroll.js
@@ -84,6 +84,7 @@
return false
})
+ $(document).on('ready', $.proxy(this.fixScrollClasses, this))
$(window).on('resize', $.proxy(this.fixScrollClasses, this))
/*
@@ -354,4 +355,4 @@
$.fn.dragScroll = old
return this
}
-}(window.jQuery);
\ No newline at end of file
+}(window.jQuery);
diff --git a/modules/backend/assets/js/october.inspector.js b/modules/backend/assets/js/october.inspector.js
index 18b3fabff..84d186dac 100644
--- a/modules/backend/assets/js/october.inspector.js
+++ b/modules/backend/assets/js/october.inspector.js
@@ -85,7 +85,7 @@
'an hidden input element with the data-inspector-values property.')
}
- Inspector.prototype.getPopoverTemplate = function() {
+ Inspector.prototype.getPopoverTemplate = function() {
return ' \
\
{{title}} \
@@ -182,14 +182,14 @@
this.$el.data('oc.inspectorVisible', true)
var displayPopover = function() {
- var offset = self.$el.data('inspector-offset')
+ var offset = self.$el.data('inspector-offset')
if (offset === undefined)
offset = 15
-
- var offsetX = self.$el.data('inspector-offset-x'),
- offsetY = self.$el.data('inspector-offset-y')
- var placement = self.$el.data('inspector-placement')
+ var offsetX = self.$el.data('inspector-offset-x'),
+ offsetY = self.$el.data('inspector-offset-y')
+
+ var placement = self.$el.data('inspector-placement')
if (placement === undefined)
placement = 'bottom'
@@ -248,8 +248,9 @@
displayPopover()
}
- // Creates group nodes in the property set
- //
+ /*
+ * Creates group nodes in the property set
+ */
Inspector.prototype.preprocessConfig = function() {
var fields = [],
result = {
@@ -707,15 +708,15 @@
InspectorEditorDropdown.prototype.showLoadingIndicator = function() {
if (!Modernizr.touch)
- this.indicatorContainer.loadIndicator({'opaque': true})
+ this.indicatorContainer.loadIndicator({'opaque': true})
}
InspectorEditorDropdown.prototype.hideLoadingIndicator = function() {
if (!Modernizr.touch)
- this.indicatorContainer.loadIndicator('hide')
+ this.indicatorContainer.loadIndicator('hide')
}
- InspectorEditorDropdown.prototype.loadOptions= function() {
+ InspectorEditorDropdown.prototype.loadOptions = function() {
var $form = $(this.selector).closest('form'),
data = this.inspector.propertyValues,
$select = $(this.selector),
@@ -764,7 +765,7 @@
// INSPECTOR DATA-API
// ==================
-
+
$(document).on('click', '[data-inspectable]', function(){
var $this = $(this),
inspector = $this.data('oc.inspector')
diff --git a/modules/backend/assets/js/october.popover.js b/modules/backend/assets/js/october.popover.js
index d9e39bd5c..e724cb63a 100644
--- a/modules/backend/assets/js/october.popover.js
+++ b/modules/backend/assets/js/october.popover.js
@@ -31,9 +31,10 @@
* Events:
* - showing.oc.popover - triggered before the popover is displayed. Allows to override the
* popover options (for example the content) or cancel the action with e.preventDefault()
+ * - show.oc.popover - triggered after the popover is displayed.
* - hiding.oc.popover - triggered before the popover is closed. Allows to cancel the action with
* e.preventDefault()
- * - hide.oc.popover - triggered after the popover is hidden.
+ * - hide.oc.popover - triggered after the popover is hidden.
*
* JavaScript API:
* $('#element').ocPopover({
@@ -55,14 +56,14 @@
Popover.prototype.hide = function() {
var e = $.Event('hiding.oc.popover', {relatedTarget: this.$el})
this.$el.trigger(e, this)
- if (e.isDefaultPrevented())
+ if (e.isDefaultPrevented())
return
if (this.$container) this.$container.remove()
if (this.$overlay) this.$overlay.remove()
- this.$overlay = false;
- this.$container = false;
+ this.$overlay = false
+ this.$container = false
this.$el.removeClass('popover-highlight')
this.$el.data('oc.popover', null)
@@ -70,46 +71,46 @@
$(document).unbind('mousedown', this.docClickHandler);
this.$el.trigger('hide.oc.popover')
- $(document).off('.oc.popover');
+ $(document).off('.oc.popover')
}
Popover.prototype.show = function(options) {
- var self = this;
+ var self = this
/*
* Trigger the show event
*/
var e = $.Event('showing.oc.popover', {relatedTarget: this.$el})
this.$el.trigger(e, this)
- if (e.isDefaultPrevented())
+ if (e.isDefaultPrevented())
return
/*
* Create the popover container and overlay
*/
-
- this.$container = $('
')
+ this.$container = $('
')
.addClass('control-popover')
.css('visibility', 'hidden')
if (this.options.containerClass)
this.$container.addClass(this.options.containerClass)
- var $content = $('
').html(this.getContent())
+ var $content = $('
').html(this.getContent())
this.$container.append($content)
if (this.options.width)
this.$container.width(this.options.width)
if (this.options.modal) {
- this.$overlay = $('
').addClass('popover-overlay')
+ this.$overlay = $('
').addClass('popover-overlay')
$(document.body).append(this.$overlay)
if (this.options.highlightModalTarget) {
this.$el.addClass('popover-highlight')
this.$el.blur()
}
- } else
+ } else {
this.$overlay = false
+ }
if (this.options.container)
$(this.options.container).append(this.$container);
@@ -119,8 +120,7 @@
/*
* Determine the popover position
*/
-
- var
+ var
placement = this.calcPlacement(),
position = this.calcPosition(placement);
@@ -132,14 +132,13 @@
/*
* Display the popover
*/
-
this.$container.css('visibility', 'visible')
$(document.body).addClass('popover-open')
+ this.$el.trigger('show.oc.popover')
/*
* Bind events
*/
-
this.$container.on('mousedown', function(e){
e.stopPropagation();
})
@@ -173,12 +172,12 @@
}
Popover.prototype.calcDimensions = function() {
- var
+ var
documentWidth = $(document).width(),
documentHeight = $(document).height(),
targetOffset = this.$el.offset(),
targetWidth = this.$el.outerWidth(),
- targetHeight = this.$el.outerHeight();
+ targetHeight = this.$el.outerHeight()
return {
containerWidth: this.$container.outerWidth() + this.arrowSize,
@@ -242,24 +241,24 @@
}
Popover.prototype.calcPosition = function(placement) {
- var
+ var
dimensions = this.calcDimensions(),
- result;
+ result
switch (placement) {
- case 'left' :
+ case 'left':
var realOffset = this.options.offsetY === undefined ? this.options.offset : this.options.offsetY
result = {x: (dimensions.targetOffset.left - dimensions.containerWidth), y: dimensions.targetOffset.top + realOffset}
break;
- case 'top' :
+ case 'top':
var realOffset = this.options.offsetX === undefined ? this.options.offset : this.options.offsetX
result = {x: dimensions.targetOffset.left + realOffset, y: (dimensions.targetOffset.top - dimensions.containerHeight)}
break;
- case 'bottom' :
+ case 'bottom':
var realOffset = this.options.offsetX === undefined ? this.options.offset : this.options.offsetX
result = {x: dimensions.targetOffset.left + realOffset, y: (dimensions.targetOffset.top + dimensions.targetHeight + this.arrowSize)}
break;
- case 'right' :
+ case 'right':
var realOffset = this.options.offsetY === undefined ? this.options.offset : this.options.offsetY
result = {x: (dimensions.targetOffset.left + dimensions.targetWidth + this.arrowSize), y: dimensions.targetOffset.top + realOffset}
break;
@@ -268,14 +267,14 @@
if (!this.options.container)
return result
- var
+ var
$container = $(this.options.container),
- containerOffset = $container.offset();
+ containerOffset = $container.offset()
- result.x -= containerOffset.left;
- result.y -= containerOffset.top;
+ result.x -= containerOffset.left
+ result.y -= containerOffset.top
- return result;
+ return result
}
Popover.prototype.onDocumentClick = function() {
diff --git a/modules/backend/assets/less/controls/filters.less b/modules/backend/assets/less/controls/filters.less
index b9efd119f..db5a5934e 100644
--- a/modules/backend/assets/less/controls/filters.less
+++ b/modules/backend/assets/less/controls/filters.less
@@ -10,16 +10,17 @@
border-top: 1px solid @color-filter-border;
border-bottom: 1px solid @color-filter-border;
a {
+ text-decoration: none;
color: @color-filter-text;
- text-decoration: none;
}
- >.filter-set {
+ > .filter-scope {
display: inline-block;
padding: 15px;
.filter-label {}
.filter-setting {
display: inline-block;
+ .transition(color 0.6s);
}
&:after {
@@ -34,6 +35,18 @@
color: #FFF;
background-color: @color-filter-bg-active;
.border-radius(4px);
+ .transition(~'color 1s, background-color 1s');
+ }
+ }
+
+ &.checkbox {
+ padding-left: 35px;
+ &, label {
+ margin-bottom: 0;
+ }
+
+ &:after {
+ content: '';
}
}
@@ -52,22 +65,37 @@
min-height: 36px;
input {
min-height: 36px;
- border: none;
+ border: none;
border-bottom: 1px solid @color-filter-border;
- background: transparent url(../images/bitmap-icons.png) no-repeat 100% -82px;
- .border-radius(0);
+ .border-bottom-radius(0);
+ background-color: transparent;
+ }
+
+ .form-control.icon.search {
+ background-position: right -81px;
+ }
+
+ .close {
+ display: none;
}
}
.filter-items, .filter-active-items {
color: @color-filter-text;
font-size: 13px;
- ul, li { list-style-type: none; margin:0; padding:0; }
+ ul, li {
+ list-style-type: none;
+ margin:0;
+ padding:0;
+ }
+ li {
+ .transition(~'color 0.6s, background-color 0.3s');
+ }
a {
text-decoration: none;
color: @color-filter-text;
display: block;
- padding: 7px 15px;
+ padding: 7px 15px;
&:before {
margin-right: 8px;
@@ -83,12 +111,57 @@
}
.filter-items {
+ height: 100px;
+ overflow: auto;
+
background-color: @color-filter-items-bg;
border-bottom: 1px solid @color-filter-border;
a:before { .icon(@plus); }
+
+ li.loading {
+ padding: 7px;
+ > span {
+ display: block;
+ height: 20px;
+ width: 20px;
+ background-image: url(../images/loading-indicator.svg);
+ background-size: 20px 20px;
+ background-position: 50% 50%;
+ .animation(spin 1s linear infinite);
+ }
+ }
+
+ li.animate-enter { .animation(fadeInUp .5s); }
}
.filter-active-items {
a:before { .icon(@times); }
+ li.animate-enter { .animation(fadeInDown .5s); }
+ }
+}
+
+@media (max-width: @screen-xs) {
+ .control-filter-popover {
+ .filter-items {
+ height: 200px;
+ }
+ .filter-search {
+ input {
+ padding-left: 36px;
+ padding-right: 36px;
+ }
+ .form-control.icon.search {
+ background-position: 0 -81px;
+ }
+ .close {
+ width: 30px;
+ display: block;
+ position: absolute;
+ top: 5px;
+ right: 5px;
+ font-size: 28px;
+ z-index: 2;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/modules/backend/assets/less/core/animations.less b/modules/backend/assets/less/core/animations.less
new file mode 100644
index 000000000..c0f7fe0bb
--- /dev/null
+++ b/modules/backend/assets/less/core/animations.less
@@ -0,0 +1,363 @@
+//
+// Fade In
+//
+
+@-webkit-keyframes fadeIn {
+ 0% {
+ opacity: 0;
+ }
+
+ 100% {
+ opacity: 1;
+ }
+}
+
+@keyframes fadeIn {
+ 0% {
+ opacity: 0;
+ }
+
+ 100% {
+ opacity: 1;
+ }
+}
+
+.fadeIn {
+ -webkit-animation-name: fadeIn;
+ animation-name: fadeIn;
+}
+
+//
+// Fade In Down
+//
+
+@-webkit-keyframes fadeInDown {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@keyframes fadeInDown {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -100%, 0);
+ -ms-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ -ms-transform: none;
+ transform: none;
+ }
+}
+
+.fadeInDown {
+ -webkit-animation-name: fadeInDown;
+ animation-name: fadeInDown;
+}
+
+//
+// Fade In Left
+//
+
+@-webkit-keyframes fadeInLeft {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@keyframes fadeInLeft {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ -ms-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ -ms-transform: none;
+ transform: none;
+ }
+}
+
+.fadeInLeft {
+ -webkit-animation-name: fadeInLeft;
+ animation-name: fadeInLeft;
+}
+
+//
+// Fade In Right
+//
+
+@-webkit-keyframes fadeInRight {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@keyframes fadeInRight {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(100%, 0, 0);
+ -ms-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ -ms-transform: none;
+ transform: none;
+ }
+}
+
+.fadeInRight {
+ -webkit-animation-name: fadeInRight;
+ animation-name: fadeInRight;
+}
+
+//
+// Fade In Up
+//
+
+@-webkit-keyframes fadeInUp {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@keyframes fadeInUp {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 100%, 0);
+ -ms-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ -ms-transform: none;
+ transform: none;
+ }
+}
+
+.fadeInUp {
+ -webkit-animation-name: fadeInUp;
+ animation-name: fadeInUp;
+}
+
+@-webkit-keyframes fadeInUpBig {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 2000px, 0);
+ transform: translate3d(0, 2000px, 0);
+ }
+
+ 100% {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+//
+// Fade Out
+//
+
+@-webkit-keyframes fadeOut {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
+
+@keyframes fadeOut {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
+
+.fadeOut {
+ -webkit-animation-name: fadeOut;
+ animation-name: fadeOut;
+}
+
+//
+// Fade Out Down
+//
+
+@-webkit-keyframes fadeOutDown {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ }
+}
+
+@keyframes fadeOutDown {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 100%, 0);
+ -ms-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ }
+}
+
+.fadeOutDown {
+ -webkit-animation-name: fadeOutDown;
+ animation-name: fadeOutDown;
+}
+
+//
+// Fade Out Left
+//
+
+@-webkit-keyframes fadeOutLeft {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ }
+}
+
+@keyframes fadeOutLeft {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ -ms-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ }
+}
+
+.fadeOutLeft {
+ -webkit-animation-name: fadeOutLeft;
+ animation-name: fadeOutLeft;
+}
+
+//
+// Fade Out Right
+//
+
+@-webkit-keyframes fadeOutRight {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ }
+}
+
+@keyframes fadeOutRight {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(100%, 0, 0);
+ -ms-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ }
+}
+
+.fadeOutRight {
+ -webkit-animation-name: fadeOutRight;
+ animation-name: fadeOutRight;
+}
+
+//
+// Fade Out Up
+//
+
+@-webkit-keyframes fadeOutUp {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ }
+}
+
+@keyframes fadeOutUp {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -100%, 0);
+ -ms-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ }
+}
+
+.fadeOutUp {
+ -webkit-animation-name: fadeOutUp;
+ animation-name: fadeOutUp;
+}
diff --git a/modules/backend/assets/less/core/flags.less b/modules/backend/assets/less/core/flags.less
index be84734bc..19c44a983 100644
--- a/modules/backend/assets/less/core/flags.less
+++ b/modules/backend/assets/less/core/flags.less
@@ -1,13 +1,13 @@
[class^="flag-"],
[class*=" flag-"] {
- background:url(../images/flag-icons-small.png) no-repeat;
+ .img-retina('../images/flag-icons-small.png', '../images/flag-icons-large.png', 16px, 3952px);
width: 16px;
height: 16px;
line-height: 16px;
vertical-align: middle;
display: inline-block;
- margin: -1px 2px 0 2px;
+ margin: -3px 2px 0 2px;
}
.flag-AfricanUnion { background-position:0 -16px }
diff --git a/modules/backend/assets/less/october.less b/modules/backend/assets/less/october.less
index bbb7d0bfe..a9b16c33f 100644
--- a/modules/backend/assets/less/october.less
+++ b/modules/backend/assets/less/october.less
@@ -4,6 +4,7 @@
@import "core/icons.less";
@import "core/flags.less";
@import "core/fonts.less";
+@import "core/animations.less";
// Boot variables and mixins
@import "core/boot.less";
diff --git a/modules/backend/behaviors/ListController.php b/modules/backend/behaviors/ListController.php
index 444a0f2fe..5509127f5 100644
--- a/modules/backend/behaviors/ListController.php
+++ b/modules/backend/behaviors/ListController.php
@@ -36,6 +36,11 @@ class ListController extends ControllerBehavior
*/
protected $toolbarWidgets = [];
+ /**
+ * @var WidgetBase Reference to the filter widget objects.
+ */
+ protected $filterWidgets = [];
+
/**
* {@inheritDoc}
*/
@@ -174,6 +179,31 @@ class ListController extends ControllerBehavior
$this->toolbarWidgets[$definition] = $toolbarWidget;
}
+ /*
+ * Prepare the filter widget (optional)
+ */
+ if (isset($listConfig->filter)) {
+ $widget->cssClasses[] = 'list-flush';
+
+ $filterConfig = $this->makeConfig($listConfig->filter);
+ $filterConfig->alias = $widget->alias . 'Filter';
+ $filterWidget = $this->makeWidget('Backend\Widgets\Filter', $filterConfig);
+ $filterWidget->bindToController();
+
+ /*
+ * Filter the list when the scopes are changed
+ */
+ $filterWidget->bindEvent('filter.update', function() use ($widget, $filterWidget){
+ $widget->addFilter([$filterWidget, 'applyAllScopesToQuery']);
+ return $widget->onRefresh();
+ });
+
+ // Apply predefined filter values
+ $widget->addFilter([$filterWidget, 'applyAllScopesToQuery']);
+
+ $this->filterWidgets[$definition] = $filterWidget;
+ }
+
return $widget;
}
@@ -206,6 +236,9 @@ class ListController extends ControllerBehavior
if (isset($this->toolbarWidgets[$definition]))
$collection[] = $this->toolbarWidgets[$definition]->render();
+ if (isset($this->filterWidgets[$definition]))
+ $collection[] = $this->filterWidgets[$definition]->render();
+
$collection[] = $this->listWidgets[$definition]->render();
return implode(PHP_EOL, $collection);
diff --git a/modules/backend/behaviors/relationcontroller/partials/_button_add.htm b/modules/backend/behaviors/relationcontroller/partials/_button_add.htm
index 9247d6539..65f0640c3 100644
--- a/modules/backend/behaviors/relationcontroller/partials/_button_add.htm
+++ b/modules/backend/behaviors/relationcontroller/partials/_button_add.htm
@@ -3,5 +3,5 @@
data-handler="onRelationManageForm"
href="javascript:;"
class="btn btn-sm btn-primary oc-icon-plus">
- = trans('backend::lang.relation.add_name', ['name'=>$relationLabel]) ?>
+ = e(trans('backend::lang.relation.add_name', ['name'=>trans($relationLabel)])) ?>
diff --git a/modules/backend/behaviors/relationcontroller/partials/_button_create.htm b/modules/backend/behaviors/relationcontroller/partials/_button_create.htm
index f7a455559..77ada5bbe 100644
--- a/modules/backend/behaviors/relationcontroller/partials/_button_create.htm
+++ b/modules/backend/behaviors/relationcontroller/partials/_button_create.htm
@@ -3,5 +3,5 @@
data-handler="onRelationManageForm"
href="javascript:;"
class="btn btn-sm btn-primary oc-icon-plus">
- = trans('backend::lang.relation.create_name', ['name'=>$relationLabel]) ?>
+ = e(trans('backend::lang.relation.create_name', ['name'=>trans($relationLabel)])) ?>
diff --git a/modules/backend/behaviors/relationcontroller/partials/_button_delete.htm b/modules/backend/behaviors/relationcontroller/partials/_button_delete.htm
index 274a686b0..108ff4af2 100644
--- a/modules/backend/behaviors/relationcontroller/partials/_button_delete.htm
+++ b/modules/backend/behaviors/relationcontroller/partials/_button_delete.htm
@@ -5,10 +5,10 @@
})"
disabled="disabled"
data-request="onRelationManageDelete"
- data-request-confirm="Are you sure?"
+ data-request-confirm="= e(trans('backend::lang.relation.delete_confirm')) ?>"
data-trigger-type="enable"
data-trigger="#= $this->relationGetId('view') ?> .control-list input[type=checkbox]"
data-trigger-condition="checked"
data-stripe-load-indicator>
- = trans('backend::lang.relation.delete') ?>
+ = e(trans('backend::lang.relation.delete')) ?>
\ No newline at end of file
diff --git a/modules/backend/behaviors/relationcontroller/partials/_button_remove.htm b/modules/backend/behaviors/relationcontroller/partials/_button_remove.htm
index 6e680af62..2ddd2770f 100644
--- a/modules/backend/behaviors/relationcontroller/partials/_button_remove.htm
+++ b/modules/backend/behaviors/relationcontroller/partials/_button_remove.htm
@@ -9,5 +9,5 @@
data-trigger="#= $this->relationGetId('view') ?> .control-list input[type=checkbox]"
data-trigger-condition="checked"
data-stripe-load-indicator>
- = trans('backend::lang.relation.remove') ?>
+ = e(trans('backend::lang.relation.remove')) ?>
\ No newline at end of file
diff --git a/modules/backend/behaviors/relationcontroller/partials/_manage_form.htm b/modules/backend/behaviors/relationcontroller/partials/_manage_form.htm
index 5fe598275..a29cc76fa 100644
--- a/modules/backend/behaviors/relationcontroller/partials/_manage_form.htm
+++ b/modules/backend/behaviors/relationcontroller/partials/_manage_form.htm
@@ -7,7 +7,7 @@
@@ -20,13 +20,13 @@
type="submit"
class="btn btn-primary"
data-dismiss="popup">
- = trans('backend::lang.relation.update') ?>
+ = e(trans('backend::lang.relation.update')) ?>
- Cancel
+ = e(trans('backend::lang.relation.cancel')) ?>
= Form::close() ?>
@@ -40,7 +40,7 @@
@@ -53,13 +53,13 @@
type="submit"
class="btn btn-primary"
data-dismiss="popup">
- = trans('backend::lang.relation.create') ?>
+ = e(trans('backend::lang.relation.create')) ?>
- Cancel
+ = e(trans('backend::lang.relation.cancel')) ?>
= Form::close() ?>
diff --git a/modules/backend/behaviors/relationcontroller/partials/_manage_list.htm b/modules/backend/behaviors/relationcontroller/partials/_manage_list.htm
index ba14e5ac4..2faa1e019 100644
--- a/modules/backend/behaviors/relationcontroller/partials/_manage_list.htm
+++ b/modules/backend/behaviors/relationcontroller/partials/_manage_list.htm
@@ -2,7 +2,7 @@
= $relationManageWidget->render() ?>
@@ -14,13 +14,13 @@
data-request="onRelationManageAdd"
data-dismiss="popup"
data-stripe-load-indicator>
- Add selected
+ = e(trans('backend::lang.relation.add_selected')) ?>
- Cancel
+ = e(trans('backend::lang.relation.cancel')) ?>
diff --git a/modules/backend/behaviors/relationcontroller/partials/_manage_pivot.htm b/modules/backend/behaviors/relationcontroller/partials/_manage_pivot.htm
index 1222b49ec..db8b55db8 100644
--- a/modules/backend/behaviors/relationcontroller/partials/_manage_pivot.htm
+++ b/modules/backend/behaviors/relationcontroller/partials/_manage_pivot.htm
@@ -2,10 +2,10 @@
= Form::open() ?>
-
Click on an item to add
+
= e(trans('backend::lang.relation.help')) ?>
= $relationManageWidget->render() ?>
@@ -15,7 +15,7 @@
type="button"
class="btn btn-default"
data-dismiss="popup">
- Cancel
+ = e(trans('backend::lang.relation.cancel')) ?>
= Form::close() ?>
diff --git a/modules/backend/behaviors/relationcontroller/partials/_pivot_form.htm b/modules/backend/behaviors/relationcontroller/partials/_pivot_form.htm
index 0dcfa363e..972b25041 100644
--- a/modules/backend/behaviors/relationcontroller/partials/_pivot_form.htm
+++ b/modules/backend/behaviors/relationcontroller/partials/_pivot_form.htm
@@ -7,7 +7,7 @@
= $relationPivotWidget->render() ?>
@@ -17,13 +17,13 @@
type="submit"
class="btn btn-primary"
data-dismiss="popup">
- Update
+ = e(trans('backend::lang.relation.update')) ?>
- Cancel
+ = e(trans('backend::lang.relation.cancel')) ?>
@@ -38,7 +38,7 @@
= $relationPivotWidget->render() ?>
@@ -48,13 +48,13 @@
type="submit"
class="btn btn-primary"
data-dismiss="popup">
- Add
+ = e(trans('backend::lang.relation.add')) ?>
- Cancel
+ = e(trans('backend::lang.relation.cancel')) ?>
diff --git a/modules/backend/classes/BackendController.php b/modules/backend/classes/BackendController.php
index 41537477c..03942a713 100644
--- a/modules/backend/classes/BackendController.php
+++ b/modules/backend/classes/BackendController.php
@@ -43,7 +43,7 @@ class BackendController extends ControllerBase
*/
$module = isset($params[0]) ? $params[0] : 'backend';
$controller = isset($params[1]) ? $params[1] : 'index';
- self::$action = $action = isset($params[2]) ? camel_case($params[2]) : 'index';
+ self::$action = $action = isset($params[2]) ? $this->parseAction($params[2]) : 'index';
self::$params = $controllerParams = array_slice($params, 3);
$controllerClass = '\\'.$module.'\Controllers\\'.$controller;
if ($controllerObj = $this->findController($controllerClass, $action, '/modules'))
@@ -55,7 +55,7 @@ class BackendController extends ControllerBase
if (count($params) >= 2) {
list($author, $plugin) = $params;
$controller = isset($params[2]) ? $params[2] : 'index';
- self::$action = $action = isset($params[3]) ? camel_case($params[3]) : 'index';
+ self::$action = $action = isset($params[3]) ? $this->parseAction($params[3]) : 'index';
self::$params = $controllerParams = array_slice($params, 4);
$controllerClass = '\\'.$author.'\\'.$plugin.'\Controllers\\'.$controller;
if ($controllerObj = $this->findController($controllerClass, $action, Config::get('cms.pluginsDir', '/plugins')))
@@ -97,4 +97,17 @@ class BackendController extends ControllerBase
return false;
}
+
+ /**
+ * Process the action name, since dashes are not supported in PHP methods.
+ * @param string $actionName
+ * @return string
+ */
+ protected function parseAction($actionName)
+ {
+ if (strpos($actionName, '-') !== false)
+ return camel_case($actionName);
+
+ return $actionName;
+ }
}
\ No newline at end of file
diff --git a/modules/backend/classes/Controller.php b/modules/backend/classes/Controller.php
index 03b59e408..bc64d5868 100644
--- a/modules/backend/classes/Controller.php
+++ b/modules/backend/classes/Controller.php
@@ -325,7 +325,7 @@ class Controller extends Extendable
* Execute the handler
*/
if (!$result = $this->runAjaxHandler($handler))
- throw new SystemException(Lang::get('cms::lang.ajax_handler.not_found', ['name'=>$handler]));
+ throw new ApplicationException(Lang::get('cms::lang.ajax_handler.not_found', ['name'=>$handler]));
/*
* If the handler returned an array, we should add it to output for rendering.
diff --git a/modules/backend/classes/FilterScope.php b/modules/backend/classes/FilterScope.php
new file mode 100644
index 000000000..f836e2ff1
--- /dev/null
+++ b/modules/backend/classes/FilterScope.php
@@ -0,0 +1,147 @@
+scopeName = $scopeName;
+ $this->label = $label;
+ }
+
+ /**
+ * Specifies a scope control rendering mode. Supported modes are:
+ * - group - filter by a group of IDs. Default.
+ * - checkbox - filter by a simple toggle switch.
+ * @param string $type Specifies a render mode as described above
+ * @param array $config A list of render mode specific config.
+ */
+ public function displayAs($type, $config = [])
+ {
+ $this->type = strtolower($type) ?: $this->type;
+ $this->config = $this->evalConfig($config);
+ return $this;
+ }
+
+ /**
+ * Process options and apply them to this object.
+ * @param array $config
+ * @return array
+ */
+ protected function evalConfig($config)
+ {
+ if (isset($config['options'])) $this->options($config['options']);
+ if (isset($config['context'])) $this->context = $config['context'];
+ if (isset($config['default'])) $this->defaults = $config['default'];
+ if (isset($config['conditions'])) $this->conditions = $config['conditions'];
+ if (isset($config['scope'])) $this->scope = $config['scope'];
+ if (isset($config['cssClass'])) $this->cssClass = $config['cssClass'];
+ if (isset($config['nameColumn'])) $this->nameColumn = $config['nameColumn'];
+ if (isset($config['descriptionColumn'])) $this->descriptionColumn = $config['descriptionColumn'];
+
+ if (array_key_exists('disabled', $config)) $this->disabled = $config['disabled'];
+ return $config;
+ }
+
+ /**
+ * Returns a value suitable for the scope id property.
+ */
+ public function getId($suffix = null)
+ {
+ $id = 'scope';
+ $id .= '-'.$this->scopeName;
+
+ if ($suffix)
+ $id .= '-'.$suffix;
+
+ if ($this->idPrefix)
+ $id = $this->idPrefix . '-' . $id;
+
+ $id = rtrim(str_replace(['[', ']'], '-', $id), '-');
+ return $id;
+ }
+
+}
\ No newline at end of file
diff --git a/modules/backend/controllers/Users.php b/modules/backend/controllers/Users.php
index 36a067a53..ac6b25f41 100644
--- a/modules/backend/controllers/Users.php
+++ b/modules/backend/controllers/Users.php
@@ -98,9 +98,9 @@ class Users extends Controller
'comment' => $permission->comment,
'type' => 'balloon-selector',
'options' => [
- 1 => 'Allow',
- 0 => 'Inherit',
- -1 => 'Deny',
+ 1 => 'backend::lang.user.allow',
+ 0 => 'backend::lang.user.inherit',
+ -1 => 'backend::lang.user.deny',
],
'attributes' => [
'data-trigger' => "input[name='User[permissions][superuser]']",
diff --git a/modules/backend/controllers/auth/reset.htm b/modules/backend/controllers/auth/reset.htm
index cd49622ab..369a04288 100644
--- a/modules/backend/controllers/auth/reset.htm
+++ b/modules/backend/controllers/auth/reset.htm
@@ -1,4 +1,4 @@
-= trans('backend::lang.account.enter_new_password') ?>
+= e(trans('backend::lang.account.enter_new_password')) ?>
= Form::open() ?>
@@ -26,7 +26,7 @@
- = trans('backend::lang.form.cancel') ?>
+ = e(trans('backend::lang.form.cancel')) ?>
diff --git a/modules/backend/controllers/auth/restore.htm b/modules/backend/controllers/auth/restore.htm
index eedbd34ea..6dfe47573 100644
--- a/modules/backend/controllers/auth/restore.htm
+++ b/modules/backend/controllers/auth/restore.htm
@@ -1,4 +1,4 @@
-= trans('backend::lang.account.enter_login') ?>
+= e(trans('backend::lang.account.enter_login')) ?>
= Form::open() ?>
@@ -22,7 +22,7 @@
- = trans('backend::lang.form.cancel') ?>
+ = e(trans('backend::lang.form.cancel')) ?>
diff --git a/modules/backend/controllers/auth/signin.htm b/modules/backend/controllers/auth/signin.htm
index b9a38da4f..82ce38808 100644
--- a/modules/backend/controllers/auth/signin.htm
+++ b/modules/backend/controllers/auth/signin.htm
@@ -1,4 +1,4 @@
-= trans('system::lang.app.motto') ?>
+= e(trans('system::lang.app.motto')) ?>
= Form::open() ?>
@@ -34,7 +34,7 @@
- = trans('backend::lang.account.forgot_password') ?>
+ = e(trans('backend::lang.account.forgot_password')) ?>
diff --git a/modules/backend/formwidgets/codeeditor/partials/_codeeditor.htm b/modules/backend/formwidgets/codeeditor/partials/_codeeditor.htm
index 82001334a..fbfe2bf5d 100644
--- a/modules/backend/formwidgets/codeeditor/partials/_codeeditor.htm
+++ b/modules/backend/formwidgets/codeeditor/partials/_codeeditor.htm
@@ -22,13 +22,13 @@
- Enter fullscreen mode
+ = e(trans('cms::lang.editor.enter_fullscreen')) ?>
- Exit fullscreen mode
+ = e(trans('cms::lang.editor.exit_fullscreen')) ?>
diff --git a/modules/backend/formwidgets/fileupload/partials/_config_form.htm b/modules/backend/formwidgets/fileupload/partials/_config_form.htm
index bd918c372..ff4bce620 100644
--- a/modules/backend/formwidgets/fileupload/partials/_config_form.htm
+++ b/modules/backend/formwidgets/fileupload/partials/_config_form.htm
@@ -3,15 +3,13 @@
-
- Add a title and description for this attachment.
-
+
= e(trans('backend::lang.fileupload.help')) ?>
- Title
+ = e(trans('backend::lang.fileupload.title_label')) ?>
- Description
+ = e(trans('backend::lang.fileupload.description_label')) ?>
@@ -31,13 +29,13 @@
class="btn btn-primary"
data-request="= $this->getEventHandler('onSaveAttachmentConfig') ?>"
data-stripe-load-indicator>
- Save
+ = e(trans('backend::lang.form.save')) ?>
- Cancel
+ = e(trans('backend::lang.form.cancel')) ?>
= Form::close() ?>
\ No newline at end of file
diff --git a/modules/backend/lang/en/lang.php b/modules/backend/lang/en/lang.php
index 5bbaff5e5..e6483cc11 100644
--- a/modules/backend/lang/en/lang.php
+++ b/modules/backend/lang/en/lang.php
@@ -48,6 +48,21 @@ return [
'widget_label' => 'Widget',
'widget_width' => 'Width',
'add_widget' => 'Add widget',
+ 'widget_inspector_title' => 'Widget configuration',
+ 'widget_inspector_description' => 'Configure the report widget',
+ 'widget_columns_label' => 'Width :columns',
+ 'widget_columns_description' => 'The widget width, a number between 1 and 10.',
+ 'widget_columns_error' => 'Please enter the widget width as a number between 1 and 10.',
+ 'columns' => '{1} column|[2,Inf] columns',
+ 'widget_new_row_label' => 'Force new row',
+ 'widget_new_row_description' => 'Put the widget in a new row.',
+ 'widget_title_label' => 'Widget title',
+ 'widget_title_error' => 'The Widget Title is required.',
+ 'status' => [
+ 'widget_title_default' => 'System status',
+ 'online' => 'online',
+ 'update_available' => '{0} updates available!|{1} update available!|[2,Inf] updates available!',
+ ]
],
'user' => [
'name' => 'Administrator',
@@ -71,6 +86,9 @@ return [
'send_invite_comment' => 'Use this checkbox to send an invitation to the user by email',
'delete_confirm' => 'Do you really want to delete this administrator?',
'return' => 'Return to the administrator list',
+ 'allow' => 'Allow',
+ 'inherit' => 'Inherit',
+ 'deny' => 'Deny',
'group' => [
'name' => 'Group',
'name_field' => 'Name',
@@ -94,6 +112,22 @@ return [
'missing_definition' => "List behavior does not contain a column for ':field'.",
'behavior_not_ready' => 'List behavior has not been initialized, check that you have called makeLists() in your controller.',
'invalid_column_datetime' => "Column value ':column' is not a DateTime object, are you missing a \$dates reference in the Model?",
+ 'pagination' => 'Displayed records: :from-:to of :total',
+ 'prev_page' => 'Previous page',
+ 'next_page' => 'Next page',
+ 'loading' => 'Loading...',
+ 'setup_title' => 'List Setup',
+ 'setup_help' => 'Use checkboxes to select columns you want to see in the list. You can change position of columns by dragging them up or down.',
+ 'records_per_page' => 'Records per page',
+ 'records_per_page_help' => 'Select the number of records per page to display. Please note that high number of records on a single page can reduce performance.',
+ 'apply_changes' => 'Apply changes',
+ 'cancel' => 'Cancel'
+ ],
+ 'fileupload' => [
+ 'attachment' => 'Attachment',
+ 'help' => 'Add a title and description for this attachment.',
+ 'title_label' => 'Title',
+ 'description_label' => 'Description'
],
'form' => [
'create_title' => "New :name",
@@ -129,13 +163,20 @@ return [
'select' => 'Select',
'select_all' => 'all',
'select_none' => 'none',
+ 'insert_row' => 'Insert Row',
+ 'delete_row' => 'Delete Row'
],
'relation' => [
'missing_definition' => "Relation behavior does not contain a definition for ':field'.",
'missing_model' => "Relation behavior used in :class does not have a model defined.",
'invalid_action_single' => "This action cannot be performed on a singular relationship.",
'invalid_action_multi' => "This action cannot be performed on a multiple relationship.",
+ 'help' => "Click on an item to add",
+ 'related_data' => "Related :name data",
'add' => "Add",
+ 'add_selected' => "Add selected",
+ 'add_a_new' => "Add a new :name",
+ 'cancel' => "Cancel",
'add_name' => "Add :name",
'create' => "Create",
'create_name' => "Create :name",
@@ -145,6 +186,7 @@ return [
'remove_name' => "Remove :name",
'delete' => "Delete",
'delete_name' => "Delete :name",
+ 'delete_confirm' => "Are you sure?",
],
'model' => [
'name' => "Model",
diff --git a/modules/backend/lang/fr/lang.php b/modules/backend/lang/fr/lang.php
index de3c3a6d4..a2f57a87d 100644
--- a/modules/backend/lang/fr/lang.php
+++ b/modules/backend/lang/fr/lang.php
@@ -45,6 +45,9 @@ return [
],
'dashboard' => [
'menu_label' => 'Tableau de bord',
+ 'widget_label' => 'Widget',
+ 'widget_width' => 'Taille',
+ 'add_widget' => 'Ajouter un widget',
],
'user' => [
'name' => 'Administrateur',
@@ -114,6 +117,7 @@ return [
'undefined_tab' => 'Misc',
'field_off' => 'Off',
'field_on' => 'On',
+ 'add' => 'Ajouter',
'apply' => 'Appliquer',
'cancel' => 'Annuler',
'close' => 'Fermer',
diff --git a/modules/backend/lang/it/lang.php b/modules/backend/lang/it/lang.php
new file mode 100644
index 000000000..b93286705
--- /dev/null
+++ b/modules/backend/lang/it/lang.php
@@ -0,0 +1,227 @@
+ [
+ 'invalid_type' => 'Il tipo di campo :type non è valido.',
+ 'options_method_not_exists' => 'La classe :model deve definire un metodo :method() che ritorni le opzioni per il campo ":field".',
+ ],
+ 'widget' => [
+ 'not_registered' => "Nessun widget ':name' è stato registrato",
+ 'not_bound' => "Nessun widget ':name' è stato legato al controller",
+ ],
+ 'page' => [
+ 'untitled' => "Senza titolo",
+ 'access_denied' => [
+ 'label' => "Accesso negato",
+ 'help' => "Non hai le autorizzazioni necessarie per accedere a questa pagina.",
+ 'cms_link' => "Ritorna al pannello di controllo",
+ ],
+ ],
+ 'partial' => [
+ 'not_found' => "La vista parziale ':name' non è stata trovata.",
+ ],
+ 'account' => [
+ 'sign_out' => 'Esci',
+ 'login' => 'Accedi',
+ 'reset' => 'Reimposta',
+ 'restore' => 'Ripristina',
+ 'login_placeholder' => 'login',
+ 'password_placeholder' => 'password',
+ 'forgot_password' => "Dimenticato la password?",
+ 'enter_email' => "Inserisci in tuo indirizzo e-mail",
+ 'enter_login' => "Inserisci il login.",
+ 'email_placeholder' => "email",
+ 'enter_new_password' => "Inserisci una nuova password",
+ 'password_reset' => "Reimposta password",
+ 'restore_success' => "Le istruzioni per reimpostare la password sono state inviate al tuo indirizzo e-mail.",
+ 'restore_error' => "Nessun utente con login ':login' è stato trovato.",
+ 'reset_success' => "La tua password è stata reimpostata con successo. Ora puoi effettuare l'accesso.",
+ 'reset_error' => "I dati forniti per la reimpostazione della password non sono validi. Riprova!",
+ 'reset_fail' => "Impossibile ripristinare la password!",
+ 'apply' => 'Applica',
+ 'cancel' => 'Annulla',
+ 'delete' => 'Elimina',
+ 'ok' => 'OK',
+ ],
+ 'dashboard' => [
+ 'menu_label' => 'Dashboard',
+ 'widget_label' => 'Widget',
+ 'widget_width' => 'Larghezza',
+ 'add_widget' => 'Aggiungi widget',
+ 'widget_inspector_title' => 'Configurazione widget',
+ 'widget_inspector_description' => 'Configura il widget',
+ 'widget_columns_label' => 'Larghezza :columns',
+ 'widget_columns_description' => 'La larghezza del widget, un numero compreso tra 1 e 10.',
+ 'widget_columns_error' => 'La larghezza del widget deve essere un numero compreso tra 1 e 10.',
+ 'columns' => '{1} colonna|[2,Inf] colonne',
+ 'widget_new_row_label' => 'Forza nuova riga',
+ 'widget_new_row_description' => 'Inserisci il widget su una nuova riga.',
+ 'widget_title_label' => 'Titolo del widget',
+ 'widget_title_error' => 'Il titolo del widget è un campo obbligatorio.',
+ 'status' => [
+ 'widget_title_default' => 'Stato del sistema',
+ 'online' => 'online',
+ 'update_available' => '{0} aggiornamenti disponibili!|{1} aggiornamento disponibile!|[2,Inf] aggiornamenti disponibili!',
+ ]
+ ],
+ 'user' => [
+ 'name' => 'Amministratori',
+ 'menu_label' => 'Amministratori',
+ 'menu_description' => 'Gestisci gli utenti amministratori, i gruppi e le autorizzazioni.',
+ 'list_title' => 'Gestisci amministratori',
+ 'new' => 'Nuovo amministratorre',
+ 'login' => "Login",
+ 'first_name' => "Nome",
+ 'last_name' => "Cognome",
+ 'full_name' => "Nome completo",
+ 'email' => "Indirizzo e-mail",
+ 'groups' => "Gruppi",
+ 'groups_comment' => "Seleziona i gruppi a cui appartiene l'utente.",
+ 'avatar' => "Avatar",
+ 'password' => "Password",
+ 'password_confirmation' => "Conferma password",
+ 'superuser' => "Super User",
+ 'superuser_comment' => "Seleziona per consentire all'utente di accedere a tutte le aree.",
+ 'send_invite' => 'Invia invito tramite e-mail',
+ 'send_invite_comment' => 'Seleziona per inviare un invito all\'utente tramite e-mail',
+ 'delete_confirm' => 'Vuoi davvero eliminare questo amministratore?',
+ 'return' => 'Ritorna alla lista degli amministratori',
+ 'group' => [
+ 'name' => 'Gruppo',
+ 'name_field' => 'Nome',
+ 'menu_label' => 'Gruppi',
+ 'list_title' => 'Gestisci gruppi',
+ 'new' => 'Nuovo gruppo amministratore',
+ 'delete_confirm' => 'Vuoi davvero eliminare questo gruppo amministratore?',
+ 'return' => 'Ritorna alla lista dei gruppi',
+ ],
+ 'preferences' => [
+ 'not_authenticated' => 'Non c\'è nessun utente autenticato per cui caricare o salvare le preferenze.'
+ ]
+ ],
+ 'list' => [
+ 'default_title' => 'Elenco',
+ 'search_prompt' => 'Cerca...',
+ 'no_records' => 'Nessun risultato trovato.',
+ 'missing_model' => 'L\'elenco utilizzato nella classe :class non ha un modello definito.',
+ 'missing_column' => 'Non ci sono colonne definite per :columns.',
+ 'missing_columns' => 'L\'elenco utilizzato nella classe :class non ha un elenco di colonne definito.',
+ 'missing_definition' => "L'elenco non contiene una colonna per il campo ':field'.",
+ 'behavior_not_ready' => 'L\'elenco non è stato inizializzato, controlla di aver chiamato il metodo makeLists() nel controller.',
+ 'invalid_column_datetime' => "Il valore della colonna ':column' non è un oggetto di tipo DateTime, hai dimenticato un riferimento a \$dates nel modello?",
+ 'pagination' => 'Record visualizzati: :from-:to di :total',
+ 'setup_title' => 'Configura elenco',
+ 'setup_help' => 'Utilizza le checkbox per selezionare le colonne che vuoi visualizzare nell\'elenco. Puoi cambiare la posizione delle colonne trascinandole verso l\'alto o il basso.',
+ 'records_per_page' => 'Record per pagina',
+ 'records_per_page_help' => 'Seleziona il numero di record da visualizzare su ogni pagina. Ricorda che un numero elevato di record in una singola pagina può ridurre le prestazioni.',
+ 'apply_changes' => 'Applica modifiche',
+ 'cancel' => 'Annulla'
+ ],
+ 'form' => [
+ 'create_title' => "Nuovo :name",
+ 'update_title' => "Modifica :name",
+ 'preview_title' => "Anteprima :name",
+ 'create_success' => ':name creato con successo',
+ 'update_success' => ':name modificato con successo',
+ 'delete_success' => ':name eliminato con successo',
+ 'missing_id' => "L'ID del record non è stato specificato.",
+ 'missing_model' => 'Il form utilizzato nella classe :class non ha un modello definito.',
+ 'missing_definition' => "Il form non contiene il campo ':field'.",
+ 'not_found' => 'Nessun record per l\'ID :id è stato trovato.',
+ 'create' => 'Crea',
+ 'create_and_close' => 'Crea e chiudi',
+ 'creating' => 'Creazione in corso...',
+ 'save' => 'Salva',
+ 'save_and_close' => 'Salva e chiudi',
+ 'saving' => 'Salvataggio in corso...',
+ 'delete' => 'Elimina',
+ 'deleting' => 'Eliminazione in corso...',
+ 'undefined_tab' => 'Varie',
+ 'field_off' => 'Off',
+ 'field_on' => 'On',
+ 'add' => 'Aggiungi',
+ 'apply' => 'Applica',
+ 'cancel' => 'Annulla',
+ 'close' => 'Chiudi',
+ 'ok' => 'OK',
+ 'or' => 'o',
+ 'confirm_tab_close' => 'Vuoi davvero chiudere il tab? Le modifiche non salvate andranno perse.',
+ 'behavior_not_ready' => 'Il form non è stato inizializzato, verifica di aver chiamato il metodo initForm() nel controller.',
+ 'preview_no_files_message' => 'I file non sono stati caricati',
+ 'select' => 'Seleziona',
+ 'select_all' => 'tutti',
+ 'select_none' => 'nessuno',
+ ],
+ 'relation' => [
+ 'missing_definition' => "La relazione non contiene una definizione per il campo ':field'.",
+ 'missing_model' => "La relazione utilizzata nella classe :class non ha un modello definito.",
+ 'invalid_action_single' => "L'azione non può essere eseguita su una relazione singola.",
+ 'invalid_action_multi' => "L'azione non può essere eseguita su una relazione multipla.",
+ 'add' => "Aggiungi",
+ 'add_name' => "Aggiungi :name",
+ 'create' => "Crea",
+ 'create_name' => "Crea :name",
+ 'update' => "Aggiorna",
+ 'update_name' => "Aggiorna :name",
+ 'remove' => "Rimuovi",
+ 'remove_name' => "Rimuovi :name",
+ 'delete' => "Elimina",
+ 'delete_name' => "Elimina :name",
+ ],
+ 'model' => [
+ 'name' => "Modello",
+ 'not_found' => "Nessun modello di ':class' con l'ID :id trovato.",
+ 'missing_id' => "Nessun ID specificato.",
+ 'missing_relation' => "Il modello ':class' non contiene una definizione per la relazione ':relation'.",
+ 'invalid_class' => "Il modello :model utilizzato nella classe :class non è valido, deve ereditare la classe Model.",
+ 'mass_assignment_failed' => "Assegnazione massiva fallita per l'attributo ':attribute' del modello.",
+ ],
+ 'warnings' => [
+ 'tips' => 'Suggerimenti di configurazione del sistema',
+ 'tips_description' => 'Ci sono elementi a cui è necessario prestare attenzione al fine di configurare il sistema in maniera corretta.',
+ 'permissions' => 'La cartella :name o le sue sottocartelle non sono scrivibili da PHP. Imposta le corrette autorizzazioni per il server web su questa cartella.',
+ 'extension' => 'L\'estenzione di PHP :name non è installata. Installa questa libreria ed attiva l\'estensione.'
+ ],
+ 'editor' => [
+ 'menu_label' => 'Preferenze editor di codice',
+ 'menu_description' => 'Personalizza le impostazioni dell\'editor, come la dimensione del carattere e lo schema di colori.',
+ 'font_size' => 'Dimensione carattere',
+ 'tab_size' => 'Dimensione Tab',
+ 'use_hard_tabs' => 'Indenta utilizzando i Tab',
+ 'code_folding' => 'Raggruppa il codice',
+ 'word_wrap' => 'A capo automatico',
+ 'highlight_active_line' => 'Evidenzia la linea attiva',
+ 'show_invisibles' => 'Mostra caratteri invisibili',
+ 'show_gutter' => 'Visualizza numeri di linea',
+ 'theme' => 'Schema di colori',
+ ],
+ 'tooltips' => [
+ 'preview_website' => 'Anteprima del sito web'
+ ],
+ 'mysettings' => [
+ 'menu_label' => 'Impostazioni personali',
+ 'menu_description' => 'Impostazioni legate al tuo account amministratore',
+ ],
+ 'myaccount' => [
+ 'menu_label' => 'Il mio account',
+ 'menu_description' => 'Aggiorna i dettagli del tuo account, come il nome, l\'indirizzo e-mail e la password.',
+ 'menu_keywords' => 'sicurezza login'
+ ],
+ 'backend_preferences' => [
+ 'menu_label' => 'Preferenze pannello di controllo',
+ 'menu_description' => 'Gestisci le preferenze della lingua e l\'aspetto del pannello di controllo.',
+ 'locale' => 'Lingua',
+ 'locale_comment' => 'Seleziona la lingua da utilizzare.',
+ ],
+ 'access_log' => [
+ 'hint' => 'Questo registro visualizza un elenco dei tentativi di accesso di un amministratore avvenuti con successo. I record sono mantenuti per un totale di :days giorni.',
+ 'menu_label' => 'Registro accessi',
+ 'menu_description' => 'Visualizza una lista degli accessi da parte degli amministratori.',
+ 'created_at' => 'Data e ora',
+ 'login' => 'Login',
+ 'ip_address' => 'Indirizzo IP',
+ 'first_name' => 'Nome',
+ 'last_name' => 'Cognome',
+ 'email' => 'Indirizzo e-mail',
+ ],
+];
diff --git a/modules/backend/lang/ru/lang.php b/modules/backend/lang/ru/lang.php
index 832e24bfe..b5986ba3d 100644
--- a/modules/backend/lang/ru/lang.php
+++ b/modules/backend/lang/ru/lang.php
@@ -45,6 +45,9 @@ return [
],
'dashboard' => [
'menu_label' => 'Панель управления',
+ 'widget_label' => 'Виджет',
+ 'widget_width' => 'Ширина',
+ 'add_widget' => 'Добавить виджет',
],
'user' => [
'name' => 'Администратора',
@@ -114,6 +117,7 @@ return [
'undefined_tab' => 'Разное',
'field_off' => 'Выкл',
'field_on' => 'Вкл',
+ 'add' => 'Добавить',
'apply' => 'Применить',
'cancel' => 'Отмена',
'close' => 'Закрыть',
@@ -187,4 +191,15 @@ return [
'locale' => 'Язык',
'locale_comment' => 'Выберите желаемый язык панели управления.',
],
+ 'access_log' => [
+ 'hint' => 'В этом журнале отображается список успешных попыток авторизаций администраторов. Записи хранятся :days дней.',
+ 'menu_label' => 'Журнал доступа',
+ 'menu_description' => 'Просмотр списка успешных авторизаций администраторов.',
+ 'created_at' => 'Дата & Время',
+ 'login' => 'Логин',
+ 'ip_address' => 'IP адересс',
+ 'first_name' => 'Имя',
+ 'last_name' => 'Фамилия',
+ 'email' => 'Почта',
+ ],
];
diff --git a/modules/backend/layouts/_footer.htm b/modules/backend/layouts/_footer.htm
index 9ecfb7f77..97a3a97e8 100644
--- a/modules/backend/layouts/_footer.htm
+++ b/modules/backend/layouts/_footer.htm
@@ -1,9 +1,9 @@
\ No newline at end of file
diff --git a/modules/backend/layouts/_head.htm b/modules/backend/layouts/_head.htm
index fc61b28d6..178b8fc56 100644
--- a/modules/backend/layouts/_head.htm
+++ b/modules/backend/layouts/_head.htm
@@ -1,6 +1,7 @@
-
-
+
+
+
= $this->pageTitle ?> | October CMS
diff --git a/modules/backend/layouts/_mainmenu.htm b/modules/backend/layouts/_mainmenu.htm
index 81e2ae1aa..61e18be3f 100644
--- a/modules/backend/layouts/_mainmenu.htm
+++ b/modules/backend/layouts/_mainmenu.htm
@@ -28,7 +28,12 @@
*/
?>
-
+
@@ -41,7 +46,13 @@
-
+
+
+
diff --git a/modules/backend/layouts/auth.htm b/modules/backend/layouts/auth.htm
index f2ba4eff0..331cb34de 100644
--- a/modules/backend/layouts/auth.htm
+++ b/modules/backend/layouts/auth.htm
@@ -26,7 +26,7 @@
-
= trans('system::lang.app.name') ?>
+ = e(trans('system::lang.app.name')) ?>
diff --git a/modules/backend/models/BackendPreferences.php b/modules/backend/models/BackendPreferences.php
index 010c8cb52..b365af49a 100644
--- a/modules/backend/models/BackendPreferences.php
+++ b/modules/backend/models/BackendPreferences.php
@@ -27,20 +27,26 @@ class BackendPreferences extends Model
public function getLocaleOptions()
{
- return [
+ $locales = [
'en' => [Lang::get('system::lang.locale.en'), 'flag-gb'],
'ru' => [Lang::get('system::lang.locale.ru'), 'flag-ru'],
'nl' => [Lang::get('system::lang.locale.nl'), 'flag-nl'],
'ja' => [Lang::get('system::lang.locale.ja'), 'flag-jp'],
- 'sv' => [Lang::get('system::lang.locale.sv'), 'flag-sv'],
+ 'se' => [Lang::get('system::lang.locale.se'), 'flag-se'],
'tr' => [Lang::get('system::lang.locale.tr'), 'flag-tr'],
'br' => [Lang::get('system::lang.locale.br'), 'flag-br'],
'de' => [Lang::get('system::lang.locale.de'), 'flag-de'],
+ 'fr' => [Lang::get('system::lang.locale.fr'), 'flag-fr'],
+ 'it' => [Lang::get('system::lang.locale.it'), 'flag-it'],
];
+
+ // Sort the locales alphabetically
+ asort($locales);
+ return $locales;
}
public function afterSave()
{
Session::put('locale', $this->locale);
}
-}
\ No newline at end of file
+}
diff --git a/modules/backend/models/EditorPreferences.php b/modules/backend/models/EditorPreferences.php
index 9d021ccd9..739fd6f26 100644
--- a/modules/backend/models/EditorPreferences.php
+++ b/modules/backend/models/EditorPreferences.php
@@ -65,6 +65,6 @@ class EditorPreferences extends Model
// Sort the theme alphabetically, and push the default theme
asort($themes);
- return [static::DEFAULT_THEME => 'Twilight'] + $themes;
+ return [static::DEFAULT_THEME => ucwords(static::DEFAULT_THEME)] + $themes;
}
}
\ No newline at end of file
diff --git a/modules/backend/widgets/Filter.php b/modules/backend/widgets/Filter.php
index 6bc473ce3..d1b34aa47 100644
--- a/modules/backend/widgets/Filter.php
+++ b/modules/backend/widgets/Filter.php
@@ -1,6 +1,11 @@
activeContext = $this->getConfig('context');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function loadAssets()
+ {
+ $this->addJs('js/october.filter.js', 'core');
+ }
+
/**
* Renders the widget.
*/
@@ -30,5 +77,388 @@ class Filter extends WidgetBase
*/
public function prepareVars()
{
+ $this->defineFilterScopes();
+ $this->vars['cssClasses'] = implode(' ', $this->cssClasses);
+ $this->vars['scopes'] = $this->allScopes;
}
+
+ /**
+ * Renders the HTML element for a scope
+ */
+ public function renderScopeElement($scope)
+ {
+ return $this->makePartial('scope_'.$scope->type, ['scope' => $scope]);
+ }
+
+ //
+ // AJAX
+ //
+
+ /**
+ * Update a filter scope value.
+ * @return array
+ */
+ public function onFilterUpdate()
+ {
+ $this->defineFilterScopes();
+
+ if (!$scope = post('scopeName'))
+ return;
+
+ $scope = $this->getScope($scope);
+
+ switch ($scope->type) {
+ case 'group':
+ $active = $this->optionsFromAjax(post('options.active'));
+ $this->setScopeValue($scope, $active);
+ break;
+
+ case 'checkbox':
+ $checked = post('value') == 'true' ? true : false;
+ $this->setScopeValue($scope, $checked);
+ break;
+ }
+
+ /*
+ * Trigger class event, merge results as viewable array
+ */
+ $params = func_get_args();
+ $result = $this->fireEvent('filter.update', [$params]);
+ if ($result && is_array($result))
+ return Util::arrayMerge($result);
+ }
+
+ /**
+ * Returns available options for group scope type.
+ * @return array
+ */
+ public function onFilterGetOptions()
+ {
+ $this->defineFilterScopes();
+
+ $searchQuery = post('search');
+ if (!$scopeName = post('scopeName'))
+ return;
+
+ $scope = $this->getScope($scopeName);
+ $activeKeys = $scope->value ? array_keys($scope->value) : [];
+ $available = $this->getAvailableOptions($scope, $searchQuery);
+ $active = $searchQuery ? [] : $this->filterActiveOptions($activeKeys, $available);
+
+ return [
+ 'scopeName' => $scopeName,
+ 'options' => [
+ 'available' => $this->optionsToAjax($available),
+ 'active' => $this->optionsToAjax($active),
+ ]
+ ];
+ }
+
+ //
+ // Internals
+ //
+
+ /**
+ * Returns the available options a scope can use, either from the
+ * model relation or from a supplied array. Optionally apply a search
+ * constraint to the options.
+ * @param string $scope
+ * @param string $searchQuery
+ * @return array
+ */
+ protected function getAvailableOptions($scope, $searchQuery = null)
+ {
+ $available = [];
+ $nameColumn = $this->getScopeNameColumn($scope);
+ $options = $this->getOptionsFromModel($scope, $searchQuery);
+ foreach ($options as $option) {
+ $available[$option->getKey()] = $option->{$nameColumn};
+ }
+ return $available;
+ }
+
+ /**
+ * Removes any already selected options from the available options, returns
+ * a newly built array.
+ * @param array $activeKeys
+ * @param array $availableOptions
+ * @return array
+ */
+ protected function filterActiveOptions(array $activeKeys, array &$availableOptions)
+ {
+ $active = [];
+ foreach ($availableOptions as $id => $option) {
+ if (!in_array($id, $activeKeys))
+ continue;
+
+ $active[$id] = $option;
+ unset($availableOptions[$id]);
+ }
+
+ return $active;
+ }
+
+ /**
+ * Looks at the model for defined scope items.
+ */
+ protected function getOptionsFromModel($scope, $searchQuery = null)
+ {
+ $model = $this->scopeModels[$scope->scopeName];
+ if (!$searchQuery)
+ return $model->all();
+
+ $searchFields = [$model->getKeyName(), $this->getScopeNameColumn($scope)];
+ return $model->searchWhere($searchQuery, $searchFields)->get();
+ }
+
+ /**
+ * Creates a flat array of filter scopes from the configuration.
+ */
+ protected function defineFilterScopes()
+ {
+ if ($this->scopesDefined)
+ return;
+
+ /*
+ * Extensibility
+ */
+ Event::fire('backend.filter.extendScopesBefore', [$this]);
+ $this->fireEvent('filter.extendScopesBefore');
+
+ /*
+ * All scopes
+ */
+ if (!isset($this->config->scopes) || !is_array($this->config->scopes))
+ $this->config->scopes = [];
+
+ $this->addScopes($this->config->scopes);
+
+ /*
+ * Extensibility
+ */
+ Event::fire('backend.filter.extendScopes', [$this]);
+ $this->fireEvent('filter.extendScopes');
+
+ $this->scopesDefined = true;
+ }
+
+ /**
+ * Programatically add scopes, used internally and for extensibility.
+ */
+ public function addScopes(array $scopes)
+ {
+ foreach ($scopes as $name => $config) {
+
+ $scopeObj = $this->makeFilterScope($name, $config);
+
+ /*
+ * Check that the filter scope matches the active context
+ */
+ if ($scopeObj->context !== null) {
+ $context = (is_array($scopeObj->context)) ? $scopeObj->context : [$scopeObj->context];
+ if (!in_array($this->getContext(), $context))
+ continue;
+ }
+
+ /*
+ * Validate scope model
+ */
+ if (isset($config['modelClass'])) {
+ $class = $config['modelClass'];
+ $model = new $class;
+ $this->scopeModels[$name] = $model;
+ }
+
+ $this->allScopes[$name] = $scopeObj;
+ }
+ }
+
+ /**
+ * Creates a filter scope object from name and configuration.
+ */
+ protected function makeFilterScope($name, $config)
+ {
+ $label = (isset($config['label'])) ? $config['label'] : null;
+ $scopeType = isset($config['type']) ? $config['type'] : null;
+
+ $scope = new FilterScope($name, $label);
+ $scope->displayAs($scopeType, $config);
+
+ /*
+ * Set scope value
+ */
+ $scope->value = $this->getScopeValue($scope);
+
+ return $scope;
+ }
+
+ //
+ // Filter query logic
+ //
+
+ /**
+ * Applies all scopes to a DB query.
+ * @param Builder $query
+ * @return Builder
+ */
+ public function applyAllScopesToQuery($query)
+ {
+ foreach ($this->allScopes as $scope) {
+ $this->applyScopeToQuery($scope, $query);
+ }
+
+ return $query;
+ }
+
+ /**
+ * Applies a filter scope constraints to a DB query.
+ * @param string $scope
+ * @param Builder $query
+ * @return Builder
+ */
+ public function applyScopeToQuery($scope, $query)
+ {
+ if (is_string($scope))
+ $scope = $this->getScope($scope);
+
+ if (!$scope->value)
+ return;
+
+ $value = is_array($scope->value) ? array_keys($scope->value) : $scope->value;
+
+ /*
+ * Condition
+ */
+ if ($scopeConditions = $scope->conditions) {
+ if (is_array($value)) {
+ $filtered = implode(',', array_build($value, function($key, $_value){
+ return [$key, Db::getPdo()->quote($_value)];
+ }));
+ }
+ else {
+ $filtered = Db::getPdo()->quote($value);
+ }
+
+ $query->whereRaw(strtr($scopeConditions, [':filtered' => $filtered]));
+ }
+
+ /*
+ * Scope
+ */
+ if ($scopeMethod = $scope->scope) {
+ $query->$scopeMethod($value);
+ }
+
+ return $query;
+ }
+
+ //
+ // Access layer
+ //
+
+ /**
+ * Returns a scope value for this widget instance.
+ */
+ public function getScopeValue($scope, $default = null)
+ {
+ if (is_string($scope))
+ $scope = $this->getScope($scope);
+
+ $cacheKey = 'scope-'.$scope->scopeName;
+ return $this->getSession($cacheKey, $default);
+ }
+
+ /**
+ * Sets an scope value for this widget instance.
+ */
+ public function setScopeValue($scope, $value)
+ {
+ if (is_string($scope))
+ $scope = $this->getScope($scope);
+
+ $cacheKey = 'scope-'.$scope->scopeName;
+ $this->putSession($cacheKey, $value);
+
+ $scope->value = $value;
+ }
+
+ /**
+ * Get all the registered scopes for the instance.
+ * @return array
+ */
+ public function getScopes()
+ {
+ return $this->allScopes;
+ }
+
+ /**
+ * Get a specified scope object
+ * @param string $scope
+ * @return mixed
+ */
+ public function getScope($scope)
+ {
+ if (!isset($this->allScopes[$scope]))
+ throw new ApplicationException('No definition for scope ' . $scope);
+
+ return $this->allScopes[$scope];
+ }
+
+ /**
+ * Returns the display name column for a scope.
+ * @param string $scope
+ * @return string
+ */
+ public function getScopeNameColumn($scope)
+ {
+ if (is_string($scope))
+ $scope = $this->getScope($scope);
+
+ return $scope->nameColumn;
+ }
+
+ /**
+ * Returns the active context for displaying the filter.
+ */
+ public function getContext()
+ {
+ return $this->activeContext;
+ }
+
+ //
+ // Helpers
+ //
+
+ /**
+ * Convert a key/pair array to a named array {id: 1, name: 'Foobar'}
+ * @param array $options
+ * @return array
+ */
+ protected function optionsToAjax($options)
+ {
+ $processed = [];
+ foreach ($options as $id => $result) {
+ $processed[] = ['id' => $id, 'name' => $result];
+ }
+ return $processed;
+ }
+
+ /**
+ * Convert a named array to a key/pair array
+ * @param array $options
+ * @return array
+ */
+ protected function optionsFromAjax($options)
+ {
+ $processed = [];
+ if (!is_array($options))
+ return $processed;
+
+ foreach ($options as $option) {
+ if (!$id = array_get($option, 'id')) continue;
+ $processed[$id] = array_get($option, 'name');
+ }
+ return $processed;
+ }
+
}
\ No newline at end of file
diff --git a/modules/backend/widgets/Lists.php b/modules/backend/widgets/Lists.php
index d68c199df..a0cce838b 100644
--- a/modules/backend/widgets/Lists.php
+++ b/modules/backend/widgets/Lists.php
@@ -78,7 +78,12 @@ class Lists extends WidgetBase
/**
* @var string Filter the records by a search term.
*/
- public $searchTerm;
+ protected $searchTerm;
+
+ /**
+ * @var array Collection of functions to apply to each list query.
+ */
+ protected $filterCallbacks = [];
/**
* @var bool Shows the sorting options for each column.
@@ -339,8 +344,11 @@ class Lists extends WidgetBase
}
/*
- * @todo Apply filters etc
+ * Apply filters
*/
+ foreach ($this->filterCallbacks as $callback) {
+ $callback($query);
+ }
/*
* Extensibility
@@ -694,6 +702,15 @@ class Lists extends WidgetBase
return $value;
}
+ //
+ // Filtering
+ //
+
+ public function addFilter(callable $filter)
+ {
+ $this->filterCallbacks[] = $filter;
+ }
+
//
// Searching
//
diff --git a/modules/backend/widgets/ReportContainer.php b/modules/backend/widgets/ReportContainer.php
index 61bd8e94d..623649777 100644
--- a/modules/backend/widgets/ReportContainer.php
+++ b/modules/backend/widgets/ReportContainer.php
@@ -1,11 +1,12 @@
getId('container-list') => $this->makePartial('widget', [
- 'widget' => $widget,
+ 'widget' => $widget,
'widgetAlias' => $widgetInfo['alias'],
- 'sortOrder' => $widgetInfo['sortOrder']
+ 'sortOrder' => $widgetInfo['sortOrder']
])
];
}
@@ -282,45 +283,45 @@ class ReportContainer extends WidgetBase
$property = [
'property' => 'ocWidgetWidth',
- 'title' => 'Width (1-10)',
- 'description' => 'The widget width, a number between 1 and 10.',
+ 'title' => Lang::get('backend::lang.dashboard.widget_columns_label', ['columns' => '(1-10)']),
+ 'description' => Lang::get('backend::lang.dashboard.widget_columns_description'),
'type' => 'dropdown',
'validationPattern' => '^[0-9]+$',
- 'validationMessage' => 'Please enter the widget width as a number between 1 and 10.',
+ 'validationMessage' => Lang::get('backend::lang.dashboard.widget_columns_error'),
'options' => [
- 1 => '1 column',
- 2 => '2 columns',
- 3 => '3 columns',
- 4 => '4 columns',
- 5 => '5 columns',
- 6 => '6 columns',
- 7 => '7 columns',
- 8 => '8 columns',
- 9 => '9 columns',
- 10 => '10 columns'
+ 1 => '1 ' . Lang::choice('backend::lang.dashboard.columns', 1),
+ 2 => '2 ' . Lang::choice('backend::lang.dashboard.columns', 2),
+ 3 => '3 ' . Lang::choice('backend::lang.dashboard.columns', 3),
+ 4 => '4 ' . Lang::choice('backend::lang.dashboard.columns', 4),
+ 5 => '5 ' . Lang::choice('backend::lang.dashboard.columns', 5),
+ 6 => '6 ' . Lang::choice('backend::lang.dashboard.columns', 6),
+ 7 => '7 ' . Lang::choice('backend::lang.dashboard.columns', 7),
+ 8 => '8 ' . Lang::choice('backend::lang.dashboard.columns', 8),
+ 9 => '9 ' . Lang::choice('backend::lang.dashboard.columns', 9),
+ 10 => '10 ' . Lang::choice('backend::lang.dashboard.columns', 10)
]
];
$result[] = $property;
$property = [
'property' => 'ocWidgetNewRow',
- 'title' => 'Force new row',
- 'description' => 'Put the widget in a new row.',
+ 'title' => Lang::get('backend::lang.dashboard.widget_new_row_label'),
+ 'description' => Lang::get('backend::lang.dashboard.widget_new_row_description'),
'type' => 'checkbox'
];
$result[] = $property;
- foreach ($properties as $name=>$params) {
+ foreach ($properties as $name => $params) {
$property = [
'property' => $name,
- 'title' => isset($params['title']) ? $params['title'] : $name,
+ 'title' => isset($params['title']) ? Lang::get($params['title']) : $name,
'type' => isset($params['type']) ? $params['type'] : 'string'
];
foreach ($params as $name => $value) {
if (isset($property[$name])) continue;
- $property[$name] = $value;
+ $property[$name] = Lang::get($value);
}
$result[] = $property;
@@ -334,8 +335,9 @@ class ReportContainer extends WidgetBase
$result = [];
$properties = $widget->defineProperties();
- foreach ($properties as $name=>$params)
- $result[$name] = $widget->property($name);
+ foreach ($properties as $name => $params) {
+ $result[$name] = Lang::get($widget->property($name));
+ }
$result['ocWidgetWidth'] = $widget->property('ocWidgetWidth');
$result['ocWidgetNewRow'] = $widget->property('ocWidgetNewRow');
diff --git a/modules/backend/widgets/Search.php b/modules/backend/widgets/Search.php
index fa75b8b6d..f7734d3be 100644
--- a/modules/backend/widgets/Search.php
+++ b/modules/backend/widgets/Search.php
@@ -109,7 +109,8 @@ class Search extends WidgetBase
*/
$params = func_get_args();
$result = $this->fireEvent('search.submit', [$params]);
- return Util::arrayMerge($result);
+ if ($result && is_array($result))
+ return Util::arrayMerge($result);
}
/**
diff --git a/modules/backend/widgets/filter/assets/js/october.filter.js b/modules/backend/widgets/filter/assets/js/october.filter.js
new file mode 100644
index 000000000..6ddd7b542
--- /dev/null
+++ b/modules/backend/widgets/filter/assets/js/october.filter.js
@@ -0,0 +1,367 @@
+/*
+ * Filter Widget
+ *
+ * Data attributes:
+ * - data-behavior="filter" - enables the filter plugin
+ *
+ * Dependences:
+ * - October Popover (october.popover.js)
+ */
++function ($) { "use strict";
+
+ var FilterWidget = function (element, options) {
+
+ var $el = this.$el = $(element);
+
+ this.options = options || {}
+ this.scopeValues = {}
+ this.$activeScope = null
+ this.activeScopeName = null
+ this.isActiveScopeDirty = false
+
+ this.init()
+ }
+
+ FilterWidget.DEFAULTS = {
+ optionsHandler: null,
+ updateHandler: null
+ }
+
+ /*
+ * Get popover template
+ */
+ FilterWidget.prototype.getPopoverTemplate = function() {
+ return ' \
+
\
+ '
+ }
+
+ FilterWidget.prototype.init = function() {
+ var self = this
+
+ this.$el.on('change', '.filter-scope input[type="checkbox"]', function(){
+ var isChecked = $(this).is(':checked'),
+ $scope = $(this).closest('.filter-scope'),
+ scopeName = $scope.data('scope-name')
+
+ self.scopeValues[scopeName] = isChecked
+ self.checkboxToggle(scopeName, isChecked)
+ })
+
+ this.$el.on('click', 'a.filter-scope', function(){
+ var $scope = $(this),
+ scopeName = $scope.data('scope-name')
+
+ // Second click closes the filter scope
+ if ($scope.hasClass('filter-scope-open')) return
+
+ self.$activeScope = $scope
+ self.activeScopeName = scopeName
+ self.isActiveScopeDirty = false
+ self.displayPopover($scope)
+ $scope.addClass('filter-scope-open')
+ })
+
+ this.$el.on('show.oc.popover', 'a.filter-scope', function(){
+ self.focusSearch()
+ })
+
+ this.$el.on('hide.oc.popover', 'a.filter-scope', function(){
+ var $scope = $(this)
+ self.pushOptions(self.activeScopeName)
+ self.activeScopeName = null
+ self.$activeScope = null
+
+ // Second click closes the filter scope
+ setTimeout(function() { $scope.removeClass('filter-scope-open') }, 200)
+ })
+
+ $(document).on('click', '#controlFilterPopover .filter-items > ul > li', function(){
+ self.selectItem($(this))
+ })
+
+ $(document).on('click', '#controlFilterPopover .filter-active-items > ul > li', function(){
+ self.selectItem($(this), true)
+ })
+
+ $(document).on('ajaxDone', '#controlFilterPopover input.filter-search-input', function(event, context, data){
+ self.filterAvailable(data.scopeName, data.options.available)
+ })
+ }
+
+ FilterWidget.prototype.focusSearch = function() {
+ if (Modernizr.touch)
+ return
+
+ var $input = $('#controlFilterPopover input.filter-search-input'),
+ length = $input.val().length
+
+ $input.focus()
+ $input.get(0).setSelectionRange(length, length)
+ }
+
+ FilterWidget.prototype.updateScopeSetting = function($scope, amount) {
+ var $setting = $scope.find('.filter-setting')
+
+ if (amount) {
+ $setting.text(amount)
+ $scope.addClass('active')
+ }
+ else {
+ $setting.text('all')
+ $scope.removeClass('active')
+ }
+ }
+
+ FilterWidget.prototype.selectItem = function($item, isDeselect) {
+ var $otherContainer = isDeselect
+ ? $item.closest('.control-filter-popover').find('.filter-items:first > ul')
+ : $item.closest('.control-filter-popover').find('.filter-active-items:first > ul')
+
+ $item
+ .addClass('animate-enter')
+ .prependTo($otherContainer)
+ .one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function(){
+ $(this).removeClass('animate-enter')
+ })
+
+ 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)
+
+ if (isDeselect)
+ this.scopeValues[this.activeScopeName].active = filtered
+ else
+ this.scopeValues[this.activeScopeName].available = filtered
+
+ if (item)
+ toItems.push(item)
+
+ this.updateScopeSetting(this.$activeScope, items.active.length)
+ this.isActiveScopeDirty = true
+ this.focusSearch()
+ }
+
+ FilterWidget.prototype.displayPopover = function($scope) {
+ var self = this,
+ scopeName = $scope.data('scope-name'),
+ data = this.scopeValues[scopeName]
+
+ if (!data) {
+ self.loadOptions(scopeName)
+ data = { loading: true }
+ }
+
+ data.scopeName = scopeName
+ data.optionsHandler = self.options.optionsHandler
+
+ // Destroy any popovers already bound
+ $scope.data('oc.popover', null)
+
+ $scope.ocPopover({
+ content: Mustache.render(self.getPopoverTemplate(), data),
+ modal: false,
+ highlightModalTarget: true,
+ closeOnPageClick: true,
+ placement: 'bottom'
+ })
+ }
+
+ FilterWidget.prototype.loadOptions = function(scopeName) {
+ var $form = this.$el.closest('form'),
+ self = this,
+ data = { scopeName: scopeName }
+
+ $form.request(this.options.optionsHandler, {
+ data: data,
+ success: function(data) {
+
+ if (self.scopeValues[scopeName])
+ return
+
+ self.scopeValues[scopeName] = data.options
+
+ // Do not render if scope has changed
+ if (scopeName != self.activeScopeName)
+ return
+
+ /*
+ * Inject available
+ */
+ if (data.options.available) {
+ var container = $('#controlFilterPopover .filter-items > ul').empty()
+ self.addItemsToListElement(container, data.options.available)
+ }
+
+ /*
+ * Inject active
+ */
+ if (data.options.active) {
+ var container = $('#controlFilterPopover .filter-active-items > ul')
+ self.addItemsToListElement(container, data.options.active)
+ }
+
+ }
+ })
+
+ }
+
+ FilterWidget.prototype.filterAvailable = function(scopeName, available) {
+ if (this.activeScopeName != scopeName)
+ return
+
+ if (!this.scopeValues[this.activeScopeName])
+ return
+
+ var
+ self = this,
+ filtered = [],
+ items = this.scopeValues[scopeName]
+
+ /*
+ * Ensure any active items do not appear in the search results
+ */
+ if (items.active.length) {
+ var compareFunc = function(a, b) { return a.id == b.id },
+ inArrayFunc = function(elem, array, testFunc) {
+ var i = array.length
+ do { if (i-- === 0) return i } while (testFunc(array[i], elem))
+ return i
+ }
+
+ filtered = $.grep(available, function(item) {
+ return !inArrayFunc(item, items.active, compareFunc)
+ })
+ }
+ else {
+ filtered = available
+ }
+
+ var container = $('#controlFilterPopover .filter-items > ul').empty()
+ self.addItemsToListElement(container, filtered)
+ }
+
+ FilterWidget.prototype.addItemsToListElement = function($ul, items) {
+ $.each(items, function(key, obj){
+ var item = $('
').data({ 'item-id': obj.id })
+ .append($('
').prop({ 'href': 'javascript:;',}).text(obj.name))
+
+ $ul.append(item)
+ })
+ }
+
+ FilterWidget.prototype.pushOptions = function(scopeName) {
+ if (!this.isActiveScopeDirty)
+ return
+
+ var $form = this.$el.closest('form'),
+ data = {
+ scopeName: scopeName,
+ options: this.scopeValues[scopeName]
+ }
+
+ $.oc.stripeLoadIndicator.show()
+ $form.request(this.options.updateHandler, {
+ data: data
+ }).always(function(){
+ $.oc.stripeLoadIndicator.hide()
+ })
+ }
+
+ FilterWidget.prototype.checkboxToggle = function(scopeName, isChecked) {
+ var $form = this.$el.closest('form'),
+ data = {
+ scopeName: scopeName,
+ value: isChecked
+ }
+
+ $.oc.stripeLoadIndicator.show()
+ $form.request(this.options.updateHandler, {
+ data: data
+ }).always(function(){
+ $.oc.stripeLoadIndicator.hide()
+ })
+ }
+
+
+ // FILTER WIDGET PLUGIN DEFINITION
+ // ============================
+
+ var old = $.fn.filterWidget
+
+ $.fn.filterWidget = function (option) {
+ var args = arguments,
+ result
+
+ this.each(function () {
+ var $this = $(this)
+ var data = $this.data('oc.filterwidget')
+ var options = $.extend({}, FilterWidget.DEFAULTS, $this.data(), typeof option == 'object' && option)
+ if (!data) $this.data('oc.filterwidget', (data = new FilterWidget(this, options)))
+ if (typeof option == 'string') result = data[option].call($this)
+ if (typeof result != 'undefined') return false
+ })
+
+ return result ? result : this
+ }
+
+ $.fn.filterWidget.Constructor = FilterWidget
+
+ // FILTER WIDGET NO CONFLICT
+ // =================
+
+ $.fn.filterWidget.noConflict = function () {
+ $.fn.filterWidget = old
+ return this
+ }
+
+ // FILTER WIDGET DATA-API
+ // ==============
+
+ $(document).render(function(){
+ $('[data-control="filterwidget"]').filterWidget();
+ })
+
+}(window.jQuery);
+
diff --git a/modules/backend/widgets/filter/partials/_filter.htm b/modules/backend/widgets/filter/partials/_filter.htm
index 552cc716a..35f867595 100644
--- a/modules/backend/widgets/filter/partials/_filter.htm
+++ b/modules/backend/widgets/filter/partials/_filter.htm
@@ -1,48 +1,9 @@
-
+
-
-
-
-
diff --git a/modules/backend/widgets/filter/partials/_filter_scopes.htm b/modules/backend/widgets/filter/partials/_filter_scopes.htm
new file mode 100644
index 000000000..95bdf2f1d
--- /dev/null
+++ b/modules/backend/widgets/filter/partials/_filter_scopes.htm
@@ -0,0 +1,3 @@
+
+ = $this->renderScopeElement($scope) ?>
+
diff --git a/modules/backend/widgets/filter/partials/_scope_checkbox.htm b/modules/backend/widgets/filter/partials/_scope_checkbox.htm
new file mode 100644
index 000000000..d9ce4fe87
--- /dev/null
+++ b/modules/backend/widgets/filter/partials/_scope_checkbox.htm
@@ -0,0 +1,7 @@
+
+
+ value ? 'checked' : '' ?> />
+ = e($scope->label) ?>
+
diff --git a/modules/backend/widgets/filter/partials/_scope_group.htm b/modules/backend/widgets/filter/partials/_scope_group.htm
new file mode 100644
index 000000000..fd3059a22
--- /dev/null
+++ b/modules/backend/widgets/filter/partials/_scope_group.htm
@@ -0,0 +1,8 @@
+
+
+ = e($scope->label) ?>:
+ = $scope->value ? count($scope->value) : 'all' ?>
+
diff --git a/modules/backend/widgets/form/partials/_field_balloon-selector.htm b/modules/backend/widgets/form/partials/_field_balloon-selector.htm
index fb38c7f32..3383261e6 100644
--- a/modules/backend/widgets/form/partials/_field_balloon-selector.htm
+++ b/modules/backend/widgets/form/partials/_field_balloon-selector.htm
@@ -5,7 +5,7 @@
attributes) ?>>
$text): ?>
- = e($text) ?>
+ = e(trans($text)) ?>
diff --git a/modules/backend/widgets/form/partials/_field_switch.htm b/modules/backend/widgets/form/partials/_field_switch.htm
index e0f4ff23c..28e8bca6d 100644
--- a/modules/backend/widgets/form/partials/_field_switch.htm
+++ b/modules/backend/widgets/form/partials/_field_switch.htm
@@ -1,6 +1,6 @@
-
= $field->label ?>
+
= e(trans($field->label)) ?>
comment): ?>
= e(trans($field->comment)) ?>
diff --git a/modules/backend/widgets/grid/partials/_button_delete.htm b/modules/backend/widgets/grid/partials/_button_delete.htm
index 5ca95b948..68dc9214c 100644
--- a/modules/backend/widgets/grid/partials/_button_delete.htm
+++ b/modules/backend/widgets/grid/partials/_button_delete.htm
@@ -2,5 +2,5 @@
href="javascript:;"
class="btn btn-sm btn-default oc-icon-minus-square"
onclick="$(this).closest('.datagrid-widget').find('[data-control=datagrid]').dataGrid('removeRow')">
- Delete Row
+ = e(trans('backend::lang.form.delete_row')) ?>
\ No newline at end of file
diff --git a/modules/backend/widgets/grid/partials/_button_insert.htm b/modules/backend/widgets/grid/partials/_button_insert.htm
index a911c4828..97af9a04b 100644
--- a/modules/backend/widgets/grid/partials/_button_insert.htm
+++ b/modules/backend/widgets/grid/partials/_button_insert.htm
@@ -2,5 +2,5 @@
href="javascript:;"
class="btn btn-sm btn-default oc-icon-plus-square"
onclick="$(this).closest('.datagrid-widget').find('[data-control=datagrid]').dataGrid('insertRow')">
- Insert Row
+ = e(trans('backend::lang.form.insert_row')) ?>
\ No newline at end of file
diff --git a/modules/backend/widgets/lists/partials/_list_pagination.htm b/modules/backend/widgets/lists/partials/_list_pagination.htm
index fa7c2a22d..9816fbd04 100644
--- a/modules/backend/widgets/lists/partials/_list_pagination.htm
+++ b/modules/backend/widgets/lists/partials/_list_pagination.htm
@@ -1,14 +1,16 @@
\ No newline at end of file
diff --git a/modules/backend/widgets/lists/partials/_setup_form.htm b/modules/backend/widgets/lists/partials/_setup_form.htm
index 09858969d..0ae7b83eb 100644
--- a/modules/backend/widgets/lists/partials/_setup_form.htm
+++ b/modules/backend/widgets/lists/partials/_setup_form.htm
@@ -1,13 +1,10 @@
= Form::open() ?>
-
- Use checkboxes to select columns you want to see in the list.
- You can change position of columns by dragging them up or down.
-
+
= e(trans('backend::lang.list.setup_help')) ?>
diff --git a/modules/backend/widgets/reportcontainer/partials/_widget.htm b/modules/backend/widgets/reportcontainer/partials/_widget.htm
index 14cfb8a1f..f345bcbfb 100644
--- a/modules/backend/widgets/reportcontainer/partials/_widget.htm
+++ b/modules/backend/widgets/reportcontainer/partials/_widget.htm
@@ -11,8 +11,8 @@
registerPermissions('October.Cms', [
- 'cms.manage_content' => ['label' => 'Manage content', 'tab' => 'Cms'],
- 'cms.manage_assets' => ['label' => 'Manage assets', 'tab' => 'Cms'],
- 'cms.manage_pages' => ['label' => 'Manage pages', 'tab' => 'Cms'],
- 'cms.manage_layouts' => ['label' => 'Manage layouts', 'tab' => 'Cms'],
- 'cms.manage_partials' => ['label' => 'Manage partials', 'tab' => 'Cms'],
- 'cms.manage_themes' => ['label' => 'Manage themes', 'tab' => 'Cms']
+ 'cms.manage_content' => ['label' => 'cms::lang.permissions.manage_content', 'tab' => 'Cms'],
+ 'cms.manage_assets' => ['label' => 'cms::lang.permissions.manage_assets', 'tab' => 'Cms'],
+ 'cms.manage_pages' => ['label' => 'cms::lang.permissions.manage_pages', 'tab' => 'Cms'],
+ 'cms.manage_layouts' => ['label' => 'cms::lang.permissions.manage_layouts', 'tab' => 'Cms'],
+ 'cms.manage_partials' => ['label' => 'cms::lang.permissions.manage_partials', 'tab' => 'Cms'],
+ 'cms.manage_themes' => ['label' => 'cms::lang.permissions.manage_themes', 'tab' => 'Cms']
]);
});
diff --git a/modules/cms/classes/ComponentBase.php b/modules/cms/classes/ComponentBase.php
index 559ab3bb1..8225d10b4 100644
--- a/modules/cms/classes/ComponentBase.php
+++ b/modules/cms/classes/ComponentBase.php
@@ -75,7 +75,7 @@ abstract class ComponentBase extends Extendable
/**
* @var array Cache of linked Component objects, used for page links.
*/
- protected $pageLinkCache = [];
+ // protected $pageLinkCache = [];
/**
* Component constructor. Takes in the page or layout code section object
diff --git a/modules/cms/classes/Controller.php b/modules/cms/classes/Controller.php
index 045fb75a6..32d8f3dc4 100644
--- a/modules/cms/classes/Controller.php
+++ b/modules/cms/classes/Controller.php
@@ -264,8 +264,18 @@ class Controller extends BaseController
$this->twig->addExtension(new SystemTwigExtension);
}
+ /**
+ * Returns the Twig environment.
+ * @return Twig_Environment
+ */
+ public function getTwig()
+ {
+ return $this->twig;
+ }
+
/**
* Initializes the custom layout and page objects.
+ * @return void
*/
protected function initCustomObjects()
{
@@ -286,6 +296,7 @@ class Controller extends BaseController
/**
* Initializes the components for the layout and page.
+ * @return void
*/
protected function initComponents()
{
diff --git a/modules/cms/lang/en/lang.php b/modules/cms/lang/en/lang.php
index c53cfa1de..21e130ddb 100644
--- a/modules/cms/lang/en/lang.php
+++ b/modules/cms/lang/en/lang.php
@@ -100,7 +100,9 @@ return [
'code' => 'Code',
'content' => 'Content',
'hidden' => 'Hidden',
- 'hidden_comment' => 'Hidden pages are accessible only by logged-in back-end users.'
+ 'hidden_comment' => 'Hidden pages are accessible only by logged-in back-end users.',
+ 'enter_fullscreen' => 'Enter fullscreen mode',
+ 'exit_fullscreen' => 'Exit fullscreen mode'
],
'asset' => [
'menu_label' => "Assets",
@@ -109,9 +111,12 @@ return [
'upload_files' => 'Upload file(s)',
'create_file' => 'Create file',
'create_directory' => 'Create directory',
+ 'directory_popup_title' => 'New directory',
+ 'directory_name' => 'Directory name',
'rename' => 'Rename',
'delete' => 'Delete',
'move' => 'Move',
+ 'select' => 'Select',
'new' => 'New file',
'rename_popup_title' => 'Rename',
'rename_new_name' => 'New name',
@@ -156,5 +161,13 @@ return [
'invalid_type' => "Unknown template type.",
'not_found' => "The requested template was not found.",
'saved'=> "The template has been successfully saved."
+ ],
+ 'permissions' => [
+ 'manage_content' => 'Manage content',
+ 'manage_assets' => 'Manage assets',
+ 'manage_pages' => 'Manage pages',
+ 'manage_layouts' => 'Manage layouts',
+ 'manage_partials' => 'Manage partials',
+ 'manage_themes' => 'Manage themes'
]
];
\ No newline at end of file
diff --git a/modules/cms/lang/it/lang.php b/modules/cms/lang/it/lang.php
new file mode 100644
index 000000000..f73a2f5c5
--- /dev/null
+++ b/modules/cms/lang/it/lang.php
@@ -0,0 +1,170 @@
+ [
+ 'invalid_file' => 'Nome file non valido: :name. I nomi dei file possono contenere solo caratteri alfanumerici, underscores, trattini e punti. Alcuni esempi di nome di file corretti: page.htm, page, subdirectory/page',
+ 'invalid_property' => 'La proprietà ":name" non può essere impostata',
+ 'file_already_exists' => 'File ":name" già esistente.',
+ 'error_saving' => 'Errore nel salvataggio del file ":name". Verifica le autorizzazioni di scrittura.',
+ 'error_creating_directory' => 'Errore nella creazione della cartella :name. Verifica le autorizzazioni di scrittura.',
+ 'invalid_file_extension'=>'Estensione del file non valida: :invalid. Le estensioni consentite sono: :allowed.',
+ 'error_deleting' => 'Errore nella cancellazione del file ":name". Verifica le autorizzazioni di scrittura.',
+ 'delete_success' => 'File eliminati correttamente: :count.',
+ 'file_name_required' => 'Il campo Nome file è obbligatorio.'
+ ],
+ 'theme' => [
+ 'active' => [
+ 'not_set' => "Il tema attivo non è impostato.",
+ 'not_found' => "Il tema attivo non è stato trovato.",
+ ],
+ 'edit' => [
+ 'not_set' => "Il tema di modifica non è impostato.",
+ 'not_found' => "Il tema di modifica non è stato trovato.",
+ 'not_match' => "L'oggetto a cui stai cercando di accedere non appartiene al tema che stai modificando. Si prega di ricaricare la pagina."
+ ],
+ 'settings_menu' => 'Tema del sito',
+ 'settings_menu_description' => 'Visualizza l\'anteprima dei temi installati e seleziona un tema attivo.',
+ 'find_more_themes' => 'Trova altri temi su OctoberCMS Theme Marketplace.',
+ 'activate_button' => 'Attiva',
+ 'active_button' => 'Attivo',
+ ],
+ 'page' => [
+ 'not_found' => [
+ 'label' => "Pagina non trovata",
+ 'help' => "La pagina richiesta non è stata trovata.",
+ ],
+ 'custom_error' => [
+ 'label' => "Errore nella pagina",
+ 'help' => "Siamo spiacenti, ma qualcosa è andato storto e la pagina non può essere visualizzata.",
+ ],
+ 'menu_label' => 'Pagine',
+ 'no_list_records' => 'Pagine non trovate',
+ 'new' => 'Nuova pagina',
+ 'invalid_url' => 'Formato URL non valido. L\'URL deve iniziare con una barra e può contenere numeri, lettere e i seguenti simboli: _-[]:?|/+*',
+ 'delete_confirm_multiple' => 'Sei sicuro di voler eliminare le pagine selezionate?',
+ 'delete_confirm_single' => 'Sei sicuro di voler eliminare questa pagina?',
+ 'no_layout' => '-- nessun layout --'
+ ],
+ 'layout' => [
+ 'not_found' => "Il layout ':name' non è stato trovato",
+ 'menu_label' => 'Layouts',
+ 'no_list_records' => 'Nessun layout trovato',
+ 'new' => 'Nuovo layout',
+ 'delete_confirm_multiple' => 'Sei sicuro di voler eliminare i layouts selezionati?',
+ 'delete_confirm_single' => 'Sei sicuro di voler eliminare questo layout?'
+ ],
+ 'partial' => [
+ 'invalid_name' => "Nome della vista parziale non valido: :name.",
+ 'not_found' => "La vista parziale ':name' non è stata trovata.",
+ 'menu_label' => 'Viste parziali',
+ 'no_list_records' => 'Nessuna vista parziale trovata',
+ 'delete_confirm_multiple' => 'Sei sicuro di voler eliminare le viste parziali selezionate?',
+ 'delete_confirm_single' => 'Sei sicuro di voler eliminare questa vista parziale?',
+ 'new' => 'Nuova vista parziale'
+ ],
+ 'content' => [
+ 'not_found' => "Il file di contenuto ':name' non è stato trovato.",
+ 'menu_label' => 'Contenuti',
+ 'no_list_records' => 'Nessun file di contenuto trovato',
+ 'delete_confirm_multiple' => 'Sei sicuro di voler eliminare i file o le cartelle di contenuti selezionate?',
+ 'delete_confirm_single' => 'Sei sicuro di voler eliminare questo file di contenuti?',
+ 'new' => 'Nuovo file di contenuti'
+ ],
+ 'ajax_handler' => [
+ 'invalid_name' => "Nome del gestore AJAX non valido: :name.",
+ 'not_found' => "Il gestore AJAX ':name' non è stato trovato.",
+ ],
+ 'combiner' => [
+ 'not_found' => "Il file combinatore ':name' non è stato trovato.",
+ ],
+ 'cms' => [
+ 'menu_label' => "CMS"
+ ],
+ 'sidebar' => [
+ 'add' => 'Aggiungi',
+ 'search' => 'Cerca...'
+ ],
+ 'editor' => [
+ 'settings' => 'Impostazioni',
+ 'title' => 'Titolo',
+ 'new_title' => 'Titolo nuova pagina',
+ 'url' => 'URL',
+ 'filename' => 'Nome file',
+ 'layout' => 'Layout',
+ 'description' => 'Descrizione',
+ 'preview' => 'Anteprima',
+ 'meta' => 'Metadati',
+ 'meta_title' => 'Meta Titolo',
+ 'meta_description' => 'Meta Descrizione',
+ 'markup' => 'Marcatore',
+ 'code' => 'Codice',
+ 'content' => 'Contenuto',
+ 'hidden' => 'Nascosto',
+ 'hidden_comment' => 'Le pagine nascoste sono accessibili sono dagli utenti registrati.',
+ 'enter_fullscreen' => 'Visualizza a schermo intero',
+ 'exit_fullscreen' => 'Esci dalla visualizzazione a schermo intero'
+ ],
+ 'asset' => [
+ 'menu_label' => "Assets",
+ 'drop_down_add_title' => 'Agiungi...',
+ 'drop_down_operation_title' => 'Azioni...',
+ 'upload_files' => 'Carica file(s)',
+ 'create_file' => 'Crea file',
+ 'create_directory' => 'Crea cartella',
+ 'rename' => 'Rinomina',
+ 'delete' => 'Elimina',
+ 'move' => 'Sposta',
+ 'new' => 'Nuovo file',
+ 'rename_popup_title' => 'Rinomina',
+ 'rename_new_name' => 'Nuovo nome',
+ 'invalid_path' => 'Il percorso può contenere solo numeri, lettere, spazi e i simboli seguenti: ._-/',
+ 'error_deleting_file' => 'Errore durante l\'eliminazione del file :name.',
+ 'error_deleting_dir_not_empty' => 'Errore durante l\'eliminazione della cartella :name. La cartella non è vuota.',
+ 'error_deleting_dir' => 'Errore durante l\'eliminazinoe della cartella :name.',
+ 'invalid_name' => 'Il nome può contenere solo numeri, lettere, spazi e i simboli seguenti: ._-',
+ 'original_not_found' => 'Il file o la cartella originali non sono stati trovati',
+ 'already_exists' => 'Un file o cartella con questo nome è già esistente',
+ 'error_renaming' => 'Errore nella rinominazione del file o della cartella',
+ 'name_cant_be_empty' => 'Il nome non può essere vuoto',
+ 'too_large' => 'Il file caricato è troppo grande. La dimensione massima consentita è :max_size',
+ 'type_not_allowed' => 'Solo i seguenti tipi di file sono consentiti: :allowed_types',
+ 'file_not_valid' => 'File non valido',
+ 'error_uploading_file' => 'Errore durante il caricamento del file ":name": :error',
+ 'move_please_select' => 'Seleziona',
+ 'move_destination' => 'Cartella di destinazione',
+ 'move_popup_title' => 'Sposta assets',
+ 'move_button' => 'Sposta',
+ 'selected_files_not_found' => 'Files selezionati non trovati.',
+ 'select_destination_dir' => 'Seleziona una cartella di destinazione',
+ 'destination_not_found' => 'Cartella di destinazione non trovata',
+ 'error_moving_file' => 'Errore durante lo spostamento del file :file',
+ 'error_moving_directory' => 'Errore durante lo spostamento della cartella :dir',
+ 'error_deleting_directory' => 'Errore durante l\'eliminazione della cartella originale :dir',
+ 'path' => 'Percorso'
+ ],
+ 'component' => [
+ 'menu_label' => "Componenti",
+ 'unnamed' => "Senza nome",
+ 'no_description' => "Nessuna descrizione fornita",
+ 'alias' => "Alias",
+ 'alias_description' => "Un nome univoco fornito a questo componente quando utilizzato nella pagina o nel layout.",
+ 'validation_message' => "L'alias del componente è obbligatorio e può contenere solo lettere, numeri e underscores. L'alias deve iniziare con una lettera.",
+ 'invalid_request' => "Il modello non può essere salvato a causa di dati dei componenti non validi.",
+ 'no_records' => 'Nessun componente trovato',
+ 'not_found' => "Il componente ':name' non è stato trovato.",
+ 'method_not_found' => "Il componente ':name' non contiene il metodo ':method'.",
+ ],
+ 'template' => [
+ 'invalid_type' => "Tipo di modello non valido.",
+ 'not_found' => "Il modello richiesto non è stato trovato.",
+ 'saved'=> "Il modello è stato salvato con successo."
+ ],
+ 'permissions' => [
+ 'manage_content' => 'Gestisci contenuti',
+ 'manage_assets' => 'Gestisci assets',
+ 'manage_pages' => 'Gestisci pagine',
+ 'manage_layouts' => 'Gesstisci layouts',
+ 'manage_partials' => 'Gestisci viste parziali',
+ 'manage_themes' => 'Gestisci temi'
+ ]
+];
diff --git a/modules/cms/lang/ru/lang.php b/modules/cms/lang/ru/lang.php
index ef00f4d44..510657d18 100644
--- a/modules/cms/lang/ru/lang.php
+++ b/modules/cms/lang/ru/lang.php
@@ -5,10 +5,10 @@ return [
'invalid_file' => "Ошибка в имени файла: :name. Имена файлов могут содержать только латинские буквы, цифры, знаки подчеркивания и точки. Пример правильных имен файлов: page.htm, page, subdirectory/page",
'invalid_property' => 'Параметр ":name" нельзя изменить.',
'file_already_exists' => 'Файл ":name" уже существует.',
- 'error_saving' => 'Ошибка сохранения файла ":name".',
- 'error_creating_directory' => 'Ошибка создания директории :name',
+ 'error_saving' => 'Ошибка сохранения файла ":name". Пожалуйста, проверьте права на запись.',
+ 'error_creating_directory' => 'Ошибка создания директории :name. Пожалуйста, проверьте права на запись.',
'invalid_file_extension'=>'Указано неправильное расширение файла: :invalid. Разрешенные расширения: :allowed.',
- 'error_deleting' => 'Невозможно удалить файл шаблона :name.',
+ 'error_deleting' => 'Невозможно удалить файл шаблона :name. Пожалуйста, проверьте права на запись.',
'delete_success' => 'Шаблоны были успешно удалены: :count.',
'file_name_required' => 'Пожалуйста, укажите имя файла шаблона.'
],
diff --git a/modules/cms/widgets/AssetList.php b/modules/cms/widgets/AssetList.php
index 97c5c4917..e9eb20127 100644
--- a/modules/cms/widgets/AssetList.php
+++ b/modules/cms/widgets/AssetList.php
@@ -640,7 +640,7 @@ class AssetList extends WidgetBase
if ($this->pathMatchesSearch($words, $path)) {
$result[] = (object)[
'type'=>'file',
- 'path'=>$path,
+ 'path'=>File::normalizePath($path),
'name'=>$item->getFilename(),
'editable'=>in_array(strtolower($item->getExtension()), $editableAssetTypes)
];
@@ -664,4 +664,4 @@ class AssetList extends WidgetBase
return true;
}
-}
\ No newline at end of file
+}
diff --git a/modules/cms/widgets/assetlist/partials/_items.htm b/modules/cms/widgets/assetlist/partials/_items.htm
index 09c6d9359..68d426b9c 100644
--- a/modules/cms/widgets/assetlist/partials/_items.htm
+++ b/modules/cms/widgets/assetlist/partials/_items.htm
@@ -1,7 +1,7 @@
-isSearchMode();
- if (($upPath = $this->getUpPath()) !== null && !$searchMode):
+ if (($upPath = $this->getUpPath()) !== null && !$searchMode):
?>
= $this->getCurrentRelativePath() ?>
@@ -25,34 +25,34 @@
-
Rename
+ >= e(trans('cms::lang.asset.rename')) ?>
path) ?>
isFileSelected($item) ? 'checked' : null ?>
data-request="= $this->getEventHandler('onSelect') ?>"
value="1">
- Select
+ = e(trans('cms::lang.asset.select')) ?>
-
= trans($this->noRecordsMessage) ?>
+
= e(trans($this->noRecordsMessage)) ?>
diff --git a/modules/cms/widgets/assetlist/partials/_move_form.htm b/modules/cms/widgets/assetlist/partials/_move_form.htm
index 2526b96f3..85e91d7b4 100644
--- a/modules/cms/widgets/assetlist/partials/_move_form.htm
+++ b/modules/cms/widgets/assetlist/partials/_move_form.htm
@@ -6,12 +6,15 @@
]) ?>
- = trans('cms::lang.asset.move_destination') ?>
-
+ = e(trans('cms::lang.asset.move_destination')) ?>
+
$directory):?>
= e($directory) ?>
@@ -23,17 +26,16 @@
= Form::close() ?>
\ No newline at end of file
diff --git a/modules/cms/widgets/assetlist/partials/_new_dir_form.htm b/modules/cms/widgets/assetlist/partials/_new_dir_form.htm
index 3b73cac53..e1f3c1f56 100644
--- a/modules/cms/widgets/assetlist/partials/_new_dir_form.htm
+++ b/modules/cms/widgets/assetlist/partials/_new_dir_form.htm
@@ -6,28 +6,27 @@
]) ?>