From 106756d656072178feadcbe4a5472dc2821940b0 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Fri, 22 Nov 2019 18:39:25 -0600 Subject: [PATCH 01/88] Fixed conflict between JS input trigger plugin & JS filter plugin. Related: https://github.com/octobercms/october/issues/3202#issuecomment-556042766 --- modules/system/assets/ui/js/filter.dates.js | 12 +++---- modules/system/assets/ui/js/filter.js | 8 ++--- modules/system/assets/ui/js/filter.numbers.js | 12 +++---- modules/system/assets/ui/storm-min.js | 32 +++++++++---------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/modules/system/assets/ui/js/filter.dates.js b/modules/system/assets/ui/js/filter.dates.js index e6d12aa07..2d5750746 100644 --- a/modules/system/assets/ui/js/filter.dates.js +++ b/modules/system/assets/ui/js/filter.dates.js @@ -43,14 +43,14 @@ this.$el.on('show.oc.popover', 'a.filter-scope-date', function (event) { self.initDatePickers($(this).hasClass('range')) - $(event.relatedTarget).on('click', '#controlFilterPopoverDate [data-trigger="filter"]', function (e) { + $(event.relatedTarget).on('click', '#controlFilterPopoverDate [data-filter-action="filter"]', function (e) { e.preventDefault() e.stopPropagation() self.filterByDate() }) - $(event.relatedTarget).on('click', '#controlFilterPopoverDate [data-trigger="clear"]', function (e) { + $(event.relatedTarget).on('click', '#controlFilterPopoverDate [data-filter-action="clear"]', function (e) { e.preventDefault() e.stopPropagation() @@ -121,7 +121,7 @@ \ \
\ - \
\ @@ -159,16 +159,16 @@ type="text" \ name="date" \ value="{{ date }}" \ - class="form-control align-right popup-allow-focus" \ + class="form-control align-right popup-allow-focus" \ autocomplete="off" \ placeholder="{{ before_placeholder }}" /> \ \ \
\ - \ - \
\ diff --git a/modules/system/assets/ui/js/filter.js b/modules/system/assets/ui/js/filter.js index 543575e9a..fa71d983c 100644 --- a/modules/system/assets/ui/js/filter.js +++ b/modules/system/assets/ui/js/filter.js @@ -78,10 +78,10 @@ \ \
\ - \ - \
\ @@ -146,12 +146,12 @@ self.filterAvailable(data.scopeName, data.options.available) }) - $(event.relatedTarget).on('click', '#controlFilterPopover [data-trigger="apply"]', function (e) { + $(event.relatedTarget).on('click', '#controlFilterPopover [data-filter-action="apply"]', function (e) { e.preventDefault() self.filterScope() }) - $(event.relatedTarget).on('click', '#controlFilterPopover [data-trigger="clear"]', function (e) { + $(event.relatedTarget).on('click', '#controlFilterPopover [data-filter-action="clear"]', function (e) { e.preventDefault() self.filterScope(true) }) diff --git a/modules/system/assets/ui/js/filter.numbers.js b/modules/system/assets/ui/js/filter.numbers.js index a57484c19..1a2cb071f 100644 --- a/modules/system/assets/ui/js/filter.numbers.js +++ b/modules/system/assets/ui/js/filter.numbers.js @@ -42,13 +42,13 @@ this.$el.on('show.oc.popover', 'a.filter-scope-number', function (event) { self.initNumberInputs($(this).hasClass('range')) - $(event.relatedTarget).on('click', '#controlFilterPopoverNum [data-trigger="filter"]', function (e) { + $(event.relatedTarget).on('click', '#controlFilterPopoverNum [data-filter-action="filter"]', function (e) { e.preventDefault() e.stopPropagation() self.filterByNumber() }) - $(event.relatedTarget).on('click', '#controlFilterPopoverNum [data-trigger="clear"]', function (e) { + $(event.relatedTarget).on('click', '#controlFilterPopoverNum [data-filter-action="clear"]', function (e) { e.preventDefault() e.stopPropagation() @@ -111,10 +111,10 @@ placeholder="{{ number_placeholder }}" /> \ \
\ - \ - \
\ @@ -157,10 +157,10 @@ \ \
\ - \ - \
\ diff --git a/modules/system/assets/ui/storm-min.js b/modules/system/assets/ui/storm-min.js index 23f2c4ebe..a3881f428 100644 --- a/modules/system/assets/ui/storm-min.js +++ b/modules/system/assets/ui/storm-min.js @@ -3083,10 +3083,10 @@ FilterWidget.prototype.getPopoverTemplate=function(){return' \ \
\ - \ - \
\ @@ -3111,9 +3111,9 @@ this.$el.on('show.oc.popover','a.filter-scope',function(event){self.focusSearch( $(event.relatedTarget).on('click','#controlFilterPopover .filter-items > ul > li',function(){self.selectItem($(this))}) $(event.relatedTarget).on('click','#controlFilterPopover .filter-active-items > ul > li',function(){self.selectItem($(this),true)}) $(event.relatedTarget).on('ajaxDone','#controlFilterPopover input.filter-search-input',function(event,context,data){self.filterAvailable(data.scopeName,data.options.available)}) -$(event.relatedTarget).on('click','#controlFilterPopover [data-trigger="apply"]',function(e){e.preventDefault() +$(event.relatedTarget).on('click','#controlFilterPopover [data-filter-action="apply"]',function(e){e.preventDefault() self.filterScope()}) -$(event.relatedTarget).on('click','#controlFilterPopover [data-trigger="clear"]',function(e){e.preventDefault() +$(event.relatedTarget).on('click','#controlFilterPopover [data-filter-action="clear"]',function(e){e.preventDefault() self.filterScope(true)})}) this.$el.on('hide.oc.popover','a.filter-scope',function(){var $scope=$(this) self.pushOptions(self.activeScopeName) @@ -3246,10 +3246,10 @@ this.initRegion() this.initFilterDate()} FilterWidget.prototype.initFilterDate=function(){var self=this this.$el.on('show.oc.popover','a.filter-scope-date',function(event){self.initDatePickers($(this).hasClass('range')) -$(event.relatedTarget).on('click','#controlFilterPopoverDate [data-trigger="filter"]',function(e){e.preventDefault() +$(event.relatedTarget).on('click','#controlFilterPopoverDate [data-filter-action="filter"]',function(e){e.preventDefault() e.stopPropagation() self.filterByDate()}) -$(event.relatedTarget).on('click','#controlFilterPopoverDate [data-trigger="clear"]',function(e){e.preventDefault() +$(event.relatedTarget).on('click','#controlFilterPopoverDate [data-filter-action="clear"]',function(e){e.preventDefault() e.stopPropagation() self.filterByDate(true)})}) this.$el.on('hiding.oc.popover','a.filter-scope-date',function(){self.clearDatePickers()}) @@ -3285,7 +3285,7 @@ FilterWidget.prototype.getPopoverDateTemplate=function(){return' \ \
\ - \
\ @@ -3317,16 +3317,16 @@ FilterWidget.prototype.getPopoverRangeTemplate=function(){return' type="text" \ name="date" \ value="{{ date }}" \ - class="form-control align-right popup-allow-focus" \ + class="form-control align-right popup-allow-focus" \ autocomplete="off" \ placeholder="{{ before_placeholder }}" /> \ \ \
\ - \ - \
\ @@ -3381,10 +3381,10 @@ if(!this.timezone){this.timezone='UTC'}}}(window.jQuery);+function($){"use stric this.initFilterNumber()} FilterWidget.prototype.initFilterNumber=function(){var self=this this.$el.on('show.oc.popover','a.filter-scope-number',function(event){self.initNumberInputs($(this).hasClass('range')) -$(event.relatedTarget).on('click','#controlFilterPopoverNum [data-trigger="filter"]',function(e){e.preventDefault() +$(event.relatedTarget).on('click','#controlFilterPopoverNum [data-filter-action="filter"]',function(e){e.preventDefault() e.stopPropagation() self.filterByNumber()}) -$(event.relatedTarget).on('click','#controlFilterPopoverNum [data-trigger="clear"]',function(e){e.preventDefault() +$(event.relatedTarget).on('click','#controlFilterPopoverNum [data-filter-action="clear"]',function(e){e.preventDefault() e.stopPropagation() self.filterByNumber(true)})}) this.$el.on('hide.oc.popover','a.filter-scope-number',function(){var $scope=$(this) @@ -3416,10 +3416,10 @@ FilterWidget.prototype.getPopoverNumberTemplate=function(){return' placeholder="{{ number_placeholder }}" /> \ \
\ - \ - \
\ @@ -3456,10 +3456,10 @@ FilterWidget.prototype.getPopoverNumberRangeTemplate=function(){return' \ \
\ - \ - \
\ From 992e84e602b92ce96d2d4ec2171d8512715e572a Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 25 Nov 2019 00:59:00 -0500 Subject: [PATCH 02/88] Add missing documentation comment blocks for fired events (#4788) Credit to @mjauvin. --- modules/backend/classes/Controller.php | 20 ++++++++++++-- modules/backend/classes/NavigationManager.php | 14 ++++++++-- modules/backend/models/User.php | 12 +++++++++ modules/backend/routes.php | 25 ++++++++++++++--- modules/cms/routes.php | 25 ++++++++++++++--- modules/cms/twig/Extension.php | 13 +++++++++ modules/system/classes/CombineAssets.php | 27 +++++++++++++++---- modules/system/classes/SettingsManager.php | 13 +++++++-- modules/system/traits/ConfigMaker.php | 15 +++++++++-- 9 files changed, 143 insertions(+), 21 deletions(-) diff --git a/modules/backend/classes/Controller.php b/modules/backend/classes/Controller.php index d019150cb..01ac494f5 100644 --- a/modules/backend/classes/Controller.php +++ b/modules/backend/classes/Controller.php @@ -242,8 +242,24 @@ class Controller extends ControllerBase } } - /* - * Extensibility + /** + * @event backend.page.beforeDisplay + * Provides an opportunity to override backend page content + * + * Example usage: + * + * Event::listen('backend.page.beforeDisplay', function ((\Backend\Classes\Controller) $backendController, (string) $action, (array) $params) { + * trace_log('redirect all backend pages to google'); + * return \Redirect::to('https://google.com'); + * }); + * + * Or + * + * $backendController->bindEvent('page.beforeDisplay', function ((string) $action, (array) $params) { + * trace_log('redirect all backend pages to google'); + * return \Redirect::to('https://google.com'); + * }); + * */ if ($event = $this->fireSystemEvent('backend.page.beforeDisplay', [$action, $params])) { return $event; diff --git a/modules/backend/classes/NavigationManager.php b/modules/backend/classes/NavigationManager.php index d91915a40..59003f7f0 100644 --- a/modules/backend/classes/NavigationManager.php +++ b/modules/backend/classes/NavigationManager.php @@ -100,8 +100,18 @@ class NavigationManager $this->registerMenuItems($id, $items); } - /* - * Extensibility + /** + * @event backend.menu.extendItems + * Provides an opportunity to manipulate the backend navigation + * + * Example usage: + * + * Event::listen('backend.menu.extendItems', function ((\Backend\Classes\NavigationManager) $navigationManager) { + * $navigationManager->addMainMenuItems(...) + * $navigationManager->addSideMenuItems(...) + * $navigationManager->removeMainMenuItem(...) + * }); + * */ Event::fire('backend.menu.extendItems', [$this]); diff --git a/modules/backend/models/User.php b/modules/backend/models/User.php index f527f58ca..f2b54442c 100644 --- a/modules/backend/models/User.php +++ b/modules/backend/models/User.php @@ -136,6 +136,18 @@ class User extends UserBase public function afterLogin() { parent::afterLogin(); + + /** + * @event backend.user.login + * Provides an opportunity to interact with the Backend User model after the user has logged in + * + * Example usage: + * + * Event::listen('backend.user.login', function ((\Backend\Models\User) $user) { + * Flash::success(sprintf('Welcome %s!', $user->getFullNameAttribute())); + * }); + * + */ Event::fire('backend.user.login', [$this]); } diff --git a/modules/backend/routes.php b/modules/backend/routes.php index c89ff91a4..81904802f 100644 --- a/modules/backend/routes.php +++ b/modules/backend/routes.php @@ -4,8 +4,17 @@ * Register Backend routes before all user routes. */ App::before(function ($request) { - /* - * Extensibility + + /** + * @event backend.beforeRoute + * Fires before backend routes get added + * + * Example usage: + * + * Event::listen('backend.beforeRoute', function () { + * // your code here + * }); + * */ Event::fire('backend.beforeRoute'); @@ -25,8 +34,16 @@ App::before(function ($request) { */ Route::any(Config::get('cms.backendUri', 'backend'), 'Backend\Classes\BackendController@run')->middleware('web'); - /* - * Extensibility + /** + * @event backend.route + * Fires after backend routes have been added + * + * Example usage: + * + * Event::listen('backend.route', function () { + * // your code here + * }); + * */ Event::fire('backend.route'); }); diff --git a/modules/cms/routes.php b/modules/cms/routes.php index 9826a6dec..76f35c4b3 100644 --- a/modules/cms/routes.php +++ b/modules/cms/routes.php @@ -4,8 +4,17 @@ * Register CMS routes before all user routes. */ App::before(function ($request) { - /* - * Extensibility + + /** + * @event cms.beforeRoute + * Fires before cms routes get added + * + * Example usage: + * + * Event::listen('cms.beforeRoute', function () { + * // your code here + * }); + * */ Event::fire('cms.beforeRoute'); @@ -15,8 +24,16 @@ App::before(function ($request) { */ Route::any('{slug}', 'Cms\Classes\CmsController@run')->where('slug', '(.*)?')->middleware('web'); - /* - * Extensibility + /** + * @event cms.route + * Fires after cms routes get added + * + * Example usage: + * + * Event::listen('cms.route', function () { + * // your code here + * }); + * */ Event::fire('cms.route'); }); diff --git a/modules/cms/twig/Extension.php b/modules/cms/twig/Extension.php index 8dfc67de5..af95e6b0b 100644 --- a/modules/cms/twig/Extension.php +++ b/modules/cms/twig/Extension.php @@ -193,6 +193,19 @@ class Extension extends TwigExtension return $default; } + /** + * @event cms.block.render + * Provides an opportunity to modify the rendered block content + * + * Example usage: + * + * Event::listen('cms.block.render', function ((string) $name, (string) $result) { + * if ($name === 'myBlockName') { + * return 'my custom content'; + * } + * }); + * + */ if ($event = Event::fire('cms.block.render', [$name, $result], true)) { $result = $event; } diff --git a/modules/system/classes/CombineAssets.php b/modules/system/classes/CombineAssets.php index 79644123e..bc5d1d6de 100644 --- a/modules/system/classes/CombineAssets.php +++ b/modules/system/classes/CombineAssets.php @@ -411,8 +411,16 @@ class CombineAssets */ protected function prepareCombiner(array $assets, $rewritePath = null) { - /* - * Extensibility + /** + * @event cms.combiner.beforePrepare + * Provides an opportunity to interact with the asset combiner before assets are combined + * + * Example usage: + * + * Event::listen('cms.combiner.beforePrepare', function ((\System\Classes\CombineAssets) $assetCombiner, (array) $assets) { + * $assetCombiner->registerFilter(...) + * }); + * */ Event::fire('cms.combiner.beforePrepare', [$this, $assets]); @@ -809,10 +817,19 @@ class CombineAssets $cacheKey .= $this->getDeepHashFromAssets($assets); } - /* - * Extensibility - */ $dataHolder = (object) ['key' => $cacheKey]; + + /** + * @event cms.combiner.getCacheKey + * Provides an opportunity to modify the asset combiner's cache key + * + * Example usage: + * + * Event::listen('cms.combiner.getCacheKey', function ((\System\Classes\CombineAssets) $assetCombiner, (stdClass) $dataHolder) { + * $dataHolder->key = rand(); + * }); + * + */ Event::fire('cms.combiner.getCacheKey', [$this, $dataHolder]); $cacheKey = $dataHolder->key; diff --git a/modules/system/classes/SettingsManager.php b/modules/system/classes/SettingsManager.php index eee21607b..67873a78e 100644 --- a/modules/system/classes/SettingsManager.php +++ b/modules/system/classes/SettingsManager.php @@ -109,8 +109,17 @@ class SettingsManager $this->registerSettingItems($id, $items); } - /* - * Extensibility + /** + * @event system.settings.extendItems + * Provides an opportunity to manipulate the system settings manager + * + * Example usage: + * + * Event::listen('system.settings.extendItems', function ((\System\Classes\SettingsManager) $settingsManager) { + * $settingsManager->addSettingItem(...) + * $settingsManager->removeSettingItem(...) + * }); + * */ Event::fire('system.settings.extendItems', [$this]); diff --git a/modules/system/traits/ConfigMaker.php b/modules/system/traits/ConfigMaker.php index 9a78be8c3..12fc45e68 100644 --- a/modules/system/traits/ConfigMaker.php +++ b/modules/system/traits/ConfigMaker.php @@ -67,8 +67,19 @@ trait ConfigMaker $config = Yaml::parseFile($configFile); - /* - * Extensibility + /** + * @event system.extendConfigFile + * Provides an opportunity to modify config files + * + * Example usage: + * + * Event::listen('system.extendConfigFile', function ((string) $path, (array) $config) { + * if ($path === '/plugins/author/plugin-name/controllers/mycontroller/config_relation.yaml') { + * unset($config['property_value']['view']['recordUrl']); + * return $config; + * } + * }); + * */ $publicFile = File::localToPublic($configFile); if ($results = Event::fire('system.extendConfigFile', [$publicFile, $config])) { From 18398a8318ac6e03af9ae28c33a6b5996d363f83 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Mon, 25 Nov 2019 16:58:39 +1100 Subject: [PATCH 03/88] Add touch events back to modernizer --- modules/system/assets/ui/vendor/modernizr/modernizr.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/modules/system/assets/ui/vendor/modernizr/modernizr.js b/modules/system/assets/ui/vendor/modernizr/modernizr.js index 60edf6d9d..0db1bd6c6 100644 --- a/modules/system/assets/ui/vendor/modernizr/modernizr.js +++ b/modules/system/assets/ui/vendor/modernizr/modernizr.js @@ -1,5 +1,3 @@ -/*! - * modernizr v3.7.1 - * Build https://modernizr.com/download?-applicationcache-audio-audioloop-backgroundsize-bgsizecover-borderimage-borderradius-boxshadow-boxsizing-canvas-canvastext-cssanimations-csscalc-csscolumns-cssgradients-cssgrid_cssgridlegacy-cssreflections-csstransforms-csstransforms3d-csstransformslevel2-csstransitions-cssvhunit-cssvmaxunit-cssvminunit-cssvwunit-flexbox-flexboxlegacy-flexboxtweener-flexwrap-fontdisplay-fontface-forcetouch-generatedcontent-geolocation-hashchange-history-hsla-indexeddb-indexeddbblob-inlinesvg-input-inputformaction-inputformenctype-inputformmethod-inputformnovalidate-inputformtarget-inputsearchevent-inputtypes-localstorage-multiplebgs-opacity-postmessage-preserve3d-rgba-sessionstorage-smil-srcset-svg-svgasimg-svgclippaths-svgfilters-svgforeignobject-textshadow-touchevents-video-videocrossorigin-videoloop-videopreload-webaudio-webgl-webglextensions-websockets-websocketsbinary-websqldatabase-webworkers-domprefixes-hasevent-mq-prefixes-printshiv-setclasses-testallprops-testprop-teststyles-dontmin -*/ -!function(f,u,p){var s=[],e={_version:"3.7.1",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,t){var n=this;setTimeout(function(){t(n[e])},0)},addTest:function(e,t,n){s.push({name:e,fn:t,options:n})},addAsyncTest:function(e){s.push({name:null,fn:e})}},c=function(){};c.prototype=e,c=new c;var d=[];function m(e,t){return typeof e===t}var h=u.documentElement,g="svg"===h.nodeName.toLowerCase();function a(e){var t=h.className,n=c._config.classPrefix||"";if(g&&(t=t.baseVal),c._config.enableJSClass){var r=new RegExp("(^|\\s)"+n+"no-js(\\s|$)");t=t.replace(r,"$1"+n+"js$2")}c._config.enableClasses&&(0",r.insertBefore(n.lastChild,r.firstChild)}function f(){var e=h.elements;return"string"==typeof e?e.split(" "):e}function p(e){var t=c[e[r]];return t||(t={},d++,e[r]=d,c[d]=t),t}function l(e,t,n){return t||(t=i),s?t.createElement(e):(n||(n=p(t)),!(r=n.cache[e]?n.cache[e].cloneNode():a.test(e)?(n.cache[e]=n.createElem(e)).cloneNode():n.createElem(e)).canHaveChildren||o.test(e)||r.tagUrn?r:n.frag.appendChild(r));var r}function m(e){e||(e=i);var t=p(e);return!h.shivCSS||n||t.hasCSS||(t.hasCSS=!!u(e,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),s||function(t,n){n.cache||(n.cache={},n.createElem=t.createElement,n.createFrag=t.createDocumentFragment,n.frag=n.createFrag()),t.createElement=function(e){return h.shivMethods?l(e,t,n):n.createElem(e)},t.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+f().join().replace(/[\w\-:]+/g,function(e){return n.createElem(e),n.frag.createElement(e),'c("'+e+'")'})+");return n}")(h,n.frag)}(e,t),e}!function(){try{var e=i.createElement("a");e.innerHTML="",n="hidden"in e,s=1==e.childNodes.length||function(){i.createElement("a");var e=i.createDocumentFragment();return void 0===e.cloneNode||void 0===e.createDocumentFragment||void 0===e.createElement}()}catch(e){s=n=!0}}();var h={elements:t.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:"3.7.3",shivCSS:!1!==t.shivCSS,supportsUnknownElements:s,shivMethods:!1!==t.shivMethods,type:"default",shivDocument:m,createElement:l,createDocumentFragment:function(e,t){if(e||(e=i),s)return e.createDocumentFragment();for(var n=(t=t||p(e)).frag.cloneNode(),r=0,o=f(),a=o.length;r+~])("+f().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),a="$1"+y+"\\:$2";r--;)(t=n[r]=n[r].split("}"))[t.length-1]=t[t.length-1].replace(o,a),n[r]=t.join("}");return n.join("{")}(o.reverse().join("")),c=function(e){for(var t,n=e.getElementsByTagName("*"),r=n.length,o=RegExp("^(?:"+f().join("|")+")$","i"),a=[];r--;)t=n[r],o.test(t.nodeName)&&a.push(t.applyElement(T(t)));return a}(s),d=u(s,o)}),n.attachEvent("onafterprint",function(){!function(e){for(var t=e.length;t--;)e[t].removeNode()}(c),clearTimeout(e._removeSheetTimer),e._removeSheetTimer=setTimeout(l,500)}),s.printShived=!0,s}h.type+=" print",(h.shivPrint=x)(i),"object"==typeof module&&module.exports&&(module.exports=h)}(void 0!==f?f:this,u);var T=e._config.usePrefixes?t.split(" "):[];function x(e,t){return!!~(""+e).indexOf(t)}e._cssomPrefixes=T;var w={elem:v("modernizr")};c._q.push(function(){delete w.elem});var S={style:w.elem.style};function C(e){return e.replace(/([A-Z])/g,function(e,t){return"-"+t.toLowerCase()}).replace(/^ms-/,"-ms-")}function E(e,t,n){var r;if("getComputedStyle"in f){r=getComputedStyle.call(f,e,t);var o=f.console;if(null!==r)n&&(r=r.getPropertyValue(n));else if(o)o[o.error?"error":"log"].call(o,"getComputedStyle returning null, its possible modernizr test results are inaccurate")}else r=!t&&e.currentStyle&&e.currentStyle[n];return r}function k(e){return e.replace(/([a-z])-([a-z])/g,function(e,t,n){return t+n.toUpperCase()}).replace(/^-/,"")}function _(e,t,n,r){if(r=!m(r,"undefined")&&r,!m(n,"undefined")){var o=function(e,t){var n=e.length;if("CSS"in f&&"supports"in f.CSS){for(;n--;)if(f.CSS.supports(C(e[n]),t))return!0;return!1}if("CSSSupportsRule"in f){for(var r=[];n--;)r.push("("+C(e[n])+":"+t+")");return y("@supports ("+(r=r.join(" or "))+") { #modernizr { position: absolute; } }",function(e){return"absolute"===E(e,null,"position")})}return p}(e,n);if(!m(o,"undefined"))return o}for(var a,i,s,d,c,l=["modernizr","tspan","samp"];!S.style&&l.length;)a=!0,S.modElem=v(l.shift()),S.style=S.modElem.style;function u(){a&&(delete S.style,delete S.modElem)}for(s=e.length,i=0;if;f++)if(m=e[f],g=G.style[m],s(m,"-")&&(m=d(m)),G.style[m]!==n){if(a||r(o,"undefined"))return c(),"pfx"==t?m:!0;try{G.style[m]=o}catch(y){}if(G.style[m]!=g)return c(),"pfx"==t?m:!0}return c(),!1}function y(e,t,n,o,a){var i=e.charAt(0).toUpperCase()+e.slice(1),s=(e+" "+U.join(i+" ")+i).split(" ");return r(t,"string")||r(t,"undefined")?v(s,t,o,a):(s=(e+" "+O.join(i+" ")+i).split(" "),p(s,t,n))}function b(e,t,r){return y(e,n,n,t,r)}function T(e,t){var n=e.deleteDatabase(t);n.onsuccess=function(){u("indexeddb.deletedatabase",!0)},n.onerror=function(){u("indexeddb.deletedatabase",!1)}}var x=[],w=[],S={_version:"3.6.0",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,t){var n=this;setTimeout(function(){t(n[e])},0)},addTest:function(e,t,n){w.push({name:e,fn:t,options:n})},addAsyncTest:function(e){w.push({name:null,fn:e})}},Modernizr=function(){};Modernizr.prototype=S,Modernizr=new Modernizr,Modernizr.addTest("applicationcache","applicationCache"in e),Modernizr.addTest("geolocation","geolocation"in navigator),Modernizr.addTest("history",function(){var t=navigator.userAgent;return-1===t.indexOf("Android 2.")&&-1===t.indexOf("Android 4.0")||-1===t.indexOf("Mobile Safari")||-1!==t.indexOf("Chrome")||-1!==t.indexOf("Windows Phone")||"file:"===location.protocol?e.history&&"pushState"in e.history:!1}),Modernizr.addTest("postmessage","postMessage"in e),Modernizr.addTest("svg",!!t.createElementNS&&!!t.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect);var C=!1;try{C="WebSocket"in e&&2===e.WebSocket.CLOSING}catch(E){}Modernizr.addTest("websockets",C),Modernizr.addTest("localstorage",function(){var e="modernizr";try{return localStorage.setItem(e,e),localStorage.removeItem(e),!0}catch(t){return!1}}),Modernizr.addTest("sessionstorage",function(){var e="modernizr";try{return sessionStorage.setItem(e,e),sessionStorage.removeItem(e),!0}catch(t){return!1}}),Modernizr.addTest("websqldatabase","openDatabase"in e),Modernizr.addTest("webworkers","Worker"in e);var _=S._config.usePrefixes?" -webkit- -moz- -o- -ms- ".split(" "):["",""];S._prefixes=_;var k=t.documentElement,P="svg"===k.nodeName.toLowerCase();P||!function(e,t){function n(e,t){var n=e.createElement("p"),r=e.getElementsByTagName("head")[0]||e.documentElement;return n.innerHTML="x",r.insertBefore(n.lastChild,r.firstChild)}function r(){var e=b.elements;return"string"==typeof e?e.split(" "):e}function o(e,t){var n=b.elements;"string"!=typeof n&&(n=n.join(" ")),"string"!=typeof e&&(e=e.join(" ")),b.elements=n+" "+e,l(t)}function a(e){var t=y[e[h]];return t||(t={},v++,e[h]=v,y[v]=t),t}function i(e,n,r){if(n||(n=t),u)return n.createElement(e);r||(r=a(n));var o;return o=r.cache[e]?r.cache[e].cloneNode():g.test(e)?(r.cache[e]=r.createElem(e)).cloneNode():r.createElem(e),!o.canHaveChildren||m.test(e)||o.tagUrn?o:r.frag.appendChild(o)}function s(e,n){if(e||(e=t),u)return e.createDocumentFragment();n=n||a(e);for(var o=n.frag.cloneNode(),i=0,s=r(),c=s.length;c>i;i++)o.createElement(s[i]);return o}function c(e,t){t.cache||(t.cache={},t.createElem=e.createElement,t.createFrag=e.createDocumentFragment,t.frag=t.createFrag()),e.createElement=function(n){return b.shivMethods?i(n,e,t):t.createElem(n)},e.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+r().join().replace(/[\w\-:]+/g,function(e){return t.createElem(e),t.frag.createElement(e),'c("'+e+'")'})+");return n}")(b,t.frag)}function l(e){e||(e=t);var r=a(e);return!b.shivCSS||d||r.hasCSS||(r.hasCSS=!!n(e,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),u||c(e,r),e}var d,u,f="3.7.3",p=e.html5||{},m=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,g=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,h="_html5shiv",v=0,y={};!function(){try{var e=t.createElement("a");e.innerHTML="",d="hidden"in e,u=1==e.childNodes.length||function(){t.createElement("a");var e=t.createDocumentFragment();return"undefined"==typeof e.cloneNode||"undefined"==typeof e.createDocumentFragment||"undefined"==typeof e.createElement}()}catch(n){d=!0,u=!0}}();var b={elements:p.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:f,shivCSS:p.shivCSS!==!1,supportsUnknownElements:u,shivMethods:p.shivMethods!==!1,type:"default",shivDocument:l,createElement:i,createDocumentFragment:s,addElements:o};e.html5=b,l(t),"object"==typeof module&&module.exports&&(module.exports=b)}("undefined"!=typeof e?e:this,t);var N="Moz O ms Webkit",O=S._config.usePrefixes?N.toLowerCase().split(" "):[];S._domPrefixes=O;var z=function(){function e(e,t){var o;return e?(t&&"string"!=typeof t||(t=i(t||"div")),e="on"+e,o=e in t,!o&&r&&(t.setAttribute||(t=i("div")),t.setAttribute(e,""),o="function"==typeof t[e],t[e]!==n&&(t[e]=n),t.removeAttribute(e)),o):!1}var r=!("onblur"in t.documentElement);return e}();S.hasEvent=z,Modernizr.addTest("hashchange",function(){return z("hashchange",e)===!1?!1:t.documentMode===n||t.documentMode>7}),Modernizr.addTest("audio",function(){var e=i("audio"),t=!1;try{t=!!e.canPlayType,t&&(t=new Boolean(t),t.ogg=e.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),t.mp3=e.canPlayType('audio/mpeg; codecs="mp3"').replace(/^no$/,""),t.opus=e.canPlayType('audio/ogg; codecs="opus"')||e.canPlayType('audio/webm; codecs="opus"').replace(/^no$/,""),t.wav=e.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),t.m4a=(e.canPlayType("audio/x-m4a;")||e.canPlayType("audio/aac;")).replace(/^no$/,""))}catch(n){}return t}),Modernizr.addTest("canvas",function(){var e=i("canvas");return!(!e.getContext||!e.getContext("2d"))}),Modernizr.addTest("canvastext",function(){return Modernizr.canvas===!1?!1:"function"==typeof i("canvas").getContext("2d").fillText}),Modernizr.addTest("video",function(){var e=i("video"),t=!1;try{t=!!e.canPlayType,t&&(t=new Boolean(t),t.ogg=e.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),t.h264=e.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),t.webm=e.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,""),t.vp9=e.canPlayType('video/webm; codecs="vp9"').replace(/^no$/,""),t.hls=e.canPlayType('application/x-mpegURL; codecs="avc1.42E01E"').replace(/^no$/,""))}catch(n){}return t}),Modernizr.addTest("webgl",function(){var t=i("canvas"),n="probablySupportsContext"in t?"probablySupportsContext":"supportsContext";return n in t?t[n]("webgl")||t[n]("experimental-webgl"):"WebGLRenderingContext"in e}),Modernizr.addTest("cssgradients",function(){for(var e,t="background-image:",n="gradient(linear,left top,right bottom,from(#9f9),to(white));",r="",o=0,a=_.length-1;a>o;o++)e=0===o?"to ":"",r+=t+_[o]+"linear-gradient("+e+"left top, #9f9, white);";Modernizr._config.usePrefixes&&(r+=t+"-webkit-"+n);var s=i("a"),c=s.style;return c.cssText=r,(""+c.backgroundImage).indexOf("gradient")>-1}),Modernizr.addTest("multiplebgs",function(){var e=i("a").style;return e.cssText="background:url(https://),url(https://),red url(https://)",/(url\s*\(.*?){3}/.test(e.background)}),Modernizr.addTest("opacity",function(){var e=i("a").style;return e.cssText=_.join("opacity:.55;"),/^0.55$/.test(e.opacity)}),Modernizr.addTest("rgba",function(){var e=i("a").style;return e.cssText="background-color:rgba(150,255,150,.5)",(""+e.backgroundColor).indexOf("rgba")>-1}),Modernizr.addTest("inlinesvg",function(){var e=i("div");return e.innerHTML="","http://www.w3.org/2000/svg"==("undefined"!=typeof SVGRect&&e.firstChild&&e.firstChild.namespaceURI)});var R=i("input"),A="autocomplete autofocus list placeholder max min multiple pattern required step".split(" "),M={};Modernizr.input=function(t){for(var n=0,r=t.length;r>n;n++)M[t[n]]=!!(t[n]in R);return M.list&&(M.list=!(!i("datalist")||!e.HTMLDataListElement)),M}(A);var $="search tel url email datetime date month week time datetime-local number range color".split(" "),B={};Modernizr.inputtypes=function(e){for(var r,o,a,i=e.length,s="1)",c=0;i>c;c++)R.setAttribute("type",r=e[c]),a="text"!==R.type&&"style"in R,a&&(R.value=s,R.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(r)&&R.style.WebkitAppearance!==n?(k.appendChild(R),o=t.defaultView,a=o.getComputedStyle&&"textfield"!==o.getComputedStyle(R,null).WebkitAppearance&&0!==R.offsetHeight,k.removeChild(R)):/^(search|tel)$/.test(r)||(a=/^(url|email)$/.test(r)?R.checkValidity&&R.checkValidity()===!1:R.value!=s)),B[e[c]]=!!a;return B}($),Modernizr.addTest("hsla",function(){var e=i("a").style;return e.cssText="background-color:hsla(120,40%,100%,.5)",s(e.backgroundColor,"rgba")||s(e.backgroundColor,"hsla")});var j="CSS"in e&&"supports"in e.CSS,L="supportsCSS"in e;Modernizr.addTest("supports",j||L);var D={}.toString;Modernizr.addTest("svgclippaths",function(){return!!t.createElementNS&&/SVGClipPath/.test(D.call(t.createElementNS("http://www.w3.org/2000/svg","clipPath")))}),Modernizr.addTest("smil",function(){return!!t.createElementNS&&/SVGAnimate/.test(D.call(t.createElementNS("http://www.w3.org/2000/svg","animate")))});var F=function(){var t=e.matchMedia||e.msMatchMedia;return t?function(e){var n=t(e);return n&&n.matches||!1}:function(t){var n=!1;return l("@media "+t+" { #modernizr { position: absolute; } }",function(t){n="absolute"==(e.getComputedStyle?e.getComputedStyle(t,null):t.currentStyle).position}),n}}();S.mq=F;var I=S.testStyles=l,W=function(){var e=navigator.userAgent,t=e.match(/w(eb)?osbrowser/gi),n=e.match(/windows phone/gi)&&e.match(/iemobile\/([0-9])+/gi)&&parseFloat(RegExp.$1)>=9;return t||n}();W?Modernizr.addTest("fontface",!1):I('@font-face {font-family:"font";src:url("https://")}',function(e,n){var r=t.getElementById("smodernizr"),o=r.sheet||r.styleSheet,a=o?o.cssRules&&o.cssRules[0]?o.cssRules[0].cssText:o.cssText||"":"",i=/src/i.test(a)&&0===a.indexOf(n.split(" ")[0]);Modernizr.addTest("fontface",i)}),I('#modernizr{font:0/0 a}#modernizr:after{content:":)";visibility:hidden;font:7px/1 a}',function(e){Modernizr.addTest("generatedcontent",e.offsetHeight>=6)}),Modernizr.addTest("touchevents",function(){var n;if("ontouchstart"in e||e.DocumentTouch&&t instanceof DocumentTouch)n=!0;else{var r=["@media (",_.join("touch-enabled),("),"heartz",")","{#modernizr{top:9px;position:absolute}}"].join("");I(r,function(e){n=9===e.offsetTop})}return n});var U=S._config.usePrefixes?N.split(" "):[];S._cssomPrefixes=U;var V=function(t){var r,o=_.length,a=e.CSSRule;if("undefined"==typeof a)return n;if(!t)return!1;if(t=t.replace(/^@/,""),r=t.replace(/-/g,"_").toUpperCase()+"_RULE",r in a)return"@"+t;for(var i=0;o>i;i++){var s=_[i],c=s.toUpperCase()+"_"+r;if(c in a)return"@-"+s.toLowerCase()+"-"+t}return!1};S.atRule=V;var q;!function(){var e={}.hasOwnProperty;q=r(e,"undefined")||r(e.call,"undefined")?function(e,t){return t in e&&r(e.constructor.prototype[t],"undefined")}:function(t,n){return e.call(t,n)}}(),S._l={},S.on=function(e,t){this._l[e]||(this._l[e]=[]),this._l[e].push(t),Modernizr.hasOwnProperty(e)&&setTimeout(function(){Modernizr._trigger(e,Modernizr[e])},0)},S._trigger=function(e,t){if(this._l[e]){var n=this._l[e];setTimeout(function(){var e,r;for(e=0;e Date: Thu, 28 Nov 2019 09:21:03 +0800 Subject: [PATCH 04/88] Parenthesise some double ternary conditions in Asset Combiner. Improve compatibility with PHP 7.4, where "Unparenthesized `a ? b : c ?: d` is deprecated" Fixes #4790. --- .github/workflows/tests.yml | 2 +- modules/system/classes/CombineAssets.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b3ce5ca08..8b73a18b9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: strategy: max-parallel: 6 matrix: - phpVersions: ['7.1', '7.2', '7.3'] + phpVersions: ['7.1', '7.2', '7.3', '7.4'] fail-fast: false name: PHP ${{ matrix.phpVersions }} steps: diff --git a/modules/system/classes/CombineAssets.php b/modules/system/classes/CombineAssets.php index bc5d1d6de..87a9d1d8f 100644 --- a/modules/system/classes/CombineAssets.php +++ b/modules/system/classes/CombineAssets.php @@ -428,7 +428,7 @@ class CombineAssets $filesSalt = null; foreach ($assets as $asset) { $filters = $this->getFilters(File::extension($asset)) ?: []; - $path = file_exists($asset) ? $asset : File::symbolizePath($asset, null) ?: $this->localPath . $asset; + $path = file_exists($asset) ? $asset : (File::symbolizePath($asset, null) ?: $this->localPath . $asset); $files[] = new FileAsset($path, $filters, public_path()); $filesSalt .= $this->localPath . $asset; } @@ -482,7 +482,7 @@ class CombineAssets $key = ''; $assetFiles = array_map(function ($file) { - return file_exists($file) ? $file : File::symbolizePath($file, null) ?: $this->localPath . $file; + return file_exists($file) ? $file : (File::symbolizePath($file, null) ?: $this->localPath . $file); }, $assets); foreach ($assetFiles as $file) { From c70af0ec64ea9512bbcd4b36af271930e36261e4 Mon Sep 17 00:00:00 2001 From: Ben Thomson Date: Thu, 28 Nov 2019 09:25:10 +0800 Subject: [PATCH 05/88] Ensure necessary PHP extensions are installed with unit tests --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8b73a18b9..536987401 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,6 +23,7 @@ jobs: uses: shivammathur/setup-php@master with: php-version: ${{ matrix.phpVersions }} + extension-csv: mbstring, intl - name: Install Composer dependencies run: composer install --no-interaction --no-progress --no-suggest - name: Reset October modules and library From 08497204940794f35f5dcb480ca14c73d1743336 Mon Sep 17 00:00:00 2001 From: Ben Thomson Date: Thu, 28 Nov 2019 12:48:56 +0800 Subject: [PATCH 06/88] Delay running post-update Composer scripts in automated tests Allows us to reset the October codebase to what's in Git *before* running these tasks. --- .github/workflows/tests.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 536987401..18143af92 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,7 +25,7 @@ jobs: php-version: ${{ matrix.phpVersions }} extension-csv: mbstring, intl - name: Install Composer dependencies - run: composer install --no-interaction --no-progress --no-suggest + run: composer install --no-interaction --no-progress --no-suggest --no-scripts - name: Reset October modules and library run: | git reset --hard HEAD @@ -34,6 +34,10 @@ jobs: unzip ./vendor/october/develop.zip -d ./vendor/october mv ./vendor/october/library-develop ./vendor/october/rain composer dump-autoload + - name: Run post-update Composer scripts + run: | + php artisan october:util set build + php artisan package:discover - name: Run Linting and Tests run: | ./vendor/bin/parallel-lint --exclude vendor --exclude storage --exclude tests/fixtures/plugins/testvendor/goto/Plugin.php . From a91fa3f5ff1b8daf142e2a28d93c89618ad18db3 Mon Sep 17 00:00:00 2001 From: Ben Thomson Date: Thu, 28 Nov 2019 12:52:31 +0800 Subject: [PATCH 07/88] Add a few more PHP extensions into the automated tests --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 18143af92..31d195a70 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,7 +23,7 @@ jobs: uses: shivammathur/setup-php@master with: php-version: ${{ matrix.phpVersions }} - extension-csv: mbstring, intl + extension-csv: mbstring, intl, gd, xml - name: Install Composer dependencies run: composer install --no-interaction --no-progress --no-suggest --no-scripts - name: Reset October modules and library From 249051b89c9c446f051d6a8ae234823e0e6639e2 Mon Sep 17 00:00:00 2001 From: Ben Thomson Date: Thu, 28 Nov 2019 12:57:36 +0800 Subject: [PATCH 08/88] Include SQLite in automated tests --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 31d195a70..072b795da 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,7 +23,7 @@ jobs: uses: shivammathur/setup-php@master with: php-version: ${{ matrix.phpVersions }} - extension-csv: mbstring, intl, gd, xml + extension-csv: mbstring, intl, gd, xml, sqlite - name: Install Composer dependencies run: composer install --no-interaction --no-progress --no-suggest --no-scripts - name: Reset October modules and library From 566f138eabd8032aefc9aed84fe67ab4f8e6011f Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Thu, 28 Nov 2019 21:03:32 +1100 Subject: [PATCH 09/88] Fixes touch events in sortable plugin Refs #4791 Refs #4777 Refs #3755 --- modules/system/assets/ui/storm-min.js | 14 +++++++------- .../ui/vendor/sortable/jquery-sortable.js | 17 ++++++++++------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/modules/system/assets/ui/storm-min.js b/modules/system/assets/ui/storm-min.js index a3881f428..7576ace78 100644 --- a/modules/system/assets/ui/storm-min.js +++ b/modules/system/assets/ui/storm-min.js @@ -51,7 +51,7 @@ return this.renderTokens(token[4],context,partials,originalTemplate);};Writer.pr return this.renderTokens(this.parse(value),context,partials,value);};Writer.prototype.unescapedValue=function unescapedValue(token,context){var value=context.lookup(token[1]);if(value!=null) return value;};Writer.prototype.escapedValue=function escapedValue(token,context){var value=context.lookup(token[1]);if(value!=null) return mustache.escape(value);};Writer.prototype.rawValue=function rawValue(token){return token[1];};mustache.name='mustache.js';mustache.version='2.3.2';mustache.tags=['{{','}}'];var defaultWriter=new Writer();mustache.clearCache=function clearCache(){return defaultWriter.clearCache();};mustache.parse=function parse(template,tags){return defaultWriter.parse(template,tags);};mustache.render=function render(template,view,partials){if(typeof template!=='string'){throw new TypeError('Invalid template! Template should be a "string" '+'but "'+typeStr(template)+'" was given as the first '+'argument for mustache#render(template, view, partials)');} -return defaultWriter.render(template,view,partials);};mustache.to_html=function to_html(template,view,partials,send){var result=mustache.render(template,view,partials);if(isFunction(send)){send(result);}else{return result;}};mustache.escape=escapeHtml;mustache.Scanner=Scanner;mustache.Context=Context;mustache.Writer=Writer;return mustache;}));!function(f,u,p){var s=[],e={_version:"3.7.1",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,t){var n=this;setTimeout(function(){t(n[e])},0)},addTest:function(e,t,n){s.push({name:e,fn:t,options:n})},addAsyncTest:function(e){s.push({name:null,fn:e})}},c=function(){};c.prototype=e,c=new c;var d=[];function m(e,t){return typeof e===t}var h=u.documentElement,g="svg"===h.nodeName.toLowerCase();function a(e){var t=h.className,n=c._config.classPrefix||"";if(g&&(t=t.baseVal),c._config.enableJSClass){var r=new RegExp("(^|\\s)"+n+"no-js(\\s|$)");t=t.replace(r,"$1"+n+"js$2")}c._config.enableClasses&&(0",r.insertBefore(n.lastChild,r.firstChild)}function f(){var e=h.elements;return"string"==typeof e?e.split(" "):e}function p(e){var t=c[e[r]];return t||(t={},d++,e[r]=d,c[d]=t),t}function l(e,t,n){return t||(t=i),s?t.createElement(e):(n||(n=p(t)),!(r=n.cache[e]?n.cache[e].cloneNode():a.test(e)?(n.cache[e]=n.createElem(e)).cloneNode():n.createElem(e)).canHaveChildren||o.test(e)||r.tagUrn?r:n.frag.appendChild(r));var r}function m(e){e||(e=i);var t=p(e);return!h.shivCSS||n||t.hasCSS||(t.hasCSS=!!u(e,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),s||function(t,n){n.cache||(n.cache={},n.createElem=t.createElement,n.createFrag=t.createDocumentFragment,n.frag=n.createFrag()),t.createElement=function(e){return h.shivMethods?l(e,t,n):n.createElem(e)},t.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+f().join().replace(/[\w\-:]+/g,function(e){return n.createElem(e),n.frag.createElement(e),'c("'+e+'")'})+");return n}")(h,n.frag)}(e,t),e}!function(){try{var e=i.createElement("a");e.innerHTML="",n="hidden"in e,s=1==e.childNodes.length||function(){i.createElement("a");var e=i.createDocumentFragment();return void 0===e.cloneNode||void 0===e.createDocumentFragment||void 0===e.createElement}()}catch(e){s=n=!0}}();var h={elements:t.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:"3.7.3",shivCSS:!1!==t.shivCSS,supportsUnknownElements:s,shivMethods:!1!==t.shivMethods,type:"default",shivDocument:m,createElement:l,createDocumentFragment:function(e,t){if(e||(e=i),s)return e.createDocumentFragment();for(var n=(t=t||p(e)).frag.cloneNode(),r=0,o=f(),a=o.length;r+~])("+f().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),a="$1"+y+"\\:$2";r--;)(t=n[r]=n[r].split("}"))[t.length-1]=t[t.length-1].replace(o,a),n[r]=t.join("}");return n.join("{")}(o.reverse().join("")),c=function(e){for(var t,n=e.getElementsByTagName("*"),r=n.length,o=RegExp("^(?:"+f().join("|")+")$","i"),a=[];r--;)t=n[r],o.test(t.nodeName)&&a.push(t.applyElement(T(t)));return a}(s),d=u(s,o)}),n.attachEvent("onafterprint",function(){!function(e){for(var t=e.length;t--;)e[t].removeNode()}(c),clearTimeout(e._removeSheetTimer),e._removeSheetTimer=setTimeout(l,500)}),s.printShived=!0,s}h.type+=" print",(h.shivPrint=x)(i),"object"==typeof module&&module.exports&&(module.exports=h)}(void 0!==f?f:this,u);var T=e._config.usePrefixes?t.split(" "):[];function x(e,t){return!!~(""+e).indexOf(t)}e._cssomPrefixes=T;var w={elem:v("modernizr")};c._q.push(function(){delete w.elem});var S={style:w.elem.style};function C(e){return e.replace(/([A-Z])/g,function(e,t){return"-"+t.toLowerCase()}).replace(/^ms-/,"-ms-")}function E(e,t,n){var r;if("getComputedStyle"in f){r=getComputedStyle.call(f,e,t);var o=f.console;if(null!==r)n&&(r=r.getPropertyValue(n));else if(o)o[o.error?"error":"log"].call(o,"getComputedStyle returning null, its possible modernizr test results are inaccurate")}else r=!t&&e.currentStyle&&e.currentStyle[n];return r}function k(e){return e.replace(/([a-z])-([a-z])/g,function(e,t,n){return t+n.toUpperCase()}).replace(/^-/,"")}function _(e,t,n,r){if(r=!m(r,"undefined")&&r,!m(n,"undefined")){var o=function(e,t){var n=e.length;if("CSS"in f&&"supports"in f.CSS){for(;n--;)if(f.CSS.supports(C(e[n]),t))return!0;return!1}if("CSSSupportsRule"in f){for(var r=[];n--;)r.push("("+C(e[n])+":"+t+")");return y("@supports ("+(r=r.join(" or "))+") { #modernizr { position: absolute; } }",function(e){return"absolute"===E(e,null,"position")})}return p}(e,n);if(!m(o,"undefined"))return o}for(var a,i,s,d,c,l=["modernizr","tspan","samp"];!S.style&&l.length;)a=!0,S.modElem=v(l.shift()),S.style=S.modElem.style;function u(){a&&(delete S.style,delete S.modElem)}for(s=e.length,i=0;if;f++)if(m=e[f],g=G.style[m],s(m,"-")&&(m=d(m)),G.style[m]!==n){if(a||r(o,"undefined"))return c(),"pfx"==t?m:!0;try{G.style[m]=o}catch(y){}if(G.style[m]!=g)return c(),"pfx"==t?m:!0}return c(),!1}function y(e,t,n,o,a){var i=e.charAt(0).toUpperCase()+e.slice(1),s=(e+" "+U.join(i+" ")+i).split(" ");return r(t,"string")||r(t,"undefined")?v(s,t,o,a):(s=(e+" "+O.join(i+" ")+i).split(" "),p(s,t,n))}function b(e,t,r){return y(e,n,n,t,r)}function T(e,t){var n=e.deleteDatabase(t);n.onsuccess=function(){u("indexeddb.deletedatabase",!0)},n.onerror=function(){u("indexeddb.deletedatabase",!1)}}var x=[],w=[],S={_version:"3.6.0",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,t){var n=this;setTimeout(function(){t(n[e])},0)},addTest:function(e,t,n){w.push({name:e,fn:t,options:n})},addAsyncTest:function(e){w.push({name:null,fn:e})}},Modernizr=function(){};Modernizr.prototype=S,Modernizr=new Modernizr,Modernizr.addTest("applicationcache","applicationCache"in e),Modernizr.addTest("geolocation","geolocation"in navigator),Modernizr.addTest("history",function(){var t=navigator.userAgent;return-1===t.indexOf("Android 2.")&&-1===t.indexOf("Android 4.0")||-1===t.indexOf("Mobile Safari")||-1!==t.indexOf("Chrome")||-1!==t.indexOf("Windows Phone")||"file:"===location.protocol?e.history&&"pushState"in e.history:!1}),Modernizr.addTest("postmessage","postMessage"in e),Modernizr.addTest("svg",!!t.createElementNS&&!!t.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect);var C=!1;try{C="WebSocket"in e&&2===e.WebSocket.CLOSING}catch(E){}Modernizr.addTest("websockets",C),Modernizr.addTest("localstorage",function(){var e="modernizr";try{return localStorage.setItem(e,e),localStorage.removeItem(e),!0}catch(t){return!1}}),Modernizr.addTest("sessionstorage",function(){var e="modernizr";try{return sessionStorage.setItem(e,e),sessionStorage.removeItem(e),!0}catch(t){return!1}}),Modernizr.addTest("websqldatabase","openDatabase"in e),Modernizr.addTest("webworkers","Worker"in e);var _=S._config.usePrefixes?" -webkit- -moz- -o- -ms- ".split(" "):["",""];S._prefixes=_;var k=t.documentElement,P="svg"===k.nodeName.toLowerCase();P||!function(e,t){function n(e,t){var n=e.createElement("p"),r=e.getElementsByTagName("head")[0]||e.documentElement;return n.innerHTML="x",r.insertBefore(n.lastChild,r.firstChild)}function r(){var e=b.elements;return"string"==typeof e?e.split(" "):e}function o(e,t){var n=b.elements;"string"!=typeof n&&(n=n.join(" ")),"string"!=typeof e&&(e=e.join(" ")),b.elements=n+" "+e,l(t)}function a(e){var t=y[e[h]];return t||(t={},v++,e[h]=v,y[v]=t),t}function i(e,n,r){if(n||(n=t),u)return n.createElement(e);r||(r=a(n));var o;return o=r.cache[e]?r.cache[e].cloneNode():g.test(e)?(r.cache[e]=r.createElem(e)).cloneNode():r.createElem(e),!o.canHaveChildren||m.test(e)||o.tagUrn?o:r.frag.appendChild(o)}function s(e,n){if(e||(e=t),u)return e.createDocumentFragment();n=n||a(e);for(var o=n.frag.cloneNode(),i=0,s=r(),c=s.length;c>i;i++)o.createElement(s[i]);return o}function c(e,t){t.cache||(t.cache={},t.createElem=e.createElement,t.createFrag=e.createDocumentFragment,t.frag=t.createFrag()),e.createElement=function(n){return b.shivMethods?i(n,e,t):t.createElem(n)},e.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+r().join().replace(/[\w\-:]+/g,function(e){return t.createElem(e),t.frag.createElement(e),'c("'+e+'")'})+");return n}")(b,t.frag)}function l(e){e||(e=t);var r=a(e);return!b.shivCSS||d||r.hasCSS||(r.hasCSS=!!n(e,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),u||c(e,r),e}var d,u,f="3.7.3",p=e.html5||{},m=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,g=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,h="_html5shiv",v=0,y={};!function(){try{var e=t.createElement("a");e.innerHTML="",d="hidden"in e,u=1==e.childNodes.length||function(){t.createElement("a");var e=t.createDocumentFragment();return"undefined"==typeof e.cloneNode||"undefined"==typeof e.createDocumentFragment||"undefined"==typeof e.createElement}()}catch(n){d=!0,u=!0}}();var b={elements:p.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:f,shivCSS:p.shivCSS!==!1,supportsUnknownElements:u,shivMethods:p.shivMethods!==!1,type:"default",shivDocument:l,createElement:i,createDocumentFragment:s,addElements:o};e.html5=b,l(t),"object"==typeof module&&module.exports&&(module.exports=b)}("undefined"!=typeof e?e:this,t);var N="Moz O ms Webkit",O=S._config.usePrefixes?N.toLowerCase().split(" "):[];S._domPrefixes=O;var z=function(){function e(e,t){var o;return e?(t&&"string"!=typeof t||(t=i(t||"div")),e="on"+e,o=e in t,!o&&r&&(t.setAttribute||(t=i("div")),t.setAttribute(e,""),o="function"==typeof t[e],t[e]!==n&&(t[e]=n),t.removeAttribute(e)),o):!1}var r=!("onblur"in t.documentElement);return e}();S.hasEvent=z,Modernizr.addTest("hashchange",function(){return z("hashchange",e)===!1?!1:t.documentMode===n||t.documentMode>7}),Modernizr.addTest("audio",function(){var e=i("audio"),t=!1;try{t=!!e.canPlayType,t&&(t=new Boolean(t),t.ogg=e.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),t.mp3=e.canPlayType('audio/mpeg; codecs="mp3"').replace(/^no$/,""),t.opus=e.canPlayType('audio/ogg; codecs="opus"')||e.canPlayType('audio/webm; codecs="opus"').replace(/^no$/,""),t.wav=e.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),t.m4a=(e.canPlayType("audio/x-m4a;")||e.canPlayType("audio/aac;")).replace(/^no$/,""))}catch(n){}return t}),Modernizr.addTest("canvas",function(){var e=i("canvas");return!(!e.getContext||!e.getContext("2d"))}),Modernizr.addTest("canvastext",function(){return Modernizr.canvas===!1?!1:"function"==typeof i("canvas").getContext("2d").fillText}),Modernizr.addTest("video",function(){var e=i("video"),t=!1;try{t=!!e.canPlayType,t&&(t=new Boolean(t),t.ogg=e.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),t.h264=e.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),t.webm=e.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,""),t.vp9=e.canPlayType('video/webm; codecs="vp9"').replace(/^no$/,""),t.hls=e.canPlayType('application/x-mpegURL; codecs="avc1.42E01E"').replace(/^no$/,""))}catch(n){}return t}),Modernizr.addTest("webgl",function(){var t=i("canvas"),n="probablySupportsContext"in t?"probablySupportsContext":"supportsContext";return n in t?t[n]("webgl")||t[n]("experimental-webgl"):"WebGLRenderingContext"in e}),Modernizr.addTest("cssgradients",function(){for(var e,t="background-image:",n="gradient(linear,left top,right bottom,from(#9f9),to(white));",r="",o=0,a=_.length-1;a>o;o++)e=0===o?"to ":"",r+=t+_[o]+"linear-gradient("+e+"left top, #9f9, white);";Modernizr._config.usePrefixes&&(r+=t+"-webkit-"+n);var s=i("a"),c=s.style;return c.cssText=r,(""+c.backgroundImage).indexOf("gradient")>-1}),Modernizr.addTest("multiplebgs",function(){var e=i("a").style;return e.cssText="background:url(https://),url(https://),red url(https://)",/(url\s*\(.*?){3}/.test(e.background)}),Modernizr.addTest("opacity",function(){var e=i("a").style;return e.cssText=_.join("opacity:.55;"),/^0.55$/.test(e.opacity)}),Modernizr.addTest("rgba",function(){var e=i("a").style;return e.cssText="background-color:rgba(150,255,150,.5)",(""+e.backgroundColor).indexOf("rgba")>-1}),Modernizr.addTest("inlinesvg",function(){var e=i("div");return e.innerHTML="","http://www.w3.org/2000/svg"==("undefined"!=typeof SVGRect&&e.firstChild&&e.firstChild.namespaceURI)});var R=i("input"),A="autocomplete autofocus list placeholder max min multiple pattern required step".split(" "),M={};Modernizr.input=function(t){for(var n=0,r=t.length;r>n;n++)M[t[n]]=!!(t[n]in R);return M.list&&(M.list=!(!i("datalist")||!e.HTMLDataListElement)),M}(A);var $="search tel url email datetime date month week time datetime-local number range color".split(" "),B={};Modernizr.inputtypes=function(e){for(var r,o,a,i=e.length,s="1)",c=0;i>c;c++)R.setAttribute("type",r=e[c]),a="text"!==R.type&&"style"in R,a&&(R.value=s,R.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(r)&&R.style.WebkitAppearance!==n?(k.appendChild(R),o=t.defaultView,a=o.getComputedStyle&&"textfield"!==o.getComputedStyle(R,null).WebkitAppearance&&0!==R.offsetHeight,k.removeChild(R)):/^(search|tel)$/.test(r)||(a=/^(url|email)$/.test(r)?R.checkValidity&&R.checkValidity()===!1:R.value!=s)),B[e[c]]=!!a;return B}($),Modernizr.addTest("hsla",function(){var e=i("a").style;return e.cssText="background-color:hsla(120,40%,100%,.5)",s(e.backgroundColor,"rgba")||s(e.backgroundColor,"hsla")});var j="CSS"in e&&"supports"in e.CSS,L="supportsCSS"in e;Modernizr.addTest("supports",j||L);var D={}.toString;Modernizr.addTest("svgclippaths",function(){return!!t.createElementNS&&/SVGClipPath/.test(D.call(t.createElementNS("http://www.w3.org/2000/svg","clipPath")))}),Modernizr.addTest("smil",function(){return!!t.createElementNS&&/SVGAnimate/.test(D.call(t.createElementNS("http://www.w3.org/2000/svg","animate")))});var F=function(){var t=e.matchMedia||e.msMatchMedia;return t?function(e){var n=t(e);return n&&n.matches||!1}:function(t){var n=!1;return l("@media "+t+" { #modernizr { position: absolute; } }",function(t){n="absolute"==(e.getComputedStyle?e.getComputedStyle(t,null):t.currentStyle).position}),n}}();S.mq=F;var I=S.testStyles=l,W=function(){var e=navigator.userAgent,t=e.match(/w(eb)?osbrowser/gi),n=e.match(/windows phone/gi)&&e.match(/iemobile\/([0-9])+/gi)&&parseFloat(RegExp.$1)>=9;return t||n}();W?Modernizr.addTest("fontface",!1):I('@font-face {font-family:"font";src:url("https://")}',function(e,n){var r=t.getElementById("smodernizr"),o=r.sheet||r.styleSheet,a=o?o.cssRules&&o.cssRules[0]?o.cssRules[0].cssText:o.cssText||"":"",i=/src/i.test(a)&&0===a.indexOf(n.split(" ")[0]);Modernizr.addTest("fontface",i)}),I('#modernizr{font:0/0 a}#modernizr:after{content:":)";visibility:hidden;font:7px/1 a}',function(e){Modernizr.addTest("generatedcontent",e.offsetHeight>=6)}),Modernizr.addTest("touchevents",function(){var n;if("ontouchstart"in e||e.DocumentTouch&&t instanceof DocumentTouch)n=!0;else{var r=["@media (",_.join("touch-enabled),("),"heartz",")","{#modernizr{top:9px;position:absolute}}"].join("");I(r,function(e){n=9===e.offsetTop})}return n});var U=S._config.usePrefixes?N.split(" "):[];S._cssomPrefixes=U;var V=function(t){var r,o=_.length,a=e.CSSRule;if("undefined"==typeof a)return n;if(!t)return!1;if(t=t.replace(/^@/,""),r=t.replace(/-/g,"_").toUpperCase()+"_RULE",r in a)return"@"+t;for(var i=0;o>i;i++){var s=_[i],c=s.toUpperCase()+"_"+r;if(c in a)return"@-"+s.toLowerCase()+"-"+t}return!1};S.atRule=V;var q;!function(){var e={}.hasOwnProperty;q=r(e,"undefined")||r(e.call,"undefined")?function(e,t){return t in e&&r(e.constructor.prototype[t],"undefined")}:function(t,n){return e.call(t,n)}}(),S._l={},S.on=function(e,t){this._l[e]||(this._l[e]=[]),this._l[e].push(t),Modernizr.hasOwnProperty(e)&&setTimeout(function(){Modernizr._trigger(e,Modernizr[e])},0)},S._trigger=function(e,t){if(this._l[e]){var n=this._l[e];setTimeout(function(){var e,r;for(e=0;e=1?"floor":"ceil"](delta/lowestDelta);deltaX=Math[deltaX>=1?"fl event.deltaX=deltaX;event.deltaY=deltaY;event.deltaFactor=lowestDelta;event.deltaMode=0;args.unshift(event,delta,deltaX,deltaY);if(nullLowestDeltaTimeout){window.clearTimeout(nullLowestDeltaTimeout);} nullLowestDeltaTimeout=window.setTimeout(nullLowestDelta,200);return($.event.dispatch||$.event.handle).apply(this,args);} function nullLowestDelta(){lowestDelta=null;} -function shouldAdjustOldDeltas(orgEvent,absDelta){return special.settings.adjustOldDeltas&&orgEvent.type==="mousewheel"&&absDelta%120===0;}});!function($,window,pluginName,undefined){var containerDefaults={drag:true,drop:true,exclude:"",nested:true,vertical:true},groupDefaults={afterMove:function($placeholder,container,$closestItemOrContainer){},containerPath:"",containerSelector:"ol, ul",distance:0,delay:0,handle:"",itemPath:"",itemSelector:"li",bodyClass:"dragging",draggedClass:"dragged",isValidTarget:function($item,container){return true},onCancel:function($item,container,_super,event){},onDrag:function($item,position,_super,event){$item.css(position)},onDragStart:function($item,container,_super,event){$item.css({height:$item.outerHeight(),width:$item.outerWidth()}) +function shouldAdjustOldDeltas(orgEvent,absDelta){return special.settings.adjustOldDeltas&&orgEvent.type==="mousewheel"&&absDelta%120===0;}});!function($,window,pluginName,undefined){var containerDefaults={drag:true,drop:true,exclude:"",nested:true,vertical:true},groupDefaults={afterMove:function($placeholder,container,$closestItemOrContainer){},containerPath:"",containerSelector:"ol, ul",distance:0,delay:0,handle:"",itemPath:"",itemSelector:"li",bodyClass:"dragging",draggedClass:"dragged",isValidTarget:function($item,container){return true},onCancel:function($item,container,_super,event){},onDrag:function($item,position,_super,event){$item.css(position) +event.preventDefault()},onDragStart:function($item,container,_super,event){$item.css({height:$item.outerHeight(),width:$item.outerWidth()}) $item.addClass(container.group.options.draggedClass) $("body").addClass(container.group.options.bodyClass)},onDrop:function($item,container,_super,event){$item.removeClass(container.group.options.draggedClass).removeAttr("style") -$("body").removeClass(container.group.options.bodyClass)},onMousedown:function($item,_super,event){if(!event.target.nodeName.match(/^(input|select|textarea)$/i)){event.preventDefault() +$("body").removeClass(container.group.options.bodyClass)},onMousedown:function($item,_super,event){if(!event.target.nodeName.match(/^(input|select|textarea)$/i)){if(event.type.match(/^mouse/))event.preventDefault() return true}},placeholderClass:"placeholder",placeholder:'
  • ',pullPlaceholder:true,serialize:function($parent,$children,parentIsContainer){var result=$.extend({},$parent.data()) -if(parentIsContainer) -return[$children] +if(parentIsContainer){return[$children]} else if($children[0]){result.children=$children} delete result.subContainers delete result.sortable @@ -1757,8 +1757,8 @@ this.lastRelativePointer=this.relativePointer this.relativePointer=relativePointer} this.lastPointer=this.pointer this.pointer=pointer},distanceMet:function(e){var currentPointer=this.getPointer(e) -return(Math.max(Math.abs(this.pointer.left-currentPointer.left),Math.abs(this.pointer.top-currentPointer.top))>=this.options.distance)},getPointer:function(e){var o=e.originalEvent||e.originalEvent.touches&&e.originalEvent.touches[0] -return{left:e.pageX||o.pageX,top:e.pageY||o.pageY}},setupDelayTimer:function(){var that=this +return(Math.max(Math.abs(this.pointer.left-currentPointer.left),Math.abs(this.pointer.top-currentPointer.top))>=this.options.distance)},getPointer:function(e){var o=e.originalEvent,t=(e.originalEvent.touches&&e.originalEvent.touches[0])||{} +return{left:e.pageX||o.pageX||t.pageX,top:e.pageY||o.pageY||t.pageY}},setupDelayTimer:function(){var that=this this.delayMet=!this.options.delay if(!this.delayMet){clearTimeout(this._mouseDelayTimer);this._mouseDelayTimer=setTimeout(function(){that.delayMet=true},this.options.delay)}},scroll:function(e){this.clearDimensions() this.clearOffsetParent()},toggleListeners:function(method){var that=this,events=['drag','drop','scroll'] diff --git a/modules/system/assets/ui/vendor/sortable/jquery-sortable.js b/modules/system/assets/ui/vendor/sortable/jquery-sortable.js index 6cbd80634..8227b4c20 100644 --- a/modules/system/assets/ui/vendor/sortable/jquery-sortable.js +++ b/modules/system/assets/ui/vendor/sortable/jquery-sortable.js @@ -85,6 +85,7 @@ // The Placeholder has not been moved yet. onDrag: function ($item, position, _super, event) { $item.css(position) + event.preventDefault() }, // Called after the drag has been started, // that is the mouse button is being held down and @@ -108,7 +109,7 @@ // Ignore if element clicked is input, select or textarea onMousedown: function ($item, _super, event) { if (!event.target.nodeName.match(/^(input|select|textarea)$/i)) { - event.preventDefault() + if (event.type.match(/^mouse/)) event.preventDefault() return true } }, @@ -126,8 +127,9 @@ serialize: function ($parent, $children, parentIsContainer) { var result = $.extend({}, $parent.data()) - if(parentIsContainer) + if (parentIsContainer) { return [$children] + } else if ($children[0]){ result.children = $children } @@ -253,7 +255,7 @@ this.item = closestItem; this.itemContainer = itemContainer; if (this.item.is(this.options.exclude) || !this.options.onMousedown(this.item, groupDefaults.onMousedown, e)) { - return; + return; } this.setPointer(e); this.toggleListeners('on'); @@ -400,10 +402,11 @@ ) >= this.options.distance) }, getPointer: function(e) { - var o = e.originalEvent || e.originalEvent.touches && e.originalEvent.touches[0] + var o = e.originalEvent, + t = (e.originalEvent.touches && e.originalEvent.touches[0]) || {} return { - left: e.pageX || o.pageX, - top: e.pageY || o.pageY + left: e.pageX || o.pageX || t.pageX, + top: e.pageY || o.pageY || t.pageY } }, setupDelayTimer: function () { @@ -689,4 +692,4 @@ }); }; -}(jQuery, window, 'jqSortable'); \ No newline at end of file +}(jQuery, window, 'jqSortable'); From 9b72c2d181ce3c121abf54949809e760113d2f5b Mon Sep 17 00:00:00 2001 From: Ben Thomson Date: Thu, 28 Nov 2019 22:23:28 +0800 Subject: [PATCH 10/88] Number range filter improvements (#4789) - Allow minimum or maximum to be unspecified, meaning you want everything up to maximum, or everything above minimum. - Allow for zero values to work - Tweak display of infinite values Fixes #3982. --- modules/backend/widgets/Filter.php | 20 +++++++++---------- modules/system/assets/ui/js/filter.numbers.js | 2 +- modules/system/assets/ui/storm-min.js | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/backend/widgets/Filter.php b/modules/backend/widgets/Filter.php index ddb8b6892..a508007fe 100644 --- a/modules/backend/widgets/Filter.php +++ b/modules/backend/widgets/Filter.php @@ -145,18 +145,19 @@ class Filter extends WidgetBase break; case 'numberrange': - if ($scope->value && is_array($scope->value) && count($scope->value) === 2 && - $scope->value[0] && - $scope->value[1] + if ( + $scope->value + && (is_array($scope->value) && count($scope->value) === 2) + && (isset($scope->value[0]) || isset($scope->value[1])) ) { $min = $scope->value[0]; $max = $scope->value[1]; - $params['minStr'] = $min ?: ''; - $params['min'] = $min ?: null; + $params['minStr'] = $min ?? '∞'; + $params['min'] = $min ?? null; - $params['maxStr'] = $max ?: '∞'; - $params['max'] = $max ?: null; + $params['maxStr'] = $max ?? '∞'; + $params['max'] = $max ?? null; } break; @@ -790,7 +791,7 @@ class Filter extends WidgetBase if (is_array($scope->value) && count($scope->value) > 1) { list($min, $max) = array_values($scope->value); - if ($min && $max) { + if (isset($min) || isset($max)) { /* * Condition * @@ -1049,8 +1050,7 @@ class Filter extends WidgetBase if (preg_match($numberRegex, $number)) { $numbers[] = $number; } else { - $numbers = []; - break; + $numbers[] = null; } } } diff --git a/modules/system/assets/ui/js/filter.numbers.js b/modules/system/assets/ui/js/filter.numbers.js index 1a2cb071f..d0c8cef0f 100644 --- a/modules/system/assets/ui/js/filter.numbers.js +++ b/modules/system/assets/ui/js/filter.numbers.js @@ -257,7 +257,7 @@ numbers[1] = numbers[1] && numbers[1].match(numberRegex) ? numbers[1] : null if(numbers[0] || numbers[1]) { - var min = numbers[0] ? numbers[0] : '', + var min = numbers[0] ? numbers[0] : '∞', max = numbers[1] ? numbers[1] : '∞' $setting.text(min + ' → ' + max) diff --git a/modules/system/assets/ui/storm-min.js b/modules/system/assets/ui/storm-min.js index 7576ace78..f4c8efa10 100644 --- a/modules/system/assets/ui/storm-min.js +++ b/modules/system/assets/ui/storm-min.js @@ -3485,7 +3485,7 @@ numberinput.value=''!==defaultValue?defaultValue:'';})} FilterWidget.prototype.updateScopeNumberSetting=function($scope,numbers){var $setting=$scope.find('.filter-setting'),numberRegex=/\d*/,reset=false if(numbers&&numbers.length){numbers[0]=numbers[0]&&numbers[0].match(numberRegex)?numbers[0]:null if(numbers.length>1){numbers[1]=numbers[1]&&numbers[1].match(numberRegex)?numbers[1]:null -if(numbers[0]||numbers[1]){var min=numbers[0]?numbers[0]:'',max=numbers[1]?numbers[1]:'∞' +if(numbers[0]||numbers[1]){var min=numbers[0]?numbers[0]:'∞',max=numbers[1]?numbers[1]:'∞' $setting.text(min+' → '+max)}else{reset=true}} else if(numbers[0]){$setting.text(numbers[0])}else{reset=true}} else{reset=true} From e24a5964a14447a037323262836f3790843dbadd Mon Sep 17 00:00:00 2001 From: Ben Thomson Date: Thu, 28 Nov 2019 22:53:40 +0800 Subject: [PATCH 11/88] Drop PHP 7.4 testing for now Will bring it back with the L6 upgrade. --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 072b795da..b6e29f9a4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: strategy: max-parallel: 6 matrix: - phpVersions: ['7.1', '7.2', '7.3', '7.4'] + phpVersions: ['7.1', '7.2', '7.3'] fail-fast: false name: PHP ${{ matrix.phpVersions }} steps: From 7e98e199a46040dbf22b8211d33115ffae780e32 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Thu, 28 Nov 2019 10:32:34 -0600 Subject: [PATCH 12/88] Revert #4567, fixes #4648. If including the asset extension is important, this can be done by listening to the `system.assets.beforeAddAsset(&$type, &$path, &$attributes)` event introduced in Build 460. --- modules/system/classes/CombineAssets.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/system/classes/CombineAssets.php b/modules/system/classes/CombineAssets.php index 87a9d1d8f..6da6e195c 100644 --- a/modules/system/classes/CombineAssets.php +++ b/modules/system/classes/CombineAssets.php @@ -400,7 +400,7 @@ class CombineAssets $this->putCache($cacheKey, $cacheInfo); } - return $this->getCombinedUrl($cacheInfo['version'] . '.' . $extension); + return $this->getCombinedUrl($cacheInfo['version']); } /** From 61129b48ec229c4fae7dd55339007f82bb55998e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20K=C3=BCndig?= Date: Fri, 29 Nov 2019 08:26:37 +0100 Subject: [PATCH 13/88] Only put sortOptions to the session if the List query succeeded --- modules/backend/widgets/Lists.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/backend/widgets/Lists.php b/modules/backend/widgets/Lists.php index c4bfd4f43..862e96d40 100644 --- a/modules/backend/widgets/Lists.php +++ b/modules/backend/widgets/Lists.php @@ -1496,14 +1496,20 @@ class Lists extends WidgetBase $this->sortColumn = $sortOptions['column'] = $column; - $this->putSession('sort', $sortOptions); - /* * Persist the page number */ $this->currentPageNumber = post('page'); - return $this->onRefresh(); + /* + * Try to refresh the list with the new sortOptions. Put the + * new sortOptions in to the session if the query succeeded. + */ + $result = $this->onRefresh(); + + $this->putSession('sort', $sortOptions); + + return $result; } } From 128312106955d894528d448700f1fc66792b7826 Mon Sep 17 00:00:00 2001 From: Ben Thomson Date: Sat, 30 Nov 2019 00:09:49 +0800 Subject: [PATCH 14/88] Trigger "change" event when time picker is changed This allows dependent fields (ie. dependsOn) to trigger correctly when a time field, or the time part of a datetime field, is changed. Fixes #4268 --- modules/system/assets/ui/js/datepicker.js | 9 +++++++-- modules/system/assets/ui/storm-min.js | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/system/assets/ui/js/datepicker.js b/modules/system/assets/ui/js/datepicker.js index f6a46bb12..d840f01de 100644 --- a/modules/system/assets/ui/js/datepicker.js +++ b/modules/system/assets/ui/js/datepicker.js @@ -190,8 +190,8 @@ autoclose: 'true', placement: 'auto', align: 'right', - twelvehour: this.isTimeTwelveHour() - // afterDone: this.proxy(this.onSelectTimePicker) + twelvehour: this.isTimeTwelveHour(), + afterDone: this.proxy(this.onChangeTimePicker) }) this.$timePicker.val(this.getDataLockerValue(this.getTimeFormat())) @@ -213,6 +213,11 @@ this.$dataLocker.val(lockerValue) } + DatePicker.prototype.onChangeTimePicker = function() { + // Trigger a change event when the time is changed, to allow dependent fields to refresh + this.$timePicker.trigger('change') + } + // Returns in user preference timezone DatePicker.prototype.getTimePickerValue = function() { var value = this.$timePicker.val() diff --git a/modules/system/assets/ui/storm-min.js b/modules/system/assets/ui/storm-min.js index f4c8efa10..b0634059f 100644 --- a/modules/system/assets/ui/storm-min.js +++ b/modules/system/assets/ui/storm-min.js @@ -2964,7 +2964,7 @@ DatePicker.prototype.getDateFormat=function(){var format='YYYY-MM-DD' if(this.options.format){format=this.options.format} else if(this.locale){format=moment().locale(this.locale).localeData().longDateFormat('l')} return format} -DatePicker.prototype.initTimePicker=function(){this.$timePicker.clockpicker({autoclose:'true',placement:'auto',align:'right',twelvehour:this.isTimeTwelveHour()}) +DatePicker.prototype.initTimePicker=function(){this.$timePicker.clockpicker({autoclose:'true',placement:'auto',align:'right',twelvehour:this.isTimeTwelveHour(),afterDone:this.proxy(this.onChangeTimePicker)}) this.$timePicker.val(this.getDataLockerValue(this.getTimeFormat()))} DatePicker.prototype.onSelectTimePicker=function(){var pickerValue=this.$timePicker.val() var timeValue=moment(pickerValue,this.getTimeFormat()).format(this.dbTimeFormat) @@ -2972,6 +2972,7 @@ var dateValue=this.getDatePickerValue() var momentObj=moment.tz(dateValue+' '+timeValue,this.dbDateTimeFormat,this.timezone).tz(this.appTimezone) var lockerValue=momentObj.format(this.dbDateTimeFormat) this.$dataLocker.val(lockerValue)} +DatePicker.prototype.onChangeTimePicker=function(){this.$timePicker.trigger('change')} DatePicker.prototype.getTimePickerValue=function(){var value=this.$timePicker.val() if(!this.hasTime||!value){return moment.tz(this.appTimezone).tz(this.timezone).format(this.dbTimeFormat)} return moment(value,this.getTimeFormat()).format(this.dbTimeFormat)} From 9f8d8ec9fabad06e0eb4b222611910e608729074 Mon Sep 17 00:00:00 2001 From: Ben Thomson Date: Mon, 2 Dec 2019 09:27:25 +0800 Subject: [PATCH 15/88] Force ordering when list widget column is sorted When ordering is applied externally, ie. by a relation config, the orderBy call in the List widget simply adds an additional field to the ordering clauses, which prevents lists in these scenarios from being re-ordered correctly. This changes the order clause so that the ordering is reset and only the specified column is ordered when the user sorts a column. Developers can continue to use the `extendQuery` event to do specialised custom ordering if required. Fixes #4439. --- modules/backend/widgets/Lists.php | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/backend/widgets/Lists.php b/modules/backend/widgets/Lists.php index 862e96d40..f1946d747 100644 --- a/modules/backend/widgets/Lists.php +++ b/modules/backend/widgets/Lists.php @@ -532,6 +532,7 @@ class Lists extends WidgetBase $sortColumn = $column->relation . '_count'; } + $query->getQuery()->orders = []; $query->orderBy($sortColumn, $this->sortDirection); } From 905e5fbb726e761bb0c2d911ce753fa71e8a5cc5 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Mon, 2 Dec 2019 17:27:43 +1100 Subject: [PATCH 16/88] Fix for strict SQL languages (Pgsql) When the value is null [id >= ''] an error is thrown, not being an integer, while [id >= null] will return nil results, curiously. Here we emulate infinity by using a large-ish number instead of null. In future if this becomes a problem we may need to resort to multiple condition definitions as a more verbose solution, for example: - For when both are set (conditions: id >= ':min' and id <= ':max') - For when min is set (conditionsMin: id >= ':min') - For when max is set (conditionsMax: id >= ':max') --- modules/backend/widgets/Filter.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/backend/widgets/Filter.php b/modules/backend/widgets/Filter.php index a508007fe..3562cd49d 100644 --- a/modules/backend/widgets/Filter.php +++ b/modules/backend/widgets/Filter.php @@ -794,12 +794,11 @@ class Filter extends WidgetBase if (isset($min) || isset($max)) { /* * Condition - * */ if ($scopeConditions = $scope->conditions) { $query->whereRaw(DbDongle::parse(strtr($scopeConditions, [ - ':min' => $min, - ':max' => $max + ':min' => $min === null ? -999999999 : $min, + ':max' => $max === null ? 999999999 : $max ]))); } /* From ee4078ac44afb61848ed5973694282719ee6826e Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Wed, 4 Dec 2019 08:25:44 +1100 Subject: [PATCH 17/88] Tweak chart markup to not depend on an external class This fixes the rendering of the example markup. The line chart has no styles but just needs to know the display height --- modules/system/assets/ui/docs/chart.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/system/assets/ui/docs/chart.md b/modules/system/assets/ui/docs/chart.md index cd576fe6b..695da9368 100644 --- a/modules/system/assets/ui/docs/chart.md +++ b/modules/system/assets/ui/docs/chart.md @@ -27,10 +27,10 @@ The next example shows a line chart markup. Data sets are defined with the SPAN
    - From aa31667952c1873ee5eb589d2e57469174201606 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Wed, 4 Dec 2019 18:30:32 +1100 Subject: [PATCH 18/88] Make fake-infinity precise to 4 bytes --- modules/backend/widgets/Filter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/backend/widgets/Filter.php b/modules/backend/widgets/Filter.php index 3562cd49d..48153bf50 100644 --- a/modules/backend/widgets/Filter.php +++ b/modules/backend/widgets/Filter.php @@ -797,8 +797,8 @@ class Filter extends WidgetBase */ if ($scopeConditions = $scope->conditions) { $query->whereRaw(DbDongle::parse(strtr($scopeConditions, [ - ':min' => $min === null ? -999999999 : $min, - ':max' => $max === null ? 999999999 : $max + ':min' => $min === null ? -2147483647 : $min, + ':max' => $max === null ? 2147483647 : $max ]))); } /* From 4ab464cede7be05e3420f56994bd1e19c5c30e02 Mon Sep 17 00:00:00 2001 From: empower-josh <45080572+empower-josh@users.noreply.github.com> Date: Wed, 4 Dec 2019 01:29:24 -0700 Subject: [PATCH 19/88] Add public methods to access Lists widget's sort direction & column (#4746) Credit to @empower-josh. --- modules/backend/widgets/Lists.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/backend/widgets/Lists.php b/modules/backend/widgets/Lists.php index f1946d747..59fc31194 100644 --- a/modules/backend/widgets/Lists.php +++ b/modules/backend/widgets/Lists.php @@ -1517,7 +1517,7 @@ class Lists extends WidgetBase /** * Returns the current sorting column, saved in a session or cached. */ - protected function getSortColumn() + public function getSortColumn() { if (!$this->isSortable()) { return false; @@ -1564,6 +1564,14 @@ class Lists extends WidgetBase return $this->sortColumn; } + /* + * Returns the current sort direction or default of 'asc' + */ + public function getSortDirection() + { + return $this->sortDirection ?? 'asc'; + } + /** * Returns true if the column can be sorted. */ From 5f8a5454eeb401d5ac13b5b644353b8dcaf0de14 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 4 Dec 2019 02:36:51 -0600 Subject: [PATCH 20/88] Narrow the scope of when Lists orderBy conditions are reset. Credit to @bennothommo & @daftspunk Replaces: https://github.com/octobercms/october/commit/9f8d8ec9fabad06e0eb4b222611910e608729074. Refs: #4439 --- modules/backend/behaviors/RelationController.php | 5 ++++- modules/backend/widgets/Lists.php | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/backend/behaviors/RelationController.php b/modules/backend/behaviors/RelationController.php index 542e4c7cf..2306b5ffb 100644 --- a/modules/backend/behaviors/RelationController.php +++ b/modules/backend/behaviors/RelationController.php @@ -706,8 +706,11 @@ class RelationController extends ControllerBehavior }); } else { - $widget->bindEvent('list.extendQueryBefore', function ($query) { + $widget->bindEvent('list.extendQueryBefore', function ($query) use ($widget) { $this->relationObject->addDefinedConstraintsToQuery($query); + if ($widget->getSortColumn()) { + $query->getQuery()->orders = []; + } }); } diff --git a/modules/backend/widgets/Lists.php b/modules/backend/widgets/Lists.php index 59fc31194..cd2b96723 100644 --- a/modules/backend/widgets/Lists.php +++ b/modules/backend/widgets/Lists.php @@ -532,7 +532,6 @@ class Lists extends WidgetBase $sortColumn = $column->relation . '_count'; } - $query->getQuery()->orders = []; $query->orderBy($sortColumn, $this->sortDirection); } From 9dc54baddd27ad1df6554b44d6ca3c56227bf257 Mon Sep 17 00:00:00 2001 From: Philipp Fehr Date: Wed, 4 Dec 2019 17:14:19 +0100 Subject: [PATCH 21/88] Fix race condition when clearing recordfinder value (#4802) Credit to @TheFehr. Fixes #4800. --- .../backend/formwidgets/recordfinder/partials/_recordfinder.htm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/backend/formwidgets/recordfinder/partials/_recordfinder.htm b/modules/backend/formwidgets/recordfinder/partials/_recordfinder.htm index 65878029e..a347114ba 100644 --- a/modules/backend/formwidgets/recordfinder/partials/_recordfinder.htm +++ b/modules/backend/formwidgets/recordfinder/partials/_recordfinder.htm @@ -28,7 +28,7 @@ class="btn btn-default clear-record" data-request="getEventHandler('onClearRecord') ?>" data-request-confirm="" - data-request-success="$('#getId() ?>').trigger('change')" + data-request-success="var $locker = $('#getId() ?>'); $locker.val(''); $locker.trigger('change')" aria-label="Remove"> From 2b05d01c6ca2fc5e0a582a2b71bff3b75b9da960 Mon Sep 17 00:00:00 2001 From: Larry Barker Date: Thu, 5 Dec 2019 02:44:04 -0600 Subject: [PATCH 22/88] Support additional file name and path characters in media manager (#4564) * Support additional file name and path characters in media manager When working with abstract file names that may contain additional characters, such as quotes or ampersands, the media manager would throw an error. This PR adds two additional characters to the character whitelist. * Add unicode filename to tests --- modules/system/classes/MediaLibrary.php | 2 ++ tests/unit/system/classes/MediaLibraryTest.php | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/modules/system/classes/MediaLibrary.php b/modules/system/classes/MediaLibrary.php index 645825750..93b6fe58f 100644 --- a/modules/system/classes/MediaLibrary.php +++ b/modules/system/classes/MediaLibrary.php @@ -486,6 +486,8 @@ class MediaLibrary preg_quote(']', '/'), preg_quote(',', '/'), preg_quote('=', '/'), + preg_quote("'", '/'), + preg_quote('&', '/'), ]; if (!preg_match('/^[' . implode('', $regexWhitelist) . ']+$/iu', $path)) { diff --git a/tests/unit/system/classes/MediaLibraryTest.php b/tests/unit/system/classes/MediaLibraryTest.php index 55c027544..24bab1567 100644 --- a/tests/unit/system/classes/MediaLibraryTest.php +++ b/tests/unit/system/classes/MediaLibraryTest.php @@ -37,6 +37,11 @@ class MediaLibraryTest extends TestCase // @codingStandardsIgnoreLine ['one(two)[].ext'], ['one=(two)[].ext'], ['one_(two)[].ext'], + /* + Example of a unicode-based filename with a single quote + @see: https://github.com/octobercms/october/pull/4564 + */ + ['BG中国通讯期刊(Blend\'r)创刊号.pdf'], ]; } From a4359c91e9bd260e431bfc23d2dbd690ed15b793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20K=C3=BCndig?= Date: Sat, 7 Dec 2019 01:33:06 +0100 Subject: [PATCH 23/88] Explicitly pass focused item along as it is already known (#4807) If the DataTable widget is loaded in a Popup, the .focus() call does not seem to focus the target element correctly, which leads to the problem, that the updateCellFromFocusedItem method fails to find the focused item. This commit passes the target item along since it is already known. --- modules/backend/widgets/table/assets/js/build-min.js | 5 +++-- .../widgets/table/assets/js/table.processor.dropdown.js | 8 +++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/modules/backend/widgets/table/assets/js/build-min.js b/modules/backend/widgets/table/assets/js/build-min.js index 7bac16e12..8c8a8637b 100644 --- a/modules/backend/widgets/table/assets/js/build-min.js +++ b/modules/backend/widgets/table/assets/js/build-min.js @@ -871,7 +871,8 @@ return cachingKey} DropdownProcessor.prototype.getAbsolutePosition=function(element){var top=document.body.scrollTop,left=0 do{top+=element.offsetTop||0;top-=element.scrollTop||0;left+=element.offsetLeft||0;element=element.offsetParent;}while(element) return{top:top,left:left}} -DropdownProcessor.prototype.updateCellFromFocusedItem=function(){var focusedItem=this.findFocusedItem();this.setSelectedItem(focusedItem);} +DropdownProcessor.prototype.updateCellFromFocusedItem=function(focusedItem){if(!focusedItem){focusedItem=this.findFocusedItem();} +this.setSelectedItem(focusedItem);} DropdownProcessor.prototype.findSelectedItem=function(){if(this.itemListElement) return this.itemListElement.querySelector('ul li.selected') return null} @@ -883,7 +884,7 @@ DropdownProcessor.prototype.findFocusedItem=function(){if(this.itemListElement) return this.itemListElement.querySelector('ul li:focus') return null} DropdownProcessor.prototype.onItemClick=function(ev){var target=this.tableObj.getEventTarget(ev) -if(target.tagName=='LI'){target.focus();this.updateCellFromFocusedItem() +if(target.tagName=='LI'){target.focus();this.updateCellFromFocusedItem(target) this.hideDropdown()}} DropdownProcessor.prototype.onItemKeyDown=function(ev){if(!this.itemListElement) return diff --git a/modules/backend/widgets/table/assets/js/table.processor.dropdown.js b/modules/backend/widgets/table/assets/js/table.processor.dropdown.js index 466c71475..3155b8149 100644 --- a/modules/backend/widgets/table/assets/js/table.processor.dropdown.js +++ b/modules/backend/widgets/table/assets/js/table.processor.dropdown.js @@ -270,8 +270,10 @@ } } - DropdownProcessor.prototype.updateCellFromFocusedItem = function() { - var focusedItem = this.findFocusedItem(); + DropdownProcessor.prototype.updateCellFromFocusedItem = function(focusedItem) { + if (!focusedItem) { + focusedItem = this.findFocusedItem(); + } this.setSelectedItem(focusedItem); } @@ -309,7 +311,7 @@ if (target.tagName == 'LI') { target.focus(); - this.updateCellFromFocusedItem() + this.updateCellFromFocusedItem(target) this.hideDropdown() } } From b22021db3bc10db143dca665cdeffdf393d8315f Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Sat, 7 Dec 2019 11:37:06 +1100 Subject: [PATCH 24/88] Minor continuity change Let's save this for L6 upgrade. Although PHP 7 partially support this, we should revisit once the PHP version is bumped + better support for it --- modules/backend/traits/PreferenceMaker.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/backend/traits/PreferenceMaker.php b/modules/backend/traits/PreferenceMaker.php index 92ce00536..728329059 100644 --- a/modules/backend/traits/PreferenceMaker.php +++ b/modules/backend/traits/PreferenceMaker.php @@ -54,7 +54,7 @@ trait PreferenceMaker * * @return array */ - public function getUserPreferences(): array + public function getUserPreferences() { if (isset(self::$preferenceCache[$this->getPreferenceKey()])) { return self::$preferenceCache[$this->getPreferenceKey()]; @@ -112,7 +112,7 @@ trait PreferenceMaker * * @return string */ - protected function getPreferenceKey(): string + protected function getPreferenceKey() { $controller = (property_exists($this, 'controller') && $this->controller) ? $this->controller From e2284301e6dd98e76d8be50bc0fa9f2c750744b4 Mon Sep 17 00:00:00 2001 From: Ben Thomson Date: Sat, 7 Dec 2019 12:36:16 +0800 Subject: [PATCH 25/88] Update archive wording --- .github/workflows/archive.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/archive.yml b/.github/workflows/archive.yml index a76ad0def..8e1b5446e 100644 --- a/.github/workflows/archive.yml +++ b/.github/workflows/archive.yml @@ -13,7 +13,7 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 30 days-before-close: 3 - stale-issue-message: 'This issue will be closed and archived in 3 days, as there has been no activity in the last 30 days. If this issue is still relevant or you would like to see action on it, please respond and we will get the ball rolling.' + stale-issue-message: 'This issue will be closed and archived in 3 days, as there has been no activity in the last 30 days. If this issue is still relevant or you would like to see it actioned, please respond and we will re-open this issue.' stale-pr-message: 'This pull request will be closed and archived in 3 days, as there has been no activity in the last 30 days. If this is still being worked on, please respond and we will re-open this pull request.' stale-issue-label: 'Status: Archived' stale-pr-label: 'Status: Archived' From 1e449c82b686228acd426de365b89c486434948c Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Sun, 8 Dec 2019 10:27:59 +1100 Subject: [PATCH 26/88] Prevents erratic rendering issues This occurs due to a race condition in the rendering where the scrollbars enable and disable over and over because of a slow height calculation. Giving any height number appears to close the loop by never letting the height resolve to 0 Refs #4632 --- .../backend/widgets/mediamanager/assets/css/mediamanager.css | 2 +- .../widgets/mediamanager/assets/less/mediamanager.less | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/backend/widgets/mediamanager/assets/css/mediamanager.css b/modules/backend/widgets/mediamanager/assets/css/mediamanager.css index 74e0a857a..c98ed5a1f 100644 --- a/modules/backend/widgets/mediamanager/assets/css/mediamanager.css +++ b/modules/backend/widgets/mediamanager/assets/css/mediamanager.css @@ -73,7 +73,7 @@ div[data-control="media-manager"] .list-container p.no-data {padding:0 20px 20px div[data-control="media-manager"] .list-container li.no-data {padding-top:20px;display:block !important;width:100% !important;border:none !important;background:transparent !important;cursor:default !important} div[data-control="media-manager"] .list-container table.table.data tbody tr:not(.no-data):active td {background:#4ea5e0 !important} div[data-control="media-manager"] [data-control="item-list"] {position:relative;display:table-cell} -div[data-control="media-manager"] .control-scrollpad {position:absolute;left:0;top:0} +div[data-control="media-manager"] .control-scrollpad {position:absolute;left:0;top:0;min-height:300px} div[data-control="media-manager"] .scroll-wrapper {position:relative} div[data-control="media-manager"] table.table {table-layout:fixed;margin-bottom:0;white-space:nowrap} div[data-control="media-manager"] table.table div.no-wrap-text {overflow:hidden;text-overflow:ellipsis} diff --git a/modules/backend/widgets/mediamanager/assets/less/mediamanager.less b/modules/backend/widgets/mediamanager/assets/less/mediamanager.less index 9ff16d393..285215d97 100644 --- a/modules/backend/widgets/mediamanager/assets/less/mediamanager.less +++ b/modules/backend/widgets/mediamanager/assets/less/mediamanager.less @@ -395,6 +395,10 @@ div[data-control="media-manager"] { position: absolute; left: 0; top: 0; + + // Prevents erratic rendering issues when the height is + // sometimes calculated as 0 then repeatedly redrawn + min-height: 300px; } .scroll-wrapper { From 9b77e8d81a09e0a3012f2d045de45caab75f6847 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Mon, 9 Dec 2019 20:30:48 +1100 Subject: [PATCH 27/88] Bump min requirements to 7.0.8 This fixes PHP 7.4 support by pulling in Symfony packages at v3.4.36 --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index e08f1cdde..0a7777605 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ "source": "https://github.com/octobercms/october" }, "require": { - "php": ">=7.0", + "php": ">=7.0.8", "ext-mbstring": "*", "ext-openssl": "*", "october/rain": "~1.0", @@ -71,7 +71,7 @@ "config": { "preferred-install": "dist", "platform": { - "php": "7.0" + "php": "7.0.8" } }, "minimum-stability": "dev", From 4704f85096037f1b08f5c754bd6e81301fa14af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20K=C3=BCndig?= Date: Mon, 9 Dec 2019 10:45:26 +0100 Subject: [PATCH 28/88] Added lazy loading for backend form tabs (#4658) * Added lazy loading for backend form tabs --- modules/backend/classes/FormTabs.php | 9 +++++ modules/backend/widgets/Form.php | 31 ++++++++++++++++- .../widgets/form/assets/js/october.form.js | 31 ++++++++++++++++- .../widgets/form/partials/_form_tabs.htm | 25 ++++++++++---- .../widgets/form/partials/_form_tabs_lazy.htm | 33 +++++++++++++++++++ 5 files changed, 120 insertions(+), 9 deletions(-) create mode 100644 modules/backend/widgets/form/partials/_form_tabs_lazy.htm diff --git a/modules/backend/classes/FormTabs.php b/modules/backend/classes/FormTabs.php index 709a0306e..74ccae2f2 100644 --- a/modules/backend/classes/FormTabs.php +++ b/modules/backend/classes/FormTabs.php @@ -27,6 +27,11 @@ class FormTabs implements IteratorAggregate, ArrayAccess */ public $fields = []; + /** + * @var array Names of tabs to lazy load. + */ + public $lazy = []; + /** * @var string Default tab label to use when none is specified. */ @@ -106,6 +111,10 @@ class FormTabs implements IteratorAggregate, ArrayAccess if (array_key_exists('paneCssClass', $config)) { $this->paneCssClass = $config['paneCssClass']; } + + if (array_key_exists('lazy', $config)) { + $this->lazy = $config['lazy']; + } } /** diff --git a/modules/backend/widgets/Form.php b/modules/backend/widgets/Form.php index ce42ad24b..efda2e017 100644 --- a/modules/backend/widgets/Form.php +++ b/modules/backend/widgets/Form.php @@ -456,6 +456,35 @@ class Form extends WidgetBase return $result; } + /** + * Renders all fields of a tab in the target tab-pane. + * + * @return array + */ + public function onLazyLoadTab() + { + $target = post('target'); + $tabName = post('name'); + + $fields = array_get(optional($this->getTab('primary'))->fields, $tabName); + + return [ + $target => $this->makePartial('form_fields', ['fields' => $fields]), + ]; + } + + /** + * Helper method to convert a field name to a valid ID attribute. + * + * @param $input + * + * @return string + */ + public function nameToId($input) + { + return HtmlHelper::nameToId($input); + } + /** * Creates a flat array of form fields from the configuration. * Also slots fields in to their respective tabs. @@ -935,7 +964,7 @@ class Form extends WidgetBase } $widgetConfig = $this->makeConfig($field->config); - $widgetConfig->alias = $this->alias . studly_case(HtmlHelper::nameToId($field->fieldName)); + $widgetConfig->alias = $this->alias . studly_case($this->nameToId($field->fieldName)); $widgetConfig->sessionKey = $this->getSessionKey(); $widgetConfig->previewMode = $this->previewMode; $widgetConfig->model = $this->model; diff --git a/modules/backend/widgets/form/assets/js/october.form.js b/modules/backend/widgets/form/assets/js/october.form.js index 2a95c5aae..7b82f5759 100644 --- a/modules/backend/widgets/form/assets/js/october.form.js +++ b/modules/backend/widgets/form/assets/js/october.form.js @@ -34,6 +34,7 @@ this.bindDependants() this.bindCheckboxlist() this.toggleEmptyTabs() + this.bindLazyTabs() this.bindCollapsibleSections() this.$el.on('oc.triggerOn.afterUpdate', this.proxy(this.toggleEmptyTabs)) @@ -161,6 +162,34 @@ }) } + /* + * Render tab form fields once a lazy tab is selected. + */ + FormWidget.prototype.bindLazyTabs = function() { + this.$el.on('click', '.tab-lazy [data-toggle="tab"]', function() { + var $el = $(this) + $.request('form::onLazyLoadTab', { + data: { + target: $el.data('target'), + name: $el.data('tab-name'), + }, + success: function(data) { + this.success(data) + $el.parent().removeClass('tab-lazy') + // Trigger all input presets to populate new fields. + setTimeout(function() { + $('[data-input-preset]').each(function() { + var preset = $(this).data('oc.inputPreset') + if (preset && preset.$src) { + preset.$src.trigger('input') + } + }) + }, 0) + } + }) + }) + } + /* * Hides tabs that have no content, it is possible this can be * called multiple times in a single cycle due to input.trigger. @@ -184,7 +213,7 @@ /* * Check each tab pane for form field groups */ - $('.tab-pane', tabControl).each(function() { + $('.tab-pane:not(.lazy)', tabControl).each(function() { $('[data-target="#' + $(this).attr('id') + '"]', tabControl) .closest('li') .toggle(!!$('> .form-group:not(:empty):not(.hide)', $(this)).length) diff --git a/modules/backend/widgets/form/partials/_form_tabs.htm b/modules/backend/widgets/form/partials/_form_tabs.htm index 1cbddcca1..4ff1e0204 100644 --- a/modules/backend/widgets/form/partials/_form_tabs.htm +++ b/modules/backend/widgets/form/partials/_form_tabs.htm @@ -14,7 +14,10 @@
    - $fields): ?> -
    - makePartial('form_fields', ['fields' => $fields]) ?> -
    + $fields): + $lazy = in_array($name, $tabs->lazy); + ?> +
    + + makePartial('form_tabs_lazy', ['fields' => $fields]) ?> + + makePartial('form_fields', ['fields' => $fields]) ?> + +
    diff --git a/modules/backend/widgets/form/partials/_form_tabs_lazy.htm b/modules/backend/widgets/form/partials/_form_tabs_lazy.htm new file mode 100644 index 000000000..4d7534587 --- /dev/null +++ b/modules/backend/widgets/form/partials/_form_tabs_lazy.htm @@ -0,0 +1,33 @@ +
    +
    + +
    +
    +type, $ignoredTypes)) continue; + + $isMultiValue = is_array($field->value); + foreach (array_wrap($field->value) as $index => $value): + // Use array field names if the field has multiple values (repeater, checkboxlist, etc.). + $fieldName = $isMultiValue ? sprintf('%s[%s]', $field->getName(), $index) : $field->getName(); + + $valueIsArray = is_array($value); + foreach (array_wrap($value) as $index => $value): + // Set the correct array keys if the value is an array (repeater form fields). + $currentFieldName = $valueIsArray ? sprintf('%s[%s]', $fieldName, $index) : $fieldName; +?> + getAttributes() ?> + /> + + + From 63729e401b6e393311d483905c932e0a47e4ddc2 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Mon, 9 Dec 2019 20:58:16 +1100 Subject: [PATCH 29/88] Fixes typo in merge conflict --- modules/backend/widgets/form/partials/_form_tabs.htm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/backend/widgets/form/partials/_form_tabs.htm b/modules/backend/widgets/form/partials/_form_tabs.htm index 4ff1e0204..83d1fa25f 100644 --- a/modules/backend/widgets/form/partials/_form_tabs.htm +++ b/modules/backend/widgets/form/partials/_form_tabs.htm @@ -18,7 +18,7 @@ $lazy = in_array($name, $tabs->lazy); ?>
  • - + getIcon($name)): ?> From 664fd6ed6bbdc2af0690ddfc3dadf204c31e900d Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Mon, 9 Dec 2019 22:05:39 +1100 Subject: [PATCH 30/88] Add 7.4 test back in --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b6e29f9a4..072b795da 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: strategy: max-parallel: 6 matrix: - phpVersions: ['7.1', '7.2', '7.3'] + phpVersions: ['7.1', '7.2', '7.3', '7.4'] fail-fast: false name: PHP ${{ matrix.phpVersions }} steps: From 72f85ebc558a8e6fe514f6496f5393e535bac41b Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Mon, 9 Dec 2019 22:56:51 +1100 Subject: [PATCH 31/88] Monkey patch PHPUnit\Framework\MockObject\Generator This avoids "Function ReflectionType::__toString() is deprecated" warnings --- tests/bootstrap.php | 11 + tests/resources/patches/php-generator-7.php | 1184 +++++++++++++++++++ 2 files changed, 1195 insertions(+) create mode 100644 tests/resources/patches/php-generator-7.php diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 5ed3e1296..e7e53fd1a 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -19,3 +19,14 @@ $loader->addDirectories([ 'modules', 'plugins' ]); + +/* + * Monkey patch PHPUnit\Framework\MockObject\Generator to avoid + * "Function ReflectionType::__toString() is deprecated" warnings + */ +$generatorPatchPath = __DIR__ . '/resources/patches/php-generator-7.php'; +$generatorSourcePath = __DIR__ . '/../vendor/phpunit/phpunit-mock-objects/src/Generator.php'; + +if (file_exists($generatorSourcePath)) { + file_put_contents($generatorSourcePath, file_get_contents($generatorPatchPath)); +} diff --git a/tests/resources/patches/php-generator-7.php b/tests/resources/patches/php-generator-7.php new file mode 100644 index 000000000..4927424b9 --- /dev/null +++ b/tests/resources/patches/php-generator-7.php @@ -0,0 +1,1184 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Patched with: https://github.com/sebastianbergmann/phpunit/pull/3765/files + */ +namespace PHPUnit\Framework\MockObject; + +use Doctrine\Instantiator\Exception\ExceptionInterface as InstantiatorException; +use Doctrine\Instantiator\Instantiator; +use Iterator; +use IteratorAggregate; +use PHPUnit\Framework\Exception; +use PHPUnit\Util\InvalidArgumentHelper; +use ReflectionClass; +use ReflectionException; +use ReflectionMethod; +use SoapClient; +use Text_Template; +use Traversable; + +/** + * Mock Object Code Generator + */ +class Generator +{ + /** + * @var array + */ + private static $cache = []; + + /** + * @var Text_Template[] + */ + private static $templates = []; + + /** + * @var array + */ + private $blacklistedMethodNames = [ + '__CLASS__' => true, + '__DIR__' => true, + '__FILE__' => true, + '__FUNCTION__' => true, + '__LINE__' => true, + '__METHOD__' => true, + '__NAMESPACE__' => true, + '__TRAIT__' => true, + '__clone' => true, + '__halt_compiler' => true, + ]; + + /** + * Returns a mock object for the specified class. + * + * @param string|string[] $type + * @param array $methods + * @param array $arguments + * @param string $mockClassName + * @param bool $callOriginalConstructor + * @param bool $callOriginalClone + * @param bool $callAutoload + * @param bool $cloneArguments + * @param bool $callOriginalMethods + * @param object $proxyTarget + * @param bool $allowMockingUnknownTypes + * + * @return MockObject + * + * @throws Exception + * @throws RuntimeException + * @throws \PHPUnit\Framework\Exception + * @throws \ReflectionException + */ + public function getMock($type, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = true, $callOriginalMethods = false, $proxyTarget = null, $allowMockingUnknownTypes = true) + { + if (!\is_array($type) && !\is_string($type)) { + throw InvalidArgumentHelper::factory(1, 'array or string'); + } + + if (!\is_string($mockClassName)) { + throw InvalidArgumentHelper::factory(4, 'string'); + } + + if (!\is_array($methods) && null !== $methods) { + throw InvalidArgumentHelper::factory(2, 'array', $methods); + } + + if ($type === 'Traversable' || $type === '\\Traversable') { + $type = 'Iterator'; + } + + if (\is_array($type)) { + $type = \array_unique( + \array_map( + function ($type) { + if ($type === 'Traversable' || + $type === '\\Traversable' || + $type === '\\Iterator') { + return 'Iterator'; + } + + return $type; + }, + $type + ) + ); + } + + if (!$allowMockingUnknownTypes) { + if (\is_array($type)) { + foreach ($type as $_type) { + if (!\class_exists($_type, $callAutoload) && + !\interface_exists($_type, $callAutoload)) { + throw new RuntimeException( + \sprintf( + 'Cannot stub or mock class or interface "%s" which does not exist', + $_type + ) + ); + } + } + } else { + if (!\class_exists($type, $callAutoload) && + !\interface_exists($type, $callAutoload) + ) { + throw new RuntimeException( + \sprintf( + 'Cannot stub or mock class or interface "%s" which does not exist', + $type + ) + ); + } + } + } + + if (null !== $methods) { + foreach ($methods as $method) { + if (!\preg_match('~[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*~', $method)) { + throw new RuntimeException( + \sprintf( + 'Cannot stub or mock method with invalid name "%s"', + $method + ) + ); + } + } + + if ($methods !== \array_unique($methods)) { + throw new RuntimeException( + \sprintf( + 'Cannot stub or mock using a method list that contains duplicates: "%s" (duplicate: "%s")', + \implode(', ', $methods), + \implode(', ', \array_unique(\array_diff_assoc($methods, \array_unique($methods)))) + ) + ); + } + } + + if ($mockClassName !== '' && \class_exists($mockClassName, false)) { + $reflect = new ReflectionClass($mockClassName); + + if (!$reflect->implementsInterface(MockObject::class)) { + throw new RuntimeException( + \sprintf( + 'Class "%s" already exists.', + $mockClassName + ) + ); + } + } + + if ($callOriginalConstructor === false && $callOriginalMethods === true) { + throw new RuntimeException( + 'Proxying to original methods requires invoking the original constructor' + ); + } + + $mock = $this->generate( + $type, + $methods, + $mockClassName, + $callOriginalClone, + $callAutoload, + $cloneArguments, + $callOriginalMethods + ); + + return $this->getObject( + $mock['code'], + $mock['mockClassName'], + $type, + $callOriginalConstructor, + $callAutoload, + $arguments, + $callOriginalMethods, + $proxyTarget + ); + } + + /** + * @param string $code + * @param string $className + * @param array|string $type + * @param bool $callOriginalConstructor + * @param bool $callAutoload + * @param array $arguments + * @param bool $callOriginalMethods + * @param object $proxyTarget + * + * @return MockObject + * + * @throws \ReflectionException + * @throws RuntimeException + */ + private function getObject($code, $className, $type = '', $callOriginalConstructor = false, $callAutoload = false, array $arguments = [], $callOriginalMethods = false, $proxyTarget = null) + { + $this->evalClass($code, $className); + + if ($callOriginalConstructor && + \is_string($type) && + !\interface_exists($type, $callAutoload)) { + if (\count($arguments) === 0) { + $object = new $className; + } else { + $class = new ReflectionClass($className); + $object = $class->newInstanceArgs($arguments); + } + } else { + try { + $instantiator = new Instantiator; + $object = $instantiator->instantiate($className); + } catch (InstantiatorException $exception) { + throw new RuntimeException($exception->getMessage()); + } + } + + if ($callOriginalMethods) { + if (!\is_object($proxyTarget)) { + if (\count($arguments) === 0) { + $proxyTarget = new $type; + } else { + $class = new ReflectionClass($type); + $proxyTarget = $class->newInstanceArgs($arguments); + } + } + + $object->__phpunit_setOriginalObject($proxyTarget); + } + + return $object; + } + + /** + * @param string $code + * @param string $className + */ + private function evalClass($code, $className) + { + if (!\class_exists($className, false)) { + eval($code); + } + } + + /** + * Returns a mock object for the specified abstract class with all abstract + * methods of the class mocked. Concrete methods to mock can be specified with + * the last parameter + * + * @param string $originalClassName + * @param array $arguments + * @param string $mockClassName + * @param bool $callOriginalConstructor + * @param bool $callOriginalClone + * @param bool $callAutoload + * @param array $mockedMethods + * @param bool $cloneArguments + * + * @return MockObject + * + * @throws \ReflectionException + * @throws RuntimeException + * @throws Exception + */ + public function getMockForAbstractClass($originalClassName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = true) + { + if (!\is_string($originalClassName)) { + throw InvalidArgumentHelper::factory(1, 'string'); + } + + if (!\is_string($mockClassName)) { + throw InvalidArgumentHelper::factory(3, 'string'); + } + + if (\class_exists($originalClassName, $callAutoload) || + \interface_exists($originalClassName, $callAutoload)) { + $reflector = new ReflectionClass($originalClassName); + $methods = $mockedMethods; + + foreach ($reflector->getMethods() as $method) { + if ($method->isAbstract() && !\in_array($method->getName(), $methods)) { + $methods[] = $method->getName(); + } + } + + if (empty($methods)) { + $methods = null; + } + + return $this->getMock( + $originalClassName, + $methods, + $arguments, + $mockClassName, + $callOriginalConstructor, + $callOriginalClone, + $callAutoload, + $cloneArguments + ); + } + + throw new RuntimeException( + \sprintf('Class "%s" does not exist.', $originalClassName) + ); + } + + /** + * Returns a mock object for the specified trait with all abstract methods + * of the trait mocked. Concrete methods to mock can be specified with the + * `$mockedMethods` parameter. + * + * @param string $traitName + * @param array $arguments + * @param string $mockClassName + * @param bool $callOriginalConstructor + * @param bool $callOriginalClone + * @param bool $callAutoload + * @param array $mockedMethods + * @param bool $cloneArguments + * + * @return MockObject + * + * @throws \ReflectionException + * @throws RuntimeException + * @throws Exception + */ + public function getMockForTrait($traitName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = true) + { + if (!\is_string($traitName)) { + throw InvalidArgumentHelper::factory(1, 'string'); + } + + if (!\is_string($mockClassName)) { + throw InvalidArgumentHelper::factory(3, 'string'); + } + + if (!\trait_exists($traitName, $callAutoload)) { + throw new RuntimeException( + \sprintf( + 'Trait "%s" does not exist.', + $traitName + ) + ); + } + + $className = $this->generateClassName( + $traitName, + '', + 'Trait_' + ); + + $classTemplate = $this->getTemplate('trait_class.tpl'); + + $classTemplate->setVar( + [ + 'prologue' => 'abstract ', + 'class_name' => $className['className'], + 'trait_name' => $traitName + ] + ); + + $this->evalClass( + $classTemplate->render(), + $className['className'] + ); + + return $this->getMockForAbstractClass($className['className'], $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); + } + + /** + * Returns an object for the specified trait. + * + * @param string $traitName + * @param array $arguments + * @param string $traitClassName + * @param bool $callOriginalConstructor + * @param bool $callOriginalClone + * @param bool $callAutoload + * + * @return object + * + * @throws \ReflectionException + * @throws RuntimeException + * @throws Exception + */ + public function getObjectForTrait($traitName, array $arguments = [], $traitClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true) + { + if (!\is_string($traitName)) { + throw InvalidArgumentHelper::factory(1, 'string'); + } + + if (!\is_string($traitClassName)) { + throw InvalidArgumentHelper::factory(3, 'string'); + } + + if (!\trait_exists($traitName, $callAutoload)) { + throw new RuntimeException( + \sprintf( + 'Trait "%s" does not exist.', + $traitName + ) + ); + } + + $className = $this->generateClassName( + $traitName, + $traitClassName, + 'Trait_' + ); + + $classTemplate = $this->getTemplate('trait_class.tpl'); + + $classTemplate->setVar( + [ + 'prologue' => '', + 'class_name' => $className['className'], + 'trait_name' => $traitName + ] + ); + + return $this->getObject($classTemplate->render(), $className['className']); + } + + /** + * @param array|string $type + * @param array $methods + * @param string $mockClassName + * @param bool $callOriginalClone + * @param bool $callAutoload + * @param bool $cloneArguments + * @param bool $callOriginalMethods + * + * @return array + * + * @throws \ReflectionException + * @throws \PHPUnit\Framework\MockObject\RuntimeException + */ + public function generate($type, array $methods = null, $mockClassName = '', $callOriginalClone = true, $callAutoload = true, $cloneArguments = true, $callOriginalMethods = false) + { + if (\is_array($type)) { + \sort($type); + } + + if ($mockClassName === '') { + $key = \md5( + \is_array($type) ? \implode('_', $type) : $type . + \serialize($methods) . + \serialize($callOriginalClone) . + \serialize($cloneArguments) . + \serialize($callOriginalMethods) + ); + + if (isset(self::$cache[$key])) { + return self::$cache[$key]; + } + } + + $mock = $this->generateMock( + $type, + $methods, + $mockClassName, + $callOriginalClone, + $callAutoload, + $cloneArguments, + $callOriginalMethods + ); + + if (isset($key)) { + self::$cache[$key] = $mock; + } + + return $mock; + } + + /** + * @param string $wsdlFile + * @param string $className + * @param array $methods + * @param array $options + * + * @return string + * + * @throws RuntimeException + */ + public function generateClassFromWsdl($wsdlFile, $className, array $methods = [], array $options = []) + { + if (!\extension_loaded('soap')) { + throw new RuntimeException( + 'The SOAP extension is required to generate a mock object from WSDL.' + ); + } + + $options = \array_merge($options, ['cache_wsdl' => WSDL_CACHE_NONE]); + $client = new SoapClient($wsdlFile, $options); + $_methods = \array_unique($client->__getFunctions()); + unset($client); + + \sort($_methods); + + $methodTemplate = $this->getTemplate('wsdl_method.tpl'); + $methodsBuffer = ''; + + foreach ($_methods as $method) { + $nameStart = \strpos($method, ' ') + 1; + $nameEnd = \strpos($method, '('); + $name = \substr($method, $nameStart, $nameEnd - $nameStart); + + if (empty($methods) || \in_array($name, $methods)) { + $args = \explode( + ',', + \substr( + $method, + $nameEnd + 1, + \strpos($method, ')') - $nameEnd - 1 + ) + ); + + foreach (\range(0, \count($args) - 1) as $i) { + $args[$i] = \substr($args[$i], \strpos($args[$i], '$')); + } + + $methodTemplate->setVar( + [ + 'method_name' => $name, + 'arguments' => \implode(', ', $args) + ] + ); + + $methodsBuffer .= $methodTemplate->render(); + } + } + + $optionsBuffer = 'array('; + + foreach ($options as $key => $value) { + $optionsBuffer .= $key . ' => ' . $value; + } + + $optionsBuffer .= ')'; + + $classTemplate = $this->getTemplate('wsdl_class.tpl'); + $namespace = ''; + + if (\strpos($className, '\\') !== false) { + $parts = \explode('\\', $className); + $className = \array_pop($parts); + $namespace = 'namespace ' . \implode('\\', $parts) . ';' . "\n\n"; + } + + $classTemplate->setVar( + [ + 'namespace' => $namespace, + 'class_name' => $className, + 'wsdl' => $wsdlFile, + 'options' => $optionsBuffer, + 'methods' => $methodsBuffer + ] + ); + + return $classTemplate->render(); + } + + /** + * @param array|string $type + * @param array|null $methods + * @param string $mockClassName + * @param bool $callOriginalClone + * @param bool $callAutoload + * @param bool $cloneArguments + * @param bool $callOriginalMethods + * + * @return array + * + * @throws \InvalidArgumentException + * @throws \ReflectionException + * @throws RuntimeException + */ + private function generateMock($type, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods) + { + $methodReflections = []; + $classTemplate = $this->getTemplate('mocked_class.tpl'); + + $additionalInterfaces = []; + $cloneTemplate = ''; + $isClass = false; + $isInterface = false; + $isMultipleInterfaces = false; + + if (\is_array($type)) { + foreach ($type as $_type) { + if (!\interface_exists($_type, $callAutoload)) { + throw new RuntimeException( + \sprintf( + 'Interface "%s" does not exist.', + $_type + ) + ); + } + + $isMultipleInterfaces = true; + + $additionalInterfaces[] = $_type; + $typeClass = new ReflectionClass($this->generateClassName( + $_type, + $mockClassName, + 'Mock_' + )['fullClassName'] + ); + + foreach ($this->getClassMethods($_type) as $method) { + if (\in_array($method, $methods)) { + throw new RuntimeException( + \sprintf( + 'Duplicate method "%s" not allowed.', + $method + ) + ); + } + + $methodReflections[$method] = $typeClass->getMethod($method); + $methods[] = $method; + } + } + } + + $mockClassName = $this->generateClassName( + $type, + $mockClassName, + 'Mock_' + ); + + if (\class_exists($mockClassName['fullClassName'], $callAutoload)) { + $isClass = true; + } elseif (\interface_exists($mockClassName['fullClassName'], $callAutoload)) { + $isInterface = true; + } + + if (!$isClass && !$isInterface) { + $prologue = 'class ' . $mockClassName['originalClassName'] . "\n{\n}\n\n"; + + if (!empty($mockClassName['namespaceName'])) { + $prologue = 'namespace ' . $mockClassName['namespaceName'] . + " {\n\n" . $prologue . "}\n\n" . + "namespace {\n\n"; + + $epilogue = "\n\n}"; + } + + $cloneTemplate = $this->getTemplate('mocked_clone.tpl'); + } else { + $class = new ReflectionClass($mockClassName['fullClassName']); + + if ($class->isFinal()) { + throw new RuntimeException( + \sprintf( + 'Class "%s" is declared "final" and cannot be mocked.', + $mockClassName['fullClassName'] + ) + ); + } + + if ($class->hasMethod('__clone')) { + $cloneMethod = $class->getMethod('__clone'); + + if (!$cloneMethod->isFinal()) { + if ($callOriginalClone && !$isInterface) { + $cloneTemplate = $this->getTemplate('unmocked_clone.tpl'); + } else { + $cloneTemplate = $this->getTemplate('mocked_clone.tpl'); + } + } + } else { + $cloneTemplate = $this->getTemplate('mocked_clone.tpl'); + } + } + + if (\is_object($cloneTemplate)) { + $cloneTemplate = $cloneTemplate->render(); + } + + if (\is_array($methods) && empty($methods) && + ($isClass || $isInterface)) { + $methods = $this->getClassMethods($mockClassName['fullClassName']); + } + + if (!\is_array($methods)) { + $methods = []; + } + + $mockedMethods = ''; + $configurable = []; + + foreach ($methods as $methodName) { + if ($methodName !== '__construct' && $methodName !== '__clone') { + $configurable[] = \strtolower($methodName); + } + } + + if (isset($class)) { + // https://github.com/sebastianbergmann/phpunit-mock-objects/issues/103 + if ($isInterface && $class->implementsInterface(Traversable::class) && + !$class->implementsInterface(Iterator::class) && + !$class->implementsInterface(IteratorAggregate::class)) { + $additionalInterfaces[] = Iterator::class; + $methods = \array_merge($methods, $this->getClassMethods(Iterator::class)); + } + + foreach ($methods as $methodName) { + try { + $method = $class->getMethod($methodName); + + if ($this->canMockMethod($method)) { + $mockedMethods .= $this->generateMockedMethodDefinitionFromExisting( + $method, + $cloneArguments, + $callOriginalMethods + ); + } + } catch (ReflectionException $e) { + $mockedMethods .= $this->generateMockedMethodDefinition( + $mockClassName['fullClassName'], + $methodName, + $cloneArguments + ); + } + } + } elseif ($isMultipleInterfaces) { + foreach ($methods as $methodName) { + if ($this->canMockMethod($methodReflections[$methodName])) { + $mockedMethods .= $this->generateMockedMethodDefinitionFromExisting( + $methodReflections[$methodName], + $cloneArguments, + $callOriginalMethods + ); + } + } + } else { + foreach ($methods as $methodName) { + $mockedMethods .= $this->generateMockedMethodDefinition( + $mockClassName['fullClassName'], + $methodName, + $cloneArguments + ); + } + } + + $method = ''; + + if (!\in_array('method', $methods) && (!isset($class) || !$class->hasMethod('method'))) { + $methodTemplate = $this->getTemplate('mocked_class_method.tpl'); + + $method = $methodTemplate->render(); + } + + $classTemplate->setVar( + [ + 'prologue' => $prologue ?? '', + 'epilogue' => $epilogue ?? '', + 'class_declaration' => $this->generateMockClassDeclaration( + $mockClassName, + $isInterface, + $additionalInterfaces + ), + 'clone' => $cloneTemplate, + 'mock_class_name' => $mockClassName['className'], + 'mocked_methods' => $mockedMethods, + 'method' => $method, + 'configurable' => '[' . \implode(', ', \array_map(function ($m) { + return '\'' . $m . '\''; + }, $configurable)) . ']' + ] + ); + + return [ + 'code' => $classTemplate->render(), + 'mockClassName' => $mockClassName['className'] + ]; + } + + /** + * @param array|string $type + * @param string $className + * @param string $prefix + * + * @return array + */ + private function generateClassName($type, $className, $prefix) + { + if (\is_array($type)) { + $type = \implode('_', $type); + } + + if ($type[0] === '\\') { + $type = \substr($type, 1); + } + + $classNameParts = \explode('\\', $type); + + if (\count($classNameParts) > 1) { + $type = \array_pop($classNameParts); + $namespaceName = \implode('\\', $classNameParts); + $fullClassName = $namespaceName . '\\' . $type; + } else { + $namespaceName = ''; + $fullClassName = $type; + } + + if ($className === '') { + do { + $className = $prefix . $type . '_' . + \substr(\md5(\mt_rand()), 0, 8); + } while (\class_exists($className, false)); + } + + return [ + 'className' => $className, + 'originalClassName' => $type, + 'fullClassName' => $fullClassName, + 'namespaceName' => $namespaceName + ]; + } + + /** + * @param array $mockClassName + * @param bool $isInterface + * @param array $additionalInterfaces + * + * @return string + */ + private function generateMockClassDeclaration(array $mockClassName, $isInterface, array $additionalInterfaces = []) + { + $buffer = 'class '; + + $additionalInterfaces[] = MockObject::class; + $interfaces = \implode(', ', $additionalInterfaces); + + if ($isInterface) { + $buffer .= \sprintf( + '%s implements %s', + $mockClassName['className'], + $interfaces + ); + + if (!\in_array($mockClassName['originalClassName'], $additionalInterfaces)) { + $buffer .= ', '; + + if (!empty($mockClassName['namespaceName'])) { + $buffer .= $mockClassName['namespaceName'] . '\\'; + } + + $buffer .= $mockClassName['originalClassName']; + } + } else { + $buffer .= \sprintf( + '%s extends %s%s implements %s', + $mockClassName['className'], + !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '', + $mockClassName['originalClassName'], + $interfaces + ); + } + + return $buffer; + } + + /** + * @param ReflectionMethod $method + * @param bool $cloneArguments + * @param bool $callOriginalMethods + * + * @return string + * + * @throws \PHPUnit\Framework\MockObject\RuntimeException + */ + private function generateMockedMethodDefinitionFromExisting(ReflectionMethod $method, $cloneArguments, $callOriginalMethods) + { + if ($method->isPrivate()) { + $modifier = 'private'; + } elseif ($method->isProtected()) { + $modifier = 'protected'; + } else { + $modifier = 'public'; + } + + if ($method->isStatic()) { + $modifier .= ' static'; + } + + if ($method->returnsReference()) { + $reference = '&'; + } else { + $reference = ''; + } + + if ($method->hasReturnType()) { + $returnType = $method->getReturnType()->getName(); + } else { + $returnType = ''; + } + + if (\preg_match('#\*[ \t]*+@deprecated[ \t]*+(.*?)\r?+\n[ \t]*+\*(?:[ \t]*+@|/$)#s', $method->getDocComment(), $deprecation)) { + $deprecation = \trim(\preg_replace('#[ \t]*\r?\n[ \t]*+\*[ \t]*+#', ' ', $deprecation[1])); + } else { + $deprecation = false; + } + + return $this->generateMockedMethodDefinition( + $method->getDeclaringClass()->getName(), + $method->getName(), + $cloneArguments, + $modifier, + $this->getMethodParameters($method), + $this->getMethodParameters($method, true), + $returnType, + $reference, + $callOriginalMethods, + $method->isStatic(), + $deprecation, + $method->hasReturnType() && PHP_VERSION_ID >= 70100 && $method->getReturnType()->allowsNull() + ); + } + + /** + * @param string $className + * @param string $methodName + * @param bool $cloneArguments + * @param string $modifier + * @param string $argumentsForDeclaration + * @param string $argumentsForCall + * @param string $returnType + * @param string $reference + * @param bool $callOriginalMethods + * @param bool $static + * @param bool|string $deprecation + * @param bool $allowsReturnNull + * + * @return string + * + * @throws \InvalidArgumentException + */ + private function generateMockedMethodDefinition($className, $methodName, $cloneArguments = true, $modifier = 'public', $argumentsForDeclaration = '', $argumentsForCall = '', $returnType = '', $reference = '', $callOriginalMethods = false, $static = false, $deprecation = false, $allowsReturnNull = false) + { + if ($static) { + $templateFile = 'mocked_static_method.tpl'; + } else { + if ($returnType === 'void') { + $templateFile = \sprintf( + '%s_method_void.tpl', + $callOriginalMethods ? 'proxied' : 'mocked' + ); + } else { + $templateFile = \sprintf( + '%s_method.tpl', + $callOriginalMethods ? 'proxied' : 'mocked' + ); + } + } + + // Mocked interfaces returning 'self' must explicitly declare the + // interface name as the return type. See + // https://bugs.php.net/bug.php?id=70722 + if ($returnType === 'self') { + $returnType = $className; + } + + if (false !== $deprecation) { + $deprecation = "The $className::$methodName method is deprecated ($deprecation)."; + $deprecationTemplate = $this->getTemplate('deprecation.tpl'); + + $deprecationTemplate->setVar( + [ + 'deprecation' => \var_export($deprecation, true), + ] + ); + + $deprecation = $deprecationTemplate->render(); + } + + $template = $this->getTemplate($templateFile); + + $template->setVar( + [ + 'arguments_decl' => $argumentsForDeclaration, + 'arguments_call' => $argumentsForCall, + 'return_delim' => $returnType ? ': ' : '', + 'return_type' => $allowsReturnNull ? '?' . $returnType : $returnType, + 'arguments_count' => !empty($argumentsForCall) ? \substr_count($argumentsForCall, ',') + 1 : 0, + 'class_name' => $className, + 'method_name' => $methodName, + 'modifier' => $modifier, + 'reference' => $reference, + 'clone_arguments' => $cloneArguments ? 'true' : 'false', + 'deprecation' => $deprecation + ] + ); + + return $template->render(); + } + + /** + * @param ReflectionMethod $method + * + * @return bool + * + * @throws \ReflectionException + */ + private function canMockMethod(ReflectionMethod $method) + { + return !($method->isConstructor() || $method->isFinal() || $method->isPrivate() || $this->isMethodNameBlacklisted($method->getName())); + } + + /** + * Returns whether a method name is blacklisted + * + * @param string $name + * + * @return bool + */ + private function isMethodNameBlacklisted($name) + { + return isset($this->blacklistedMethodNames[$name]); + } + + /** + * Returns the parameters of a function or method. + * + * @param ReflectionMethod $method + * @param bool $forCall + * + * @return string + * + * @throws RuntimeException + */ + private function getMethodParameters(ReflectionMethod $method, $forCall = false) + { + $parameters = []; + + foreach ($method->getParameters() as $i => $parameter) { + $name = '$' . $parameter->getName(); + + /* Note: PHP extensions may use empty names for reference arguments + * or "..." for methods taking a variable number of arguments. + */ + if ($name === '$' || $name === '$...') { + $name = '$arg' . $i; + } + + if ($parameter->isVariadic()) { + if ($forCall) { + continue; + } + + $name = '...' . $name; + } + + $nullable = ''; + $default = ''; + $reference = ''; + $typeDeclaration = ''; + + if (!$forCall) { + if (PHP_VERSION_ID >= 70100 && $parameter->hasType() && $parameter->allowsNull()) { + $nullable = '?'; + } + + if ($parameter->hasType() && $parameter->getType()->getName() !== 'self') { + $typeDeclaration = $parameter->getType()->getName() . ' '; + } elseif ($parameter->isArray()) { + $typeDeclaration = 'array '; + } elseif ($parameter->isCallable()) { + $typeDeclaration = 'callable '; + } else { + try { + $class = $parameter->getClass(); + } catch (ReflectionException $e) { + throw new RuntimeException( + \sprintf( + 'Cannot mock %s::%s() because a class or ' . + 'interface used in the signature is not loaded', + $method->getDeclaringClass()->getName(), + $method->getName() + ), + 0, + $e + ); + } + + if ($class !== null) { + $typeDeclaration = $class->getName() . ' '; + } + } + + if (!$parameter->isVariadic()) { + if ($parameter->isDefaultValueAvailable()) { + $value = $parameter->getDefaultValueConstantName(); + + if ($value === null) { + $value = \var_export($parameter->getDefaultValue(), true); + } elseif (!\defined($value)) { + $rootValue = \preg_replace('/^.*\\\\/', '', $value); + $value = \defined($rootValue) ? $rootValue : $value; + } + + $default = ' = ' . $value; + } elseif ($parameter->isOptional()) { + $default = ' = null'; + } + } + } + + if ($parameter->isPassedByReference()) { + $reference = '&'; + } + + $parameters[] = $nullable . $typeDeclaration . $reference . $name . $default; + } + + return \implode(', ', $parameters); + } + + /** + * @param string $className + * + * @return array + * + * @throws \ReflectionException + */ + public function getClassMethods($className) + { + $class = new ReflectionClass($className); + $methods = []; + + foreach ($class->getMethods() as $method) { + if ($method->isPublic() || $method->isAbstract()) { + $methods[] = $method->getName(); + } + } + + return $methods; + } + + /** + * @param string $template + * + * @return Text_Template + * + * @throws \InvalidArgumentException + */ + private function getTemplate($template) + { + $filename = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR . $template; + + if (!isset(self::$templates[$filename])) { + self::$templates[$filename] = new Text_Template($filename); + } + + return self::$templates[$filename]; + } +} From 15036fd1d9bb6623f90943e11e7202fb77844366 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Mon, 9 Dec 2019 23:03:03 +1100 Subject: [PATCH 32/88] Tell codesniffer to ignore this patched file --- tests/resources/patches/php-generator-7.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/resources/patches/php-generator-7.php b/tests/resources/patches/php-generator-7.php index 4927424b9..677680d69 100644 --- a/tests/resources/patches/php-generator-7.php +++ b/tests/resources/patches/php-generator-7.php @@ -1,4 +1,5 @@ Date: Mon, 9 Dec 2019 23:16:06 +1100 Subject: [PATCH 33/88] October is a platform, powered by a framework :-) Ref https://octobercms.com/blog/post/putting-octobercms-words --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 362c4aab1..de8b75333 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ October

    -[October](https://octobercms.com) is a Content Management Framework (CMF) and web platform whose sole purpose is to make your development workflow simple again. It was born out of frustration with existing systems. We feel building websites has become a convoluted and confusing process that leaves developers unsatisfied. We want to turn you around to the simpler side and get back to basics. +[October](https://octobercms.com) is a Content Management System (CMS) and web platform whose sole purpose is to make your development workflow simple again. It was born out of frustration with existing systems. We feel building websites has become a convoluted and confusing process that leaves developers unsatisfied. We want to turn you around to the simpler side and get back to basics. October's mission is to show the world that web development is not rocket science. From 864816f7f2b45201e76f4f8161530ad5f84aeb2e Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 9 Dec 2019 08:05:50 -0500 Subject: [PATCH 34/88] Make CMS object code editor read-only in safe mode (#4769) Adds a dismissable message to the CMS object code editor indicating that the PHP code section of a CMS object cannot be edited when `cms.enableSafeMode` is `true` (or when debugging is disabled if `null`). Credit to @mjauvin. --- modules/cms/classes/CmsCompoundObject.php | 8 ++------ modules/cms/classes/layout/fields.yaml | 6 ++++++ modules/cms/classes/page/fields.yaml | 6 ++++++ modules/cms/classes/partial/fields.yaml | 6 ++++++ modules/cms/controllers/Index.php | 15 +++++++++++++++ .../cms/controllers/index/_safemode_notice.htm | 6 ++++++ modules/cms/helpers/Cms.php | 10 ++++++++++ modules/cms/lang/en/lang.php | 2 +- 8 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 modules/cms/controllers/index/_safemode_notice.htm diff --git a/modules/cms/classes/CmsCompoundObject.php b/modules/cms/classes/CmsCompoundObject.php index 856d7d258..5705515bc 100644 --- a/modules/cms/classes/CmsCompoundObject.php +++ b/modules/cms/classes/CmsCompoundObject.php @@ -7,6 +7,7 @@ use Config; use Cms\Twig\Loader as TwigLoader; use Cms\Twig\Extension as CmsTwigExtension; use Cms\Components\ViewBag; +use Cms\Helpers\Cms as CmsHelpers; use System\Twig\Extension as SystemTwigExtension; use October\Rain\Halcyon\Processors\SectionParser; use Twig\Source as TwigSource; @@ -143,12 +144,7 @@ class CmsCompoundObject extends CmsObject */ protected function checkSafeMode() { - $safeMode = Config::get('cms.enableSafeMode', null); - if ($safeMode === null) { - $safeMode = !Config::get('app.debug', false); - } - - if ($safeMode && $this->isDirty('code') && strlen(trim($this->code))) { + if (CmsHelpers::safeModeEnabled() && $this->isDirty('code') && strlen(trim($this->code))) { throw new ApplicationException(Lang::get('cms::lang.cms_object.safe_mode_enabled')); } } diff --git a/modules/cms/classes/layout/fields.yaml b/modules/cms/classes/layout/fields.yaml index 3c02f16d3..62c292114 100644 --- a/modules/cms/classes/layout/fields.yaml +++ b/modules/cms/classes/layout/fields.yaml @@ -32,6 +32,12 @@ secondaryTabs: type: codeeditor language: twig + safemode_notice: + tab: cms::lang.editor.code + type: partial + hidden: true + cssClass: p-b-0 + code: tab: cms::lang.editor.code stretch: true diff --git a/modules/cms/classes/page/fields.yaml b/modules/cms/classes/page/fields.yaml index 44c56a5e9..ac754d31e 100644 --- a/modules/cms/classes/page/fields.yaml +++ b/modules/cms/classes/page/fields.yaml @@ -74,6 +74,12 @@ secondaryTabs: type: codeeditor language: twig + safemode_notice: + tab: cms::lang.editor.code + type: partial + hidden: true + cssClass: p-b-0 + code: tab: cms::lang.editor.code stretch: true diff --git a/modules/cms/classes/partial/fields.yaml b/modules/cms/classes/partial/fields.yaml index 4d23dba15..9e5f0a838 100644 --- a/modules/cms/classes/partial/fields.yaml +++ b/modules/cms/classes/partial/fields.yaml @@ -32,6 +32,12 @@ secondaryTabs: type: codeeditor language: twig + safemode_notice: + tab: cms::lang.editor.code + type: partial + hidden: true + cssClass: p-b-0 + code: tab: cms::lang.editor.code stretch: true diff --git a/modules/cms/controllers/Index.php b/modules/cms/controllers/Index.php index c0ecc5ae7..4f9d2e78d 100644 --- a/modules/cms/controllers/Index.php +++ b/modules/cms/controllers/Index.php @@ -4,6 +4,7 @@ use Url; use Lang; use Flash; use Config; +use Event; use Request; use Exception; use BackendMenu; @@ -20,6 +21,7 @@ use Cms\Classes\CmsObject; use Cms\Classes\CmsCompoundObject; use Cms\Classes\ComponentManager; use Cms\Classes\ComponentPartial; +use Cms\Helpers\Cms as CmsHelpers; use Backend\Classes\Controller; use System\Helpers\DateTime; use October\Rain\Router\Router as RainRouter; @@ -59,6 +61,19 @@ class Index extends Controller { parent::__construct(); + Event::listen('backend.form.extendFieldsBefore', function ($widget) { + if (!$widget->getController() instanceof Index) { + return; + } + if (!$widget->model instanceof CmsCompoundObject) { + return; + } + if (key_exists('code', $widget->secondaryTabs['fields']) && CmsHelpers::safeModeEnabled()) { + $widget->secondaryTabs['fields']['safemode_notice']['hidden'] = false; + $widget->secondaryTabs['fields']['code']['readOnly'] = true; + }; + }); + BackendMenu::setContext('October.Cms', 'cms', true); try { diff --git a/modules/cms/controllers/index/_safemode_notice.htm b/modules/cms/controllers/index/_safemode_notice.htm new file mode 100644 index 000000000..b9e320c89 --- /dev/null +++ b/modules/cms/controllers/index/_safemode_notice.htm @@ -0,0 +1,6 @@ +
    +
    + +

    +
    +
    diff --git a/modules/cms/helpers/Cms.php b/modules/cms/helpers/Cms.php index e8195d2a5..34332c2ea 100644 --- a/modules/cms/helpers/Cms.php +++ b/modules/cms/helpers/Cms.php @@ -2,6 +2,7 @@ use Url; use Route; +use Config; /** * CMS Helper @@ -35,4 +36,13 @@ class Cms return Url::to($path); } + + public static function safeModeEnabled() + { + $safeMode = Config::get('cms.enableSafeMode', null); + if ($safeMode === null) { + $safeMode = !Config::get('app.debug', false); + } + return $safeMode; + } } diff --git a/modules/cms/lang/en/lang.php b/modules/cms/lang/en/lang.php index a6096b04d..d3e28b27e 100644 --- a/modules/cms/lang/en/lang.php +++ b/modules/cms/lang/en/lang.php @@ -11,7 +11,7 @@ return [ 'error_deleting' => "Error deleting the template file ':name'. Please check write permissions.", 'delete_success' => 'Templates deleted: :count.', 'file_name_required' => 'The File Name field is required.', - 'safe_mode_enabled' => 'Safe mode is currently enabled.' + 'safe_mode_enabled' => 'Safe mode is currently enabled. Editing the PHP code of CMS templates is disabled.' ], 'dashboard' => [ 'active_theme' => [ From 260e1f503fe18588f099de5efc928cfe941eed77 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Tue, 10 Dec 2019 03:12:12 +1100 Subject: [PATCH 35/88] Rollback d31006ae1a1f5a709e9a100d0096a5633ab820b5 --- modules/cms/classes/Controller.php | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/modules/cms/classes/Controller.php b/modules/cms/classes/Controller.php index 777a89147..ecb60c448 100644 --- a/modules/cms/classes/Controller.php +++ b/modules/cms/classes/Controller.php @@ -146,17 +146,6 @@ class Controller $url = '/'; } - /* - * Check security token. - * - * Note: Ignore AJAX requests until a CSRF policy introduced. - * - * @see \System\Traits\SecurityController - */ - if (!Request::ajax() && !$this->verifyCsrfToken()) { - return Response::make(Lang::get('system::lang.page.invalid_token.label'), 403); - } - /* * Hidden page */ @@ -389,6 +378,7 @@ class Controller if ( $useAjax && ($handler = post('_handler')) && + $this->verifyCsrfToken() && ($handlerResponse = $this->runAjaxHandler($handler)) && $handlerResponse !== true ) { From 70e57120d0e65d7b40bf6b89ebe42afeeaa1fbf3 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Tue, 10 Dec 2019 19:59:49 +1100 Subject: [PATCH 36/88] ApplicationException -> SystemException This appears to be a typoe. It doesn't make sense to ever log "user errors", only "system errors" Fixes #4569 --- modules/system/classes/ErrorHandler.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/system/classes/ErrorHandler.php b/modules/system/classes/ErrorHandler.php index 910f044e8..4a5836f05 100644 --- a/modules/system/classes/ErrorHandler.php +++ b/modules/system/classes/ErrorHandler.php @@ -7,7 +7,7 @@ use Cms\Classes\Theme; use Cms\Classes\Router; use Cms\Classes\Controller as CmsController; use October\Rain\Exception\ErrorHandler as ErrorHandlerBase; -use October\Rain\Exception\ApplicationException; +use October\Rain\Exception\SystemException; /** * System Error Handler, this class handles application exception events. @@ -36,12 +36,12 @@ class ErrorHandler extends ErrorHandlerBase /** * We are about to display an error page to the user, - * if it is an ApplicationException, this event should be logged. + * if it is an SystemException, this event should be logged. * @return void */ public function beforeHandleError($exception) { - if ($exception instanceof ApplicationException) { + if ($exception instanceof SystemException) { Log::error($exception); } } From 91459559785d90302ab5832c4683010908289ae7 Mon Sep 17 00:00:00 2001 From: Robin Bonnes Date: Tue, 10 Dec 2019 10:06:10 +0100 Subject: [PATCH 37/88] Adds the ability to override the column header label in import/export. (#4737) Now we can use the `backend.list.overrideHeaderValue` event also in the import/export. --- modules/backend/behaviors/ImportExportController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/backend/behaviors/ImportExportController.php b/modules/backend/behaviors/ImportExportController.php index 61ac7551d..88e063ce7 100644 --- a/modules/backend/behaviors/ImportExportController.php +++ b/modules/backend/behaviors/ImportExportController.php @@ -629,7 +629,7 @@ class ImportExportController extends ControllerBehavior $headers = []; $columns = $widget->getVisibleColumns(); foreach ($columns as $column) { - $headers[] = Lang::get($column->label); + $headers[] = $widget->getHeaderValue($column); } $csv->insertOne($headers); From 8abed1794f73302b517a8b85541f81bb8e63a301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Ora=C5=BEem?= Date: Tue, 10 Dec 2019 10:17:42 +0100 Subject: [PATCH 38/88] Slovenian language added (#4796) * Add Slovenian language --- .../assets/vendor/froala/js/languages/sl.js | 280 ++++++++ modules/backend/lang/sl/lang.php | 628 ++++++++++++++++++ modules/backend/models/Preference.php | 1 + modules/cms/lang/sl/lang.php | 301 +++++++++ modules/system/assets/js/lang/lang.sl.js | 299 +++++++++ modules/system/lang/ar/lang.php | 1 + modules/system/lang/en/lang.php | 1 + modules/system/lang/et/lang.php | 1 + modules/system/lang/fa/lang.php | 1 + modules/system/lang/fi/lang.php | 1 + modules/system/lang/lt/lang.php | 1 + modules/system/lang/nl/lang.php | 1 + modules/system/lang/pt-pt/lang.php | 1 + modules/system/lang/sk/lang.php | 1 + modules/system/lang/sl/client.php | 115 ++++ modules/system/lang/sl/lang.php | 478 +++++++++++++ modules/system/lang/sl/validation.php | 121 ++++ modules/system/lang/tr/lang.php | 1 + modules/system/lang/vn/lang.php | 1 + 19 files changed, 2234 insertions(+) create mode 100644 modules/backend/formwidgets/richeditor/assets/vendor/froala/js/languages/sl.js create mode 100644 modules/backend/lang/sl/lang.php create mode 100644 modules/cms/lang/sl/lang.php create mode 100644 modules/system/assets/js/lang/lang.sl.js create mode 100644 modules/system/lang/sl/client.php create mode 100644 modules/system/lang/sl/lang.php create mode 100644 modules/system/lang/sl/validation.php diff --git a/modules/backend/formwidgets/richeditor/assets/vendor/froala/js/languages/sl.js b/modules/backend/formwidgets/richeditor/assets/vendor/froala/js/languages/sl.js new file mode 100644 index 000000000..86da844f8 --- /dev/null +++ b/modules/backend/formwidgets/richeditor/assets/vendor/froala/js/languages/sl.js @@ -0,0 +1,280 @@ +/*! + * froala_editor v2.9.3 (https://www.froala.com/wysiwyg-editor) + * License https://froala.com/wysiwyg-editor/terms/ + * Copyright 2014-2019 Froala Labs + */ + +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } else if (typeof module === 'object' && module.exports) { + // Node/CommonJS + module.exports = function( root, jQuery ) { + if ( jQuery === undefined ) { + // require('jQuery') returns a factory that requires window to + // build a jQuery instance, we normalize how we use modules + // that require this pattern but the window provided is a noop + // if it's defined (how jquery works) + if ( typeof window !== 'undefined' ) { + jQuery = require('jquery'); + } + else { + jQuery = require('jquery')(root); + } + } + return factory(jQuery); + }; + } else { + // Browser globals + factory(window.jQuery); + } +}(function ($) { +/** + * Slovenian + */ + +$.FE.LANGUAGE['sl'] = { + translation: { + // Place holder + "Type something": "Nekaj vtipkajte", + + // Basic formatting + "Bold": "Krepko", + "Italic": "Poševno", + "Underline": "Podčrtano", + "Strikethrough": "Prečrtano", + + // Main buttons + "Insert": "Vstavi", + "Delete": "Izbriši", + "Cancel": "Prekliči", + "OK": "OK", + "Back": "Nazaj", + "Remove": "Odstrani", + "More": "Več", + "Update": "Posodobi", + "Style": "Slog", + + // Font + "Font Family": "Oblika pisave", + "Font Size": "Velikost pisave", + + // Colors + "Colors": "Barve", + "Background": "Ozadje", + "Text": "Besedilo", + "HEX Color": "HEX barva", + + // Paragraphs + "Paragraph Format": "Oblika odstavka", + "Normal": "Normalno", + "Code": "Koda", + "Heading 1": "Naslov 1", + "Heading 2": "Naslov 2", + "Heading 3": "Naslov 3", + "Heading 4": "Naslov 4", + + // Style + "Paragraph Style": "Slog odstavka", + "Inline Style": "Vrstični slog", + + // Alignment + "Align": "Poravnava", + "Align Left": "Leva poravnava", + "Align Center": "Sredinska poravnava", + "Align Right": "Desna poravnava", + "Align Justify": "Obojestranska poravnava", + "None": "Brez poravnave", + + // Lists + "Ordered List": "Številčni seznam", + "Default": "Privzeto", + "Lower Alpha": "Latinica male", + "Lower Greek": "Grške male", + "Lower Roman": "Rimske male", + "Upper Alpha": "Latinica velike", + "Upper Roman": "Rimske velike", + + "Unordered List": "Neštevilčni seznam", + "Circle": "Krog", + "Disc": "Disk", + "Square": "Kvadrat", + + // Line height + "Line Height": "Višina vrstice", + "Single": "Enojna", + "Double": "Dvojna", + + // Indent + "Decrease Indent": "Zmanjšaj zamik", + "Increase Indent": "Povečaj zamik", + + // Links + "Insert Link": "Vstavi povezavo", + "Open in new tab": "Odpri povezavo v novem zavihku", + "Open Link": "Odpri povezavo", + "Edit Link": "Uredi povezavo", + "Unlink": "Odstrani povezavo", + "Choose Link": "Izberi povezavo", + + // Images + "Insert Image": "Vstavi sliko", + "Upload Image": "Naloži sliko", + "By URL": "Iz URL povezave", + "Browse": "Prebrskaj", + "Drop image": "Spustite sliko sem", + "or click": "ali kliknite", + "Manage Images": "Urejaj slike", + "Loading": "Nalaganje", + "Deleting": "Brisanje", + "Tags": "Značke", + "Are you sure? Image will be deleted.": "Ali ste prepričani? Slika bo izbrisana.", + "Replace": "Zamenjaj", + "Uploading": "Nalaganje", + "Loading image": "Nalagam sliko", + "Display": "Prikaži", + "Inline": "Vrstično", + "Break Text": "Prelomi besedilo", + "Alternative Text": "Nadomestno besedilo", + "Change Size": "Spremeni velikost", + "Width": "Širina", + "Height": "Višina", + "Something went wrong. Please try again.": "Nekaj je šlo narobe. Prosimo, poskusite ponovno.", + "Image Caption": "Opis slike", + "Advanced Edit": "Napredno urejanje", + + // Video + "Insert Video": "Vstavi video posnetek", + "Embedded Code": "Vdelana koda", + "Paste in a video URL": "Prilepite URL video posnetka", + "Drop video": "Spustite video posnetek sem", + "Your browser does not support HTML5 video.": "Vaš brskalnik ne podpira HTML5 video funkcionalnosti.", + "Upload Video": "Naloži video posnetek", + + // Tables + "Insert Table": "Vstavi tabelo", + "Table Header": "Glava tabele", + "Remove Table": "Odstrani tabelo", + "Table Style": "Slog tabele", + "Horizontal Align": "Horizontalna poravnava", + "Row": "Vrstica", + "Insert row above": "Vstavi vrstico nad", + "Insert row below": "Vstavi vrstico pod", + "Delete row": "Izbriši vrstico", + "Column": "Stolpec", + "Insert column before": "Vstavi stolpec pred", + "Insert column after": "Vstavi stolpec po", + "Delete column": "Izbriši stolpec", + "Cell": "Celica", + "Merge cells": "Združi celice", + "Horizontal split": "Horizontalni razcep", + "Vertical split": "Vertikalni razcep", + "Cell Background": "Ozadje celice", + "Vertical Align": "Vertikalna poravnava", + "Top": "Vrh", + "Middle": "Sredina", + "Bottom": "Dno", + "Align Top": "Vrhnja poravnava", + "Align Middle": "Sredinska poravnava", + "Align Bottom": "Spodnja poravnava", + "Cell Style": "Slog celice", + + // Files + "Upload File": "Naloži datoteko", + "Drop file": "Spustite datoteko sem", + + // Emoticons + "Emoticons": "Emotikoni", + + // Line breaker + "Break": "Prelom", + + // Math + "Subscript": "Podpisano", + "Superscript": "Nadpisano", + + // Full screen + "Fullscreen": "Celozaslonski način", + + // Horizontal line + "Insert Horizontal Line": "Vstavi vodoravno črto", + + // Clear formatting + "Clear Formatting": "Počisti oblikovanje", + + // Save + "Save": "Shrani", + + // Undo, redo + "Undo": "Razveljavi", + "Redo": "Ponovno uveljavi", + + // Select all + "Select All": "Izberi vse", + + // Code view + "Code View": "Pogled kode", + + // Quote + "Quote": "Citat", + "Increase": "Povečaj", + "Decrease": "Zmanjšaj", + + // Quick Insert + "Quick Insert": "Hitro vstavljanje", + + // Special Characters + "Special Characters": "Posebni znaki", + "Latin": "Latinica", + "Greek": "Grščina", + "Cyrillic": "Cirilica", + "Punctuation": "Ločila", + "Currency": "Valute", + "Arrows": "Puščice", + "Math": "Matematika", + "Misc": "Razno", + + // Print. + "Print": "Natisni", + + // Spell Checker. + "Spell Checker": "Črkovalnik", + + // Help + "Help": "Pomoč", + "Shortcuts": "Bližnjice", + "Inline Editor": "Vdelani urejevalnik", + "Show the editor": "Pokaži urejevalnik", + "Common actions": "Skupna dejanja", + "Copy": "Kopiraj", + "Cut": "Izreži", + "Paste": "Prilepi", + "Basic Formatting": "Osnovno oblikovanje", + "Increase quote level": "Povečaj raven citata", + "Decrease quote level": "Zmanjšaj raven citata", + "Image / Video": "Slika / Video", + "Resize larger": "Povečaj", + "Resize smaller": "Pomanjšaj", + "Table": "Tabela", + "Select table cell": "Izberi celico tabele", + "Extend selection one cell": "Razširi izbor za eno celico", + "Extend selection one row": "Razširi izbor za eno vrstico", + "Navigation": "Navigacija", + "Focus popup / toolbar": "Fokusiraj pojavno okno / orodno vrstico", + "Return focus to previous position": "Vrni fokus v prejšnji položaj", + + // Embed.ly + "Embed URL": "Vdelaj URL", + "Paste in a URL to embed": "Prilepite URL za vdelavo", + + // Word Paste. + "The pasted content is coming from a Microsoft Word document. Do you want to keep the format or clean it up?": "Prilepljena vsebina prihaja iz dokumenta Microsoft Word. Ali želite obliko obdržati ali jo želite očistiti?", + "Keep": "Obdrži", + "Clean": "Počisti", + "Word Paste Detected": "Zaznano je lepljenje s programa Word" + }, + direction: "ltr" +}; + +})); diff --git a/modules/backend/lang/sl/lang.php b/modules/backend/lang/sl/lang.php new file mode 100644 index 000000000..9e2fa903c --- /dev/null +++ b/modules/backend/lang/sl/lang.php @@ -0,0 +1,628 @@ + [ + 'title' => 'Nadzorna plošča', + 'invalid_login' => 'Podatki, ki ste jih vnesli, se ne ujemajo z našimi zapisi. Prosimo, ponovno preverite podatke in poskusite znova.', + ], + 'field' => [ + 'invalid_type' => 'Uporabljen je neveljaven tip polja :type.', + 'options_method_invalid_model' => "Atribut ':field' ne ustreza veljavnemu modelu. Poskusite natančno določiti možnosti metode za model :model.", + 'options_method_not_exists' => "Model :model mora vsebovati metodo :method(), ki vrača možnosti za polje ':field' na obrazcu.", + 'colors_method_not_exists' => "Model :model mora vsebovati metodo :method(), ki vrača HTML barvne kode v HEX formatu za polje ':field' na obrazcu.", + ], + 'widget' => [ + 'not_registered' => "Ime vtičnika ':name' ni bilo registrirano", + 'not_bound' => "Vtičnik z imenom ':name' ni vezan na kontroler", + ], + 'page' => [ + 'untitled' => 'Brez naslova', + '404' => [ + 'label' => 'Stran ne obstaja', + 'help' => 'Kljub intenzivnemu iskanju, zahtevanega URL-ja preprosto ni mogoče najti. Ste morda iskali kaj drugega?', + 'back_link' => 'Vrni se na prejšnjo stran', + ], + 'access_denied' => [ + 'label' => 'Dostop zavrnjen', + 'help' => 'Nimate potrebnih dovoljenj za ogled te strani.', + 'cms_link' => 'Vrni se v administracijo', + ], + 'no_database' => [ + 'label' => 'Podatkovna zbirka manjka', + 'help' => 'Za dostop do administracije je potrebna podatkovna zbirka. Preverite, če je podatkovna zbirka pravilno nastavljena in če so bile migracije pognane ter poskusite ponovno.', + 'cms_link' => 'Vrni se na domačo stran', + ], + ], + 'partial' => [ + 'not_found_name' => "Predloge ':name' ni mogoče najti.", + 'invalid_name' => 'Neveljavno ime predloge: :name.', + ], + 'ajax_handler' => [ + 'invalid_name' => 'Neveljavno ime AJAX akcije: :name.', + 'not_found' => "Ni mogoče najti AJAX akcije ':name'.", + ], + 'account' => [ + 'impersonate' => 'Oponašaj uporabnika', + 'impersonate_confirm' => 'Ali ste prepričani, da želite oponašati tega uporabnika? V prvotno stanje se lahko vrnete tako, da se odjavite.', + 'impersonate_success' => 'Sedaj oponašate tega uporabnika', + 'impersonate_working' => 'Oponašam...', + 'impersonating' => 'Oponašanje :full_name', + 'stop_impersonating' => 'Prekliči oponašanje', + 'signed_in_as' => 'Prijavljen kot :full_name', + 'sign_out' => 'Odjava', + 'login' => 'Prijava', + 'reset' => 'Ponastavi', + 'restore' => 'Obnovi', + 'login_placeholder' => 'uporabniško ime', + 'password_placeholder' => 'geslo', + 'remember_me' => 'Ostanite prijavljeni', + 'forgot_password' => 'Ste pozabili svoje geslo?', + 'enter_email' => 'Vnesite svoj e-poštni naslov', + 'enter_login' => 'Vnesite svoje uporabniško ime', + 'email_placeholder' => 'e-pošta', + 'enter_new_password' => 'Vnesite novo geslo', + 'password_reset' => 'Ponastavitev gesla', + 'restore_success' => 'Na vaš e-poštni naslov je bilo poslano sporočilo z navodili.', + 'restore_error' => "Uporabnika z uporabniškim imenom ':login' ni mogoče najti.", + 'reset_success' => 'Geslo je bilo ponastavljeno. Sedaj se lahko prijavite.', + 'reset_error' => 'Posredovani so bili neveljavni podatki za ponastavitev gesla. Prosimo, poskusite znova!', + 'reset_fail' => 'Gesla ni bilo mogoče ponastaviti!', + 'apply' => 'Sprejmi', + 'cancel' => 'Prekliči', + 'delete' => 'Izbriši', + 'ok' => 'OK', + ], + 'dashboard' => [ + 'menu_label' => 'Nadzorna plošča', + 'widget_label' => 'Vtičnik', + 'widget_width' => 'Širina', + 'full_width' => 'Celotna širina', + 'manage_widgets' => 'Upravljanje vtičnikov', + 'add_widget' => 'Dodaj vtičnik', + 'widget_inspector_title' => 'Nastavitve vtičnika', + 'widget_inspector_description' => 'Nastavitve prikaza vtičnika', + 'widget_columns_label' => 'Širina :columns', + 'widget_columns_description' => 'Širina vtičnika, število med 1 in 10.', + 'widget_columns_error' => 'Prosimo, vnesite širino vtičnika v obliki števila med 1 in 10.', + 'columns' => '{1} stolpec|{2} stolpca|[3,4] stolpci|[5,Inf] stolpcev', + 'widget_new_row_label' => 'Vsili novo vrstico', + 'widget_new_row_description' => 'Postavi vtičnik v novo vrstico', + 'widget_title_label' => 'Naslov vtičnika', + 'widget_title_error' => 'Potreben je vnos naslova vtičnika.', + 'reset_layout' => 'Ponastavi postavitev', + 'reset_layout_confirm' => 'Želite postavitev ponastaviti nazaj na privzeto obliko?', + 'reset_layout_success' => 'Postavitev je bila ponastavljena', + 'make_default' => 'Nastavi za privzeto', + 'make_default_confirm' => 'Želite trenutno postavitev nastaviti za privzeto?', + 'make_default_success' => 'Trenutna postavitev je nastavljena kot privzeta', + 'collapse_all' => 'Strni vse', + 'expand_all' => 'Razširi vse', + 'status' => [ + 'widget_title_default' => 'Status sistema', + 'update_available' => '[0,1] posodobitev na voljo!|{2} posodobitvi na voljo!|[3,4] posodobitve na voljo!|[5,Inf] posodobitev na voljo!', + 'updates_pending' => 'Posodobitev programske opreme je na voljo', + 'updates_nil' => 'Programska oprema je posodobljena', + 'updates_link' => 'Posodobi', + 'warnings_pending' => 'Nekatere težave potrebujejo vašo pozornost', + 'warnings_nil' => 'Ni opozoril za prikaz', + 'warnings_link' => 'Prikaži', + 'core_build' => 'Različica sistema', + 'event_log' => 'Dnevnik dogodkov', + 'request_log' => 'Dnevnik zahtev', + 'app_birthday' => 'Na spletu od', + ], + 'welcome' => [ + 'widget_title_default' => 'Dobrodošli!', + 'welcome_back_name' => 'Dobrodošli nazaj v :app, :name.', + 'welcome_to_name' => 'Dobrodošli v :app, :name.', + 'first_sign_in' => 'To je vaša prva prijava.', + 'last_sign_in' => 'Vaša zadnja prijava je zabeležena.', + 'view_access_logs' => 'Prikaži dnevnik prijav', + 'nice_message' => 'Imejte lep dan!', + ], + ], + 'user' => [ + 'name' => 'Administrator', + 'menu_label' => 'Administratorji', + 'menu_description' => 'Upravljanje z administratorji, skupinami in dovoljenji.', + 'list_title' => 'Upravljanje administratorjev', + 'new' => 'Nov administrator', + 'login' => 'Uporabniško ime', + 'first_name' => 'Ime', + 'last_name' => 'Priimek', + 'full_name' => 'Polno ime', + 'email' => 'E-poštni naslov', + 'role_field' => 'Vloga', + 'role_comment' => 'Vloge določajo uporabniška dovoljenja, ki jih je možno spremeniti na ravni uporabnika, na zavihku Dovoljenja.', + 'groups' => 'Skupine', + 'groups_comment' => 'Določite, katerim skupinam pripada ta uporabniški račun.', + 'avatar' => 'Avatar', + 'password' => 'Geslo', + 'password_confirmation' => 'Potrdite geslo', + 'permissions' => 'Dovoljenja', + 'account' => 'Uporabniški račun', + 'superuser' => 'Super administrator', + 'superuser_comment' => 'Temu uporabniškemu računu omogoča neomejen dostop do vseh področij sistema. Super administrator lahko dodaja in upravlja druge uporabnike.', + 'send_invite' => 'Pošlji vabilo po e-pošti', + 'send_invite_comment' => 'Pošlje pozdravno e-poštno sporočilo s podatki o uporabniškem imenu in geslu.', + 'delete_confirm' => 'Želite izbrisati tega administratorja?', + 'return' => 'Vrni se na seznam administratorjev', + 'allow' => 'Dovoli', + 'inherit' => 'Podeduj', + 'deny' => 'Ne dovoli', + 'activated' => 'Aktiviran', + 'last_login' => 'Zadnja prijava', + 'created_at' => 'Ustvarjen', + 'updated_at' => 'Posodobljen', + 'deleted_at' => 'Izbrisan', + 'show_deleted' => 'Prikaži izbrisane', + 'group' => [ + 'name' => 'Skupina', + 'name_field' => 'Ime', + 'name_comment' => 'Ime je prikazano na seznamu skupin na administratorskem obrazcu.', + 'description_field' => 'Opis', + 'is_new_user_default_field_label' => 'Privzeta skupina', + 'is_new_user_default_field_comment' => 'Nove administratorje vedno vključi v to skupino.', + 'code_field' => 'Koda', + 'code_comment' => 'Vnesite unikatno kodo, če želite dostopati do objekta skupine preko API klica.', + 'menu_label' => 'Upravljanje s skupinami', + 'list_title' => 'Upravljanje s skupinami', + 'new' => 'Nova skupina', + 'delete_confirm' => 'Želite odstraniti to administratorsko skupino?', + 'return' => 'Vrni se na seznam skupin', + 'users_count' => 'Uporabniki', + ], + 'role' => [ + 'name' => 'Vloga ', + 'name_field' => 'Ime', + 'name_comment' => 'Ime je prikazano na seznamu vlog na administratorskem obrazcu.', + 'description_field' => 'Opis', + 'code_field' => 'Koda', + 'code_comment' => 'Vnesite unikatno kodo, če želite dostopati do objekta vloge preko API klica.', + 'menu_label' => 'Upravljanje z vlogami', + 'list_title' => 'Upravljanje z vlogami', + 'new' => 'Nova vloga', + 'delete_confirm' => 'Želite odstraniti to administratorsko vlogo?', + 'return' => 'Vrni se na seznam vlog', + 'users_count' => 'Uporabniki', + ], + 'preferences' => [ + 'not_authenticated' => 'Ni overjenega uporabnika, za katerega bi lahko naložili ali shranili nastavitve.', + ], + 'trashed_hint_title' => 'Ta uporabniški račun je bil izbrisan', + 'trashed_hint_desc' => 'Ta uporabniški račun je bil izbrisan in prijava z njim ni več mogoča. Če ga želite obnoviti, kliknite ikono za obnovitev uporabnika v spodnjem desnem kotu', + ], + 'list' => [ + 'default_title' => 'Seznam', + 'search_prompt' => 'Iskanje...', + 'no_records' => 'Ni najdenih zapisov.', + 'missing_model' => 'Seznam, uporabljen v :class, nima definiranega modela.', + 'missing_column' => 'Manjkajo definicije stolpcev za stolpce :columns.', + 'missing_columns' => 'Seznam, uporabljen v :class, nima definiranih stolpcev seznama.', + 'missing_definition' => "Seznam ne vsebuje stolpca za ':field'.", + 'missing_parent_definition' => "Seznam ne vsebuje definicije za ':definition'.", + 'behavior_not_ready' => 'Seznam se ni inicializiral. Preverite, ali ste v kontrolerju poklicali metodo makeLists().', + 'invalid_column_datetime' => "Vrednost stolpca ':column' ni DateTime objekt. Preverite, ali imate v vašem Modelu definirano referenco \$dates.", + 'pagination' => 'Prikazani zapisi: :from-:to od :total', + 'first_page' => 'Prva stran', + 'last_page' => 'Zadnja stran', + 'prev_page' => 'Prejšnja stran', + 'next_page' => 'Naslednja stran', + 'refresh' => 'Osveži', + 'updating' => 'Posodabljanje...', + 'loading' => 'Nalaganje...', + 'setup_title' => 'Nastavitve seznama', + 'setup_help' => 'Izberite stolpce, ki jih želite prikazati na seznamu. Položaj stolpcev lahko spremenite tako, da jih povlečete gor ali dol.', + 'records_per_page' => 'Število zapisov na strani', + 'records_per_page_help' => 'Izberite koliko zapisov želite prikazati na eni strani. Upoštevajte, da lahko večje število zapisov na eni strani zmanjša hitrost delovanja.', + 'check' => 'Označi', + 'delete_selected' => 'Izbriši izbrano', + 'delete_selected_empty' => 'Ni izbranih zapisov za izbris.', + 'delete_selected_confirm' => 'Želite izbrisati izbrane zapise?', + 'delete_selected_success' => 'Izbrani zapisi so izbrisani.', + 'column_switch_true' => 'Da', + 'column_switch_false' => 'Ne', + ], + 'fileupload' => [ + 'attachment' => 'Priponka', + 'help' => 'Dodajte naslov in opis za to priponko.', + 'title_label' => 'Naslov', + 'description_label' => 'Opis', + 'default_prompt' => 'Če želite naložiti datoteko, kliknite %s ali povlecite datoteko v to polje', + 'attachment_url' => 'URL priloge', + 'upload_file' => 'Naloži datoteko', + 'upload_error' => 'Napaka pri nalaganju', + 'remove_confirm' => 'Ali ste prepričani?', + 'remove_file' => 'Odstrani datoteko', + ], + 'repeater' => [ + 'add_new_item' => 'Dodaj nov element', + 'min_items_failed' => ':name zahteva najmanj :min elementov, zagotovljenih je le :items elementov', + 'max_items_failed' => ':name dovoli največ :max elementov, :items elementov je bilo podanih', + ], + 'form' => [ + 'create_title' => 'Ustvari element ":name"', + 'update_title' => 'Uredi element ":name"', + 'preview_title' => 'Predogled elementa ":name"', + 'create_success' => 'Element ":name" je ustvarjen', + 'update_success' => 'Element ":name" je posodobljen', + 'delete_success' => 'Element ":name" je izbrisan', + 'restore_success' => 'Element ":name" je obnovljen', + 'reset_success' => 'Ponastavitev je zaključena', + 'missing_id' => 'ID obrazca ni bil določen.', + 'missing_model' => 'Obrazec, uporabljen v :class, nima definiranega modela.', + 'missing_definition' => "Obrazec ne vsebuje stolpca za ':field'.", + 'not_found' => 'Obrazca z ID-jem :id ni mogoče najti.', + 'action_confirm' => 'Ali ste prepričani?', + 'create' => 'Ustvari', + 'create_and_close' => 'Ustvari in zapri', + 'creating' => 'Ustvarjanje...', + 'creating_name' => 'Ustvarjanje :name...', + 'save' => 'Shrani', + 'save_and_close' => 'Shrani in zapri', + 'saving' => 'Shranjevanje...', + 'saving_name' => 'Shranjevanje :name...', + 'delete' => 'Izbriši', + 'deleting' => 'Brisanje...', + 'confirm_delete' => 'Želite izbrisati zapis?', + 'confirm_delete_multiple' => 'Želite izbrisati izbrane zapise?', + 'deleting_name' => 'Brisanje :name...', + 'restore' => 'Obnovi', + 'restoring' => 'Obnavljanje', + 'confirm_restore' => 'Ali ste prepričani, da želite obnoviti ta zapis?', + 'reset_default' => 'Ponastavi na privzeto', + 'resetting' => 'Ponastavljanje', + 'resetting_name' => 'Ponastavljanje :name', + 'undefined_tab' => 'Razno', + 'field_off' => 'Ne', + 'field_on' => 'Da', + 'add' => 'Dodaj', + 'apply' => 'Uporabi', + 'cancel' => 'Prekliči', + 'close' => 'Zapri', + 'confirm' => 'Potrdi', + 'reload' => 'Ponovno naloži', + 'complete' => 'Zaključi', + 'ok' => 'OK', + 'or' => 'ali', + 'confirm_tab_close' => 'Zaprem zavihek? Neshranjene spremembe bodo izgubljene.', + 'behavior_not_ready' => 'Obrazec se ni inicializiral. Preverite, ali ste v kontrolerju poklicali metodo initForm().', + 'preview_no_files_message' => 'Ni naloženih datotek.', + 'preview_no_media_message' => 'Ni izbranih media datotek.', + 'preview_no_record_message' => 'Ni izbranih zapisov.', + 'select' => 'Izberi', + 'select_all' => 'vse', + 'select_none' => 'nobenega', + 'select_placeholder' => 'izberite', + 'insert_row' => 'Vstavi vrstico', + 'insert_row_below' => 'Vstavi vrstico spodaj', + 'delete_row' => 'Izbriši vrstico', + 'concurrency_file_changed_title' => 'Datoteka je bila spremenjena', + 'concurrency_file_changed_description' => 'Datoteka, ki jo urejate, je bila spremenjena s strani drugega uporabnika. Datoteko lahko znova naložite in izgubite vaše spremembe ali pa jo prepišete s svojimi spremembami.', + 'return_to_list' => 'Vrni se na seznam', + ], + 'recordfinder' => [ + 'find_record' => 'Poišči zapis', + 'invalid_model_class' => 'Model ":modelClass", podan za iskalnik zapisov, je neveljaven', + 'cancel' => 'Prekliči', + ], + 'pagelist' => [ + 'page_link' => 'Povezava do strani', + 'select_page' => 'Izberite stran...', + ], + 'relation' => [ + 'missing_config' => "Relacija ne vsebuje nastavitev za ':config'.", + 'missing_definition' => "Relacija ne vsebuje definicije za ':field'.", + 'missing_model' => 'Relacija, uporabljena v :class, nima definiranega modela.', + 'invalid_action_single' => 'Tega dejanja ni mogoče izvesti na relaciji ena proti ena.', + 'invalid_action_multi' => 'Tega dejanja ni mogoče izvesti na relaciji mnogo proti mnogo.', + 'help' => 'Kliknite na element, ki ga želite dodati', + 'related_data' => 'Povezani podatki :name', + 'add' => 'Dodaj', + 'add_selected' => 'Dodaj izbrano', + 'add_a_new' => 'Dodaj novo :name', + 'link_selected' => 'Povezava je izbrana', + 'link_a_new' => 'Poveži novo :name', + 'cancel' => 'Prekliči', + 'close' => 'Zapri', + 'add_name' => 'Dodaj :name', + 'create' => 'Ustvari', + 'create_name' => 'Ustvari :name', + 'update' => 'Posodobi', + 'update_name' => 'Posodobi :name', + 'preview' => 'Predogled', + 'preview_name' => 'Predogled za :name', + 'remove' => 'Odstrani', + 'remove_name' => 'Odstrani :name', + 'delete' => 'Izbriši', + 'delete_name' => 'Izbriši :name', + 'delete_confirm' => 'Ali ste prepričani?', + 'link' => 'Povezava', + 'link_name' => 'Povezava za :name', + 'unlink' => 'Odstrani povezavo', + 'unlink_name' => 'Odstrani povezavo :name', + 'unlink_confirm' => 'Ali ste prepričani?', + ], + 'reorder' => [ + 'default_title' => 'Razvrsti zapise', + 'no_records' => 'Na voljo ni nobenih zapisov za razvrščanje.', + ], + 'model' => [ + 'name' => 'Model', + 'not_found' => "Modela ':class' z ID-jem :id ni mogoče najti", + 'missing_id' => 'Za iskanje modela ni določen noben ID.', + 'missing_relation' => "Model ':class' nima definirane relacije ':relation'.", + 'missing_method' => "Model ':class' ne vsebuje metode ':method'.", + 'invalid_class' => "Model :model, uporabljen v :class ni veljaven. Dedovati mora objekt \Model.", + 'mass_assignment_failed' => "Masovna dodelitev je bila neuspešna za atribut ':attribute' na modelu.", + ], + 'warnings' => [ + 'tips' => 'Nasveti glede nastavitev sistema', + 'tips_description' => 'Za pravilno nastavitev sistema morate biti pozorni na določene podrobnosti.', + 'permissions' => 'PHP ne more pisati v mapo :name in njene podmape. Prosimo, nastavite spletnemu strežniku ustrezna dovoljenja za to mapo.', + 'extension' => 'PHP razširitev (extension) :name ni nameščena. Prosimo, namestite ustrezno knjižnico in aktivirajte razširitev.', + 'plugin_missing' => 'Za delovanje je potreben vtičnik :name, vendar ni nameščen. Prosimo, namestite ta vtičnik.', + 'debug' => 'Način za odpravljanje napak je omogočen. Ta način ni priporočljiv za produkcijsko okolje.', + 'decompileBackendAssets' => 'Oblikovne datoteke (CSS, JavaSrcipt) v administraciji so trenutno nestisnjene. To ni priporočljivo za produkcijsko okolje.', + ], + 'editor' => [ + 'menu_label' => 'Nastavitve urejevalnika', + 'menu_description' => 'Splošne nastavitve urejevalnika, kot npr. velikost pisave in barvna shema.', + 'font_size' => 'Velikost pisave', + 'tab_size' => 'Širina tabulatorja', + 'use_hard_tabs' => 'Odmik s tabulatorjem', + 'code_folding' => 'Strnjevanje kode', + 'code_folding_begin' => 'Označi začetek', + 'code_folding_begin_end' => 'Označi začetek in konec', + 'autocompletion' => 'Samodejno dokončanje', + 'word_wrap' => 'Prelom besed', + 'highlight_active_line' => 'Označi aktivno vrstico', + 'auto_closing' => 'Samodejno zapri označbe', + 'show_invisibles' => 'Prikaži nevidne znake', + 'show_gutter' => 'Prikaži odmike', + 'basic_autocompletion' => 'Osnovno samodejno dokončanje (Ctrl + Space)', + 'live_autocompletion' => 'Instantno samodejno dokončanje', + 'enable_snippets' => 'Omogoči odseke kode (Tab)', + 'display_indent_guides' => 'Prikaži vodila za odmike', + 'show_print_margin' => 'Prikaži rob tiskanja', + 'mode_off' => 'Izključi', + 'mode_fluid' => 'Fluidno', + '40_characters' => '40 znakov', + '80_characters' => '80 znakov', + 'theme' => 'Barvna shema', + 'markup_styles' => 'Označevalni slogi', + 'custom_styles' => 'Slog po meri', + 'custom styles_comment' => 'Slogi, ki jih želite vključiti v urejevalnik HTML.', + 'markup_classes' => 'Označevalni razredi', + 'paragraph' => 'Odstavek', + 'link' => 'Povezava', + 'table' => 'Tabela', + 'table_cell' => 'Celica tabele', + 'image' => 'Slika', + 'label' => 'Opis', + 'class_name' => 'Oznaka razreda', + 'markup_tags' => 'Označevalne oznake', + 'allowed_empty_tags' => 'Dovoljene prazne oznake', + 'allowed_empty_tags_comment' => 'Seznam oznak, ki niso odstranjene, če v njih ni vsebine.', + 'allowed_tags' => 'Dovoljene oznake', + 'allowed_tags_comment' => 'Seznam dovoljenih oznak.', + 'no_wrap' => 'Nezavite oznake', + 'no_wrap_comment' => 'Seznam oznak, ki naj ne bodo zavite znotraj blokovskih oznak.', + 'remove_tags' => 'Odstrani oznake', + 'remove_tags_comment' => 'Seznam oznak, ki so odstranjene skupaj z njihovo vsebino.', + 'line_breaker_tags' => 'Oznake prekinitve vrstic', + 'line_breaker_tags_comment' => 'Seznam oznak, ki se uporabljajo za postavitev elementa prekinitve med vrstice.', + 'toolbar_buttons' => 'Gumbi orodne vrstice', + 'toolbar_buttons_comment' => 'Gumbi orodne vrstice, ki se privzeto prikažejo v urejevalniku. [fullscreen, bold, italic, underline, strikeThrough, subscript, superscript, fontFamily, fontSize, |, color, emoticons, inlineStyle, paragraphStyle, |, paragraphFormat, align, formatOL, formatUL, outdent, indent, quote, insertHR, -, insertLink, insertImage, insertVideo, insertAudio, insertFile, insertTable, undo, redo, clearFormatting, selectAll, html]', + ], + 'tooltips' => [ + 'preview_website' => 'Ogled spletne strani', + ], + 'mysettings' => [ + 'menu_label' => 'Moje nastavitve', + 'menu_description' => 'Nastavitve, povezane z vašim administratorskim računom', + ], + 'myaccount' => [ + 'menu_label' => 'Moj profil', + 'menu_description' => 'Urejanje podatkov vašega profila, kot so ime, e-pošta in geslo.', + 'menu_keywords' => 'varnostna prijava', + ], + 'branding' => [ + 'menu_label' => 'Prilagoditev administracije', + 'menu_description' => 'Prilagoditev okolja administracije, kot so npr. ime, barve in logotip.', + 'brand' => 'Splošno', + 'logo' => 'Logo', + 'logo_description' => 'Naložite poljuben logotip, ki ga želite prikazati v administraciji.', + 'favicon' => 'Ikona zaznamka (favicon)', + 'favicon_description' => 'Naložite poljubno ikono zaznamka administracijo.', + 'app_name' => 'Ime aplikacije', + 'app_name_description' => 'Ime je prikazano v naslovni vrstici administracije.', + 'app_tagline' => 'Slogan aplikacije', + 'app_tagline_description' => 'Slogan je prikazan na prijavnem oknu administracije.', + 'colors' => 'Barve', + 'primary_color' => 'Primarna barva', + 'secondary_color' => 'Sekundarna barva', + 'accent_color' => 'Poudarjena barva', + 'styles' => 'CSS slogi', + 'custom_stylesheet' => 'CSS slogi po meri', + 'navigation' => 'Navigacija', + 'menu_mode' => 'Slog menija', + 'menu_mode_inline' => 'Vrstični', + 'menu_mode_inline_no_icons' => 'Vrstični (brez ikon)', + 'menu_mode_tile' => 'Ploščice', + 'menu_mode_collapsed' => 'Strnjen', + ], + 'backend_preferences' => [ + 'menu_label' => 'Nastavitve administracije', + 'menu_description' => 'Upravljajte nastavitve vašega profila, kot je npr. jezik.', + 'region' => 'Regija', + 'code_editor' => 'Urejevalnik kode', + 'timezone' => 'Časovni pas', + 'timezone_comment' => 'Prikazani datumi se prilagodijo glede na izbran časovni pas.', + 'locale' => 'Jezik', + 'locale_comment' => 'Izberite želeni jezik za uporabo v administraciji.', + ], + 'access_log' => [ + 'hint' => 'Ta dnevnik beleži seznam uspešnih prijav administratorjev. Zapisi se hranijo :days dni.', + 'menu_label' => 'Dnevnik dostopa', + 'menu_description' => 'Prikaz seznama uspešnih prijav administratorjev.', + 'id' => 'ID', + 'created_at' => 'Datum in čas', + 'type' => 'Tip', + 'login' => 'Uporabniško ime', + 'ip_address' => 'IP naslov', + 'first_name' => 'Ime', + 'last_name' => 'Priimek', + 'email' => 'E-pošta', + ], + 'filter' => [ + 'all' => 'vsi', + 'options_method_not_exists' => "Model :model mora vsebovati metodo :method(), ki vrača možnosti za filter ':filter'.", + 'date_all' => 'vse periode', + 'number_all' => 'vsa števila', + ], + 'import_export' => [ + 'upload_csv_file' => '1. Naložite CSV datoteko', + 'import_file' => 'Uvozi datoteko', + 'row' => 'Vrstica :row', + 'first_row_contains_titles' => 'Prva vrstica vsebuje naslove stolpcev', + 'first_row_contains_titles_desc' => 'To polje pustite označeno, če prva vrstica v vaši CSV datoteki vsebuje naslove stolpcev.', + 'match_columns' => '2. Povežite stolce v datoteki s polji v podatkovni zbirki', + 'file_columns' => 'Stolpci v datoteki', + 'database_fields' => 'Polja v podatkovni zbirki', + 'set_import_options' => '3. Nastavite možnosti uvoza', + 'export_output_format' => '1. Izberite format izvozne datoteke', + 'file_format' => 'Format datoteke', + 'standard_format' => 'Standardni format', + 'custom_format' => 'Format po meri', + 'delimiter_char' => 'Znak ločila', + 'enclosure_char' => 'Znak zaključka', + 'escape_char' => 'Izhodni znak', + 'select_columns' => '2. Označite stolpce za izvoz', + 'column' => 'Stolpec', + 'columns' => 'Stolpci', + 'set_export_options' => '3. Nastavitve možnosti izvoza', + 'show_ignored_columns' => 'Prikaži prezrte stolpce', + 'auto_match_columns' => 'Samodejno poveži stolpce', + 'created' => 'Ustvarjeno', + 'updated' => 'Posodobljeno', + 'skipped' => 'Izpuščeno', + 'warnings' => 'Opozorila', + 'errors' => 'Napake', + 'skipped_rows' => 'Izpuščene vrstice', + 'import_progress' => 'Napredek uvoza', + 'processing' => 'Procesiranje', + 'import_error' => 'Napaka pri uvozu', + 'upload_valid_csv' => 'Prosimo, naložite veljavno CSV datoteko.', + 'drop_column_here' => 'Spustite stolpec sem...', + 'ignore_this_column' => 'Prezri ta stolpec', + 'processing_successful_line1' => 'Izvoz datoteke je zaključen!', + 'processing_successful_line2' => 'Brskalnik vas bo sedaj preusmeril na prenos datoteke.', + 'export_progress' => 'Napredek izvoza', + 'export_error' => 'Napaka pri izvozu', + 'column_preview' => 'Predogled stolpca', + 'file_not_found_error' => 'Datoteke ni mogoče najti', + 'empty_error' => 'Ni podanih podatkov za izvoz', + 'empty_import_columns_error' => 'Prosimo, določite nekaj stolpcev za uvoz.', + 'match_some_column_error' => 'Prosimo, najprej povežite nekaj stolpcev.', + 'required_match_column_error' => 'Prosimo, določite povezavo za zahtevano polje :label.', + 'empty_export_columns_error' => 'Prosimo, določite nekaj stolpcev za izvoz.', + 'behavior_missing_uselist_error' => 'Implementirati morate kontroler ListController z omogočeno možnostjo izvoza "useList".', + 'missing_model_class_error' => 'Prosimo, določite modelClass lastnost za :type', + 'missing_column_id_error' => 'Manjka identifikator stolpca', + 'unknown_column_error' => 'Neznan stolpec', + 'encoding_not_supported_error' => 'Kodiranje izvorne datoteke ni prepoznano. Za uspešen uvoz vaše datoteke izberite ustrezno kodiranje.', + 'encoding_format' => 'Kodiranje datoteke', + 'encodings' => [ + 'utf_8' => 'UTF-8', + 'us_ascii' => 'US-ASCII', + 'iso_8859_1' => 'ISO-8859-1 (Latin-1, Western European)', + 'iso_8859_2' => 'ISO-8859-2 (Latin-2, Central European)', + 'iso_8859_3' => 'ISO-8859-3 (Latin-3, South European)', + 'iso_8859_4' => 'ISO-8859-4 (Latin-4, North European)', + 'iso_8859_5' => 'ISO-8859-5 (Latin, Cyrillic)', + 'iso_8859_6' => 'ISO-8859-6 (Latin, Arabic)', + 'iso_8859_7' => 'ISO-8859-7 (Latin, Greek)', + 'iso_8859_8' => 'ISO-8859-8 (Latin, Hebrew)', + 'iso_8859_0' => 'ISO-8859-9 (Latin-5, Turkish)', + 'iso_8859_10' => 'ISO-8859-10 (Latin-6, Nordic)', + 'iso_8859_11' => 'ISO-8859-11 (Latin, Thai)', + 'iso_8859_13' => 'ISO-8859-13 (Latin-7, Baltic Rim)', + 'iso_8859_14' => 'ISO-8859-14 (Latin-8, Celtic)', + 'iso_8859_15' => 'ISO-8859-15 (Latin-9, Western European revision with euro sign)', + 'windows_1251' => 'Windows-1251 (CP1251)', + 'windows_1252' => 'Windows-1252 (CP1252)' + ] + ], + 'permissions' => [ + 'manage_media' => 'Nalaganje in upravljanje z media vsebinami - slike, video posnetki, zvočni posnetki, dokumenti', + ], + 'mediafinder' => [ + 'label' => 'Media brskalnik', + 'default_prompt' => 'Kliknite gumb %s za iskanje media elementa', + 'no_image' => 'Slike ni mogoče najti', + ], + 'media' => [ + 'menu_label' => 'Media', + 'upload' => 'Naloži', + 'move' => 'Premakni', + 'delete' => 'Izbriši', + 'add_folder' => 'Dodaj mapo', + 'search' => 'Iskanje', + 'display' => 'Prikaz', + 'filter_everything' => 'Vse', + 'filter_images' => 'Slike', + 'filter_video' => 'Video', + 'filter_audio' => 'Audio', + 'filter_documents' => 'Dokumenti', + 'library' => 'Knjižnica', + 'size' => 'Velikost', + 'title' => 'Naslov', + 'last_modified' => 'Zadnja sprememba', + 'public_url' => 'URL', + 'click_here' => 'Kliknite tukaj', + 'thumbnail_error' => 'Napaka pri ustvarjanju sličice.', + 'return_to_parent' => 'Vrni se v nadrejeno mapo', + 'return_to_parent_label' => 'Pojdi gor ..', + 'nothing_selected' => 'Nič ni izbrano.', + 'multiple_selected' => 'Izbranih je več elementov.', + 'uploading_file_num' => 'Nalaganje :number datotek(e)...', + 'uploading_complete' => 'Nalaganje zaključeno', + 'uploading_error' => 'Nalaganje ni uspelo', + 'type_blocked' => 'Izbrani tip datoteke je blokiran iz varnostnih razlogov.', + 'order_by' => 'Razvrsti po', + 'direction' => 'Smer', + 'direction_asc' => 'Naraščajoče', + 'direction_desc' => 'Padajoče', + 'folder' => 'Mapa', + 'no_files_found' => 'Vaše iskanje se ne ujema z nobeno datoteko.', + 'delete_empty' => 'Prosimo, izberite elemente, ki jih želite izbrisati.', + 'delete_confirm' => 'Želite izbrisati izbrane elemente?', + 'error_renaming_file' => 'Napaka pri preimenovanju elementa.', + 'new_folder_title' => 'Nova mapa', + 'folder_name' => 'Ime mape', + 'error_creating_folder' => 'Napaka pri ustvarjanju mape', + 'folder_or_file_exist' => 'Datoteka ali mapa z izbranim imenom že obstaja.', + 'move_empty' => 'Prosimo, izberite elemente, ki jih želite premakniti.', + 'move_popup_title' => 'Premakni datoteke ali mape', + 'move_destination' => 'Ciljna mapa', + 'please_select_move_dest' => 'Prosimo, izberite ciljno mapo.', + 'move_dest_src_match' => 'Prosimo, izberite drugo ciljno mapo.', + 'empty_library' => 'Tukaj izgleda malo prazno. Za začetek naložite datoteke ali ustvarite mape.', + 'insert' => 'Vstavi', + 'crop_and_insert' => 'Obreži in vstavi', + 'select_single_image' => 'Prosimo, izberite samo eno sliko.', + 'selection_not_image' => 'Izbrani element ni slika.', + 'restore' => 'Razveljavi vse spremembe', + 'resize' => 'Spremeni velikost...', + 'selection_mode_normal' => 'Normalno', + 'selection_mode_fixed_ratio' => 'Fiksno razmerje', + 'selection_mode_fixed_size' => 'Fiksna velikost', + 'height' => 'Višina', + 'width' => 'Širina', + 'selection_mode' => 'Izbirni način', + 'resize_image' => 'Spremeni velikost slike', + 'image_size' => 'Velikost slike:', + 'selected_size' => 'Izbrano:', + ], +]; diff --git a/modules/backend/models/Preference.php b/modules/backend/models/Preference.php index 18044180b..12d7f0ae4 100644 --- a/modules/backend/models/Preference.php +++ b/modules/backend/models/Preference.php @@ -212,6 +212,7 @@ class Preference extends Model 'ro' => [Lang::get('system::lang.locale.ro'), 'flag-ro'], 'ru' => [Lang::get('system::lang.locale.ru'), 'flag-ru'], 'sk' => [Lang::get('system::lang.locale.sk'), 'flag-sk'], + 'sl' => [Lang::get('system::lang.locale.sl'), 'flag-si'], 'sv' => [Lang::get('system::lang.locale.sv'), 'flag-se'], 'th' => [Lang::get('system::lang.locale.th'), 'flag-th'], 'tr' => [Lang::get('system::lang.locale.tr'), 'flag-tr'], diff --git a/modules/cms/lang/sl/lang.php b/modules/cms/lang/sl/lang.php new file mode 100644 index 000000000..a46cbbce0 --- /dev/null +++ b/modules/cms/lang/sl/lang.php @@ -0,0 +1,301 @@ + [ + 'invalid_file' => 'Neveljavno ime datoteke :name. Imena datotek lahko vsebujejo le alfanumerične simbole, podčrtaje, pomišljaje in pike. Nekaj primerov pravilnih imen datotek: page.htm, page, subdirectory/page', + 'invalid_property' => "Lastnost ':name' ne more biti nastavljena", + 'file_already_exists' => "Datoteka ':name' že obstaja", + 'error_saving' => "Napaka pri shranjevanju datoteke ':name'. Prosimo, preverite vaša uporabniška dovoljenja.", + 'error_creating_directory' => 'Napaka pri ustvarjanju mape :name. Prosimo, preverite vaša uporabniška dovoljenja.', + 'invalid_file_extension' => "Neveljaven format datoteke :invalid'. Veljavni formati so :allowed.", + 'error_deleting' => "Napaka pri brisanju predloge ':name'. Prosimo, preverite vaša uporabniška dovoljenja.", + 'delete_success' => 'Izbrisanih predlog: :count.', + 'file_name_required' => 'Obvezno polje: Ime datoteke', + 'safe_mode_enabled' => 'Varnostni način je trenutno vključen.', + ], + 'dashboard' => [ + 'active_theme' => [ + 'widget_title_default' => 'Spletna stran', + 'online' => 'Aktivna', + 'maintenance' => 'V vzdrževanju', + 'manage_themes' => 'Upravljaj teme', + 'customize_theme' => 'Prilagodi temo', + ], + ], + 'theme' => [ + 'not_found_name' => "Teme ':name' ni bilo mogoče najti.", + 'by_author' => 'Od :name', + 'active' => [ + 'not_set' => 'Aktivna tema ni nastavljena.', + 'not_found' => 'Aktivne teme ni mogoče najti.', + ], + 'edit' => [ + 'not_set' => 'Urejana tema ni nastavljena.', + 'not_found' => 'Urejane teme ni mogoče najti.', + 'not_match' => "Objekt, do katerega poskušate dostopati, ne pripada urejani temi. Prosimo, osvežite stran.", + ], + 'settings_menu' => 'Tema spletne strani', + 'settings_menu_description' => 'Upravljanje s temo spletne strani in možnostmi prilagoditve.', + 'default_tab' => 'Lastnosti', + 'name_label' => 'Ime', + 'name_create_placeholder' => 'Novo ime teme', + 'author_label' => 'Avtor', + 'author_placeholder' => 'Oseba ali ime podjetja', + 'description_label' => 'Opis', + 'description_placeholder' => 'Opis teme', + 'homepage_label' => 'Spletna stran', + 'homepage_placeholder' => 'URL spletne strani', + 'code_label' => 'Koda', + 'code_placeholder' => 'Unikatna koda teme, ki se uporablja za distribucijo', + 'preview_image_label' => 'Slika za predogled', + 'preview_image_placeholder' => 'Lokacija slike za predogled teme.', + 'dir_name_label' => 'Ime mape', + 'dir_name_create_label' => 'Ciljna mapa teme', + 'theme_label' => 'Tema', + 'theme_title' => 'Teme', + 'activate_button' => 'Aktiviraj', + 'active_button' => 'Aktiviraj', + 'customize_theme' => 'Prilagodi temo', + 'customize_button' => 'Prilagodi', + 'duplicate_button' => 'Podvoji', + 'duplicate_title' => 'Podvoji temo', + 'duplicate_theme_success' => 'Tema podvojena!', + 'manage_button' => 'Upravljanje', + 'manage_title' => 'Upravljanje s temo', + 'edit_properties_title' => 'Tema', + 'edit_properties_button' => 'Uredi lastnosti', + 'save_properties' => 'Shrani lastnosti', + 'import_button' => 'Uvozi', + 'import_title' => 'Uvozi temo', + 'import_theme_success' => 'Tema uvožena!', + 'import_uploaded_file' => 'Datoteka z arhivom teme', + 'import_overwrite_label' => 'Prepiši obstoječe datoteke', + 'import_overwrite_comment' => 'Odkljukajte to polje, če želite uvoziti le nove datoteke', + 'import_folders_label' => 'Mape', + 'import_folders_comment' => 'Prosimo, izberite mape tem, ki jih želite uvoziti', + 'export_button' => 'Izvozi', + 'export_title' => 'Izvozi temo', + 'export_folders_label' => 'Mape', + 'export_folders_comment' => 'Prosimo, izberite mape tem, ki jih želite izvoziti', + 'delete_button' => 'Izbriši', + 'delete_confirm' => 'Želite izbrisati to temo? Ukaza ni mogoče razveljaviti!', + 'delete_active_theme_failed' => 'Aktivne teme ni mogoče izbrisati. Najprej je potrebno zamenjati aktivno temo.', + 'delete_theme_success' => 'Tema izbrisana!', + 'create_title' => 'Ustvari temo', + 'create_button' => 'Ustvari', + 'create_new_blank_theme' => 'Ustvari novo prazno temo', + 'create_theme_success' => 'Tema ustvarjena!', + 'create_theme_required_name' => 'Prosimo, navedite ime teme.', + 'new_directory_name_label' => 'Mapa za temo', + 'new_directory_name_comment' => 'Podajte novo ime mape za podvojeno temo.', + 'dir_name_invalid' => 'Ime lahko vsebuje samo številke, latinične črke in naslednje simbole: _-', + 'dir_name_taken' => 'Želena mapa za temo že obstaja.', + 'find_more_themes' => 'Poišči več tem', + 'saving' => 'Shranjevanje teme...', + 'return' => 'Nazaj na seznam tem', + ], + 'maintenance' => [ + 'settings_menu' => 'Način vzdrževanja', + 'settings_menu_description' => 'Nastavitve načina vzdrževanja in preklop na način vzdrževanja.', + 'is_enabled' => 'Omogoči način vzdrževanja', + 'is_enabled_comment' => 'Izberite stran, ki bo prikazana ob vključenem načinu vzdrževanja.', + 'hint' => 'V načinu vzdrževanja bo stran o vzdrževanju prikazana obiskovalcem, ki niso prijavljeni v administracijo.', + ], + 'page' => [ + 'not_found_name' => "Strani ':name' ni mogoče najti.", + 'not_found' => [ + 'label' => 'Stran ne obstaja', + 'help' => 'Zahtevane strani ni bilo mogoče najti.', + ], + 'custom_error' => [ + 'label' => 'Napaka strani', + 'help' => 'Žal je šlo nekaj narobe in strani ni mogoče prikazati.', + ], + 'menu_label' => 'Strani', + 'unsaved_label' => 'Neshranjene strani', + 'no_list_records' => 'Ni najdenih strani.', + 'new' => 'Nova stran', + 'invalid_url' => 'Neveljavna oblika URL formata. URL se mora začeti z znakom za desno poševnico in lahko vsebuje številke, latinične črke in naslednje znake: ._-[]:?|/+*^$', + 'delete_confirm_multiple' => 'Želite izbrisati izbrane strani?', + 'delete_confirm_single' => 'Želite izbrisati to stran?', + 'no_layout' => '-- brez postavitve --', + 'cms_page' => 'CMS stran', + 'title' => 'Naslov strani', + 'url' => 'URL strani', + 'file_name' => 'Ime datoteke strani', + ], + 'layout' => [ + 'not_found_name' => "Postavitve ':name' ni mogoče najti.", + 'menu_label' => 'Postavitve', + 'unsaved_label' => 'Neshranjene postavitve', + 'no_list_records' => 'Ni najdenih postavitev.', + 'new' => 'Nova postavitev', + 'delete_confirm_multiple' => 'Želite izbrisati izbrane postavitve?', + 'delete_confirm_single' => 'Želite izbrisati to postavitev?', + ], + 'partial' => [ + 'not_found_name' => "Predloge ':name' ni mogoče najti.", + 'invalid_name' => 'Neveljavno ime predloge :name.', + 'menu_label' => 'Predloge', + 'unsaved_label' => 'Neshranjene predloge', + 'no_list_records' => 'Ni najdenih predlog.', + 'delete_confirm_multiple' => 'Želite izbrisati izbrane predloge?', + 'delete_confirm_single' => 'Želite izbrisati to predlogo?', + 'new' => 'Nova predloga', + ], + 'content' => [ + 'not_found_name' => "Datoteke z vsebino ':name' ni mogoče najti.", + 'menu_label' => 'Vsebine', + 'unsaved_label' => 'Neshranjena vsebina', + 'no_list_records' => 'Ni najdenih datotek z vsebino.', + 'delete_confirm_multiple' => 'Ali želite izbrisati izbrane datoteke ali mape z vsebino?', + 'delete_confirm_single' => 'Želite izbrisati to datoteko z vsebino?', + 'new' => 'Nova datoteka z vsebino', + ], + 'ajax_handler' => [ + 'invalid_name' => 'Neveljavno ime AJAX akcije: :name.', + 'not_found' => "Ni mogoče najti AJAX akcije ':name'.", + ], + 'cms' => [ + 'menu_label' => 'CMS' + ], + 'sidebar' => [ + 'add' => 'Dodaj', + 'search' => 'Iskanje...', + ], + 'editor' => [ + 'settings' => 'Nastavitve', + 'title' => 'Naslov', + 'new_title' => 'Nov naslov strani', + 'url' => 'URL', + 'filename' => 'Ime datoteke', + 'layout' => 'Postavitev', + 'description' => 'Opis', + 'preview' => 'Predogled', + 'meta' => 'Meta podatki', + 'meta_title' => 'Meta naslov', + 'meta_description' => 'Meta opis', + 'markup' => 'Označevalni jezik', + 'code' => 'Koda', + 'content' => 'Vsebina', + 'hidden' => 'Skrito', + 'hidden_comment' => 'Skrite strani so dostopne le uporabnikom, ki so prijavljeni v administracijo.', + 'enter_fullscreen' => 'Celozaslonski način', + 'exit_fullscreen' => 'Zapri celozaslonski način', + 'open_searchbox' => 'Odpri iskalnik', + 'close_searchbox' => 'Zapri iskalnik', + 'open_replacebox' => 'Odpri "Najdi in zamenjaj"', + 'close_replacebox' => 'Zapri "Najdi in zamenjaj"', + 'commit' => 'Shrani spremembe', + 'reset' => 'Ponastavi', + 'commit_confirm' => 'Ali ste prepričani, da želite shraniti spremembe datoteke v datotečni sistem? To bo prepisalo obstoječo datoteko v datotečnem sistemu', + 'reset_confirm' => 'Ali ste prepričani, da želite datoteko ponastaviti na kopijo, ki se nahaja v datotečnem sistemu? To bo datoteko v celoti nadomestilo z datoteko, ki se nahaja v datotečnem sistemu', + 'committing' => 'Shranjujem spremembe', + 'resetting' => 'Ponastavljam', + 'commit_success' => 'Sprememba :type je bila shranjena v datotečni sistem', + 'reset_success' => 'Sprememba :type je bila ponastavljena na različico iz datotečnega sistema', + ], + 'asset' => [ + 'menu_label' => 'Oblikovne datoteke', + 'unsaved_label' => 'Neshranjene datoteke', + 'drop_down_add_title' => 'Dodaj...', + 'drop_down_operation_title' => 'Dejanje...', + 'upload_files' => 'Naloži datoteke', + 'create_file' => 'Ustvari datoteko', + 'create_directory' => 'Ustvari mapo', + 'directory_popup_title' => 'Nova mapa', + 'directory_name' => 'Ime mape', + 'rename' => 'Preimenuj', + 'delete' => 'Izbriši', + 'move' => 'Premakni', + 'select' => 'Izberi', + 'new' => 'Nova datoteka', + 'rename_popup_title' => 'Preimenuj', + 'rename_new_name' => 'Novo ime', + 'invalid_path' => 'Lokacija lahko vsebuje le številke, latinične črke, presledke in naslednje znake: ._-/', + 'error_deleting_file' => 'Napaka pri brisanju datoteke :name.', + 'error_deleting_dir_not_empty' => 'Napaka pri brisanju mape :name. Mapa ni prazna.', + 'error_deleting_dir' => 'Napaka pri brisanju mape :name.', + 'invalid_name' => 'Ime lahko vsebuje le številke, latinične črke, presledke in naslednje znake: ._-', + 'original_not_found' => 'Originalne datoteke oziroma mape ni mogoče najti', + 'already_exists' => 'Datoteka oziroma mapa s tem imenom že obstaja', + 'error_renaming' => 'Napaka pri preimenovanju datoteke oziroma mape', + 'name_cant_be_empty' => 'Ime ne more biti prazno', + 'too_large' => 'Naložena datoteka je prevelika. Največja dovoljena velikost datoteke je :max_size', + 'type_not_allowed' => 'Dovoljeni so le formati datotek: :alowed_types', + 'file_not_valid' => 'Neveljavna datoteka', + 'error_uploading_file' => "Napaka pri nalaganju datoteke ':name': :error", + 'move_please_select' => 'izberite', + 'move_destination' => 'Ciljna mapa', + 'move_popup_title' => 'Premakni oblikovne datoteke', + 'move_button' => 'Premakni', + 'selected_files_not_found' => 'Izbranih datotek ni mogoče najti', + 'select_destination_dir' => 'Prosimo, izberite ciljno mapo', + 'destination_not_found' => 'Ciljne mape ni mogoče najti', + 'error_moving_file' => 'Napaka pri premikanju datoteke :file', + 'error_moving_directory' => 'Napaka pri premikanju mape :dir', + 'error_deleting_directory' => 'Napaka pri brisanju originalne mape :dir', + 'no_list_records' => 'Ni najdenih datotek.', + 'delete_confirm' => 'Želite izbrisati izbrane datoteke ali mape?', + 'path' => 'Lokacija', + ], + 'component' => [ + 'menu_label' => 'Komponente', + 'unnamed' => 'Neimenovano', + 'no_description' => 'Opis ni podan', + 'alias' => 'Vzdevek', + 'alias_description' => 'Unikatno ime komponente, ki se uporablja na strani ali v kodi postavitve.', + 'validation_message' => 'Vzdevki komponent so obvezni in lahko vsebujejo le latinične znake, številke in podčrtaje. Vzdevki naj se začnejo z latiničnim znakom.', + 'invalid_request' => 'Predloge ni bilo mogoče shraniti zaradi neveljavnih podatkov komponente.', + 'no_records' => 'Ni najdenih komponent.', + 'not_found' => "Komponente :name ni mogoče najti.", + 'method_not_found' => "Komponenta ':name' ne vsebuje metode ':method'.", + ], + 'template' => [ + 'invalid_type' => 'Neznan format predloge.', + 'not_found' => 'Predloge ni mogoče najti.', + 'saved' => 'Predloga je shranjena.', + 'no_list_records' => 'Ni najdenih zapisov.', + 'delete_confirm' => 'Želite izbrisati izbrane predloge?', + 'order_by' => 'Razvrsti po', + ], + 'permissions' => [ + 'name' => 'CMS', + 'manage_content' => 'Upravljanje datotek z vsebino spletne strani', + 'manage_assets' => 'Upravljanje z oblikovnimi datotekami - slike, JavaScript, CSS datoteke', + 'manage_pages' => 'Ustvarjanje, spremeninjanje ali brisanje strani', + 'manage_layouts' => 'Ustvarjanje, spremeninjanje ali brisanje CMS postavitev', + 'manage_partials' => 'Ustvarjanje, spremeninjanje ali brisanje CMS predlog', + 'manage_themes' => 'Aktiviranje, deaktiviranje ali konfiguriranje CMS tem', + 'manage_theme_options' => 'Konfiguriranje možnosti prilagajanja za aktivno temo', + ], + 'theme_log' => [ + 'hint' => 'V tem dnevniku so prikazane vse spremembe teme, ki so jih naredili administratorji v administraciji.', + 'menu_label' => 'Dnevnik sprememb teme', + 'menu_description' => 'Pokaži spremembe aktivne teme.', + 'empty_link' => 'Sprazni dnevnik sprememb teme', + 'empty_loading' => 'Praznjenje dnevnika sprememb...', + 'empty_success' => 'Dnevnik sprememb je izpraznjen', + 'return_link' => 'Vrni se na dnevnik sprememb teme', + 'id' => 'ID', + 'id_label' => 'ID dnevnika', + 'created_at' => 'Čas in datum', + 'user' => 'Uporabnik', + 'type' => 'Tip', + 'type_create' => 'Ustvari', + 'type_update' => 'Posodobi', + 'type_delete' => 'Izbriši', + 'theme_name' => 'Tema', + 'theme_code' => 'Koda teme', + 'old_template' => 'Predloga (stara)', + 'new_template' => 'Predloga (nova)', + 'template' => 'Predloga', + 'diff' => 'Spremembe', + 'old_value' => 'Stara vrednost', + 'new_value' => 'Nova vrednost', + 'preview_title' => 'Spremembe predloge', + 'template_updated' => 'Predloga je posodobljena', + 'template_created' => 'Predloga je ustvarjena', + 'template_deleted' => 'Predloga je izbrisana', + ], +]; diff --git a/modules/system/assets/js/lang/lang.sl.js b/modules/system/assets/js/lang/lang.sl.js new file mode 100644 index 000000000..0caba5199 --- /dev/null +++ b/modules/system/assets/js/lang/lang.sl.js @@ -0,0 +1,299 @@ +/* + * This file has been compiled from: /modules/system/lang/sl/client.php + */ +if ($.oc === undefined) $.oc = {} +if ($.oc.langMessages === undefined) $.oc.langMessages = {} +$.oc.langMessages['sl'] = $.extend( + $.oc.langMessages['sl'] || {}, + {"markdowneditor":{"formatting":"Oblikovanje","quote":"Citat","code":"Koda","header1":"Naslov 1","header2":"Naslov 2","header3":"Naslov 3","header4":"Naslov 4","header5":"Naslov 5","header6":"Naslov 6","bold":"Krepko","italic":"Le\u017ee\u010de","unorderedlist":"Ne\u0161tevil\u010dni seznam","orderedlist":"\u0160tevil\u010dni seznam","video":"Video","image":"Slika","link":"Povezava","horizontalrule":"Vstavi vodoravno \u010drto","fullscreen":"Celozaslonski na\u010din","preview":"Predogled"},"mediamanager":{"insert_link":"Vstavi povezavo","insert_image":"Vstavi sliko","insert_video":"Vstavi video posnetek","insert_audio":"Vstavi zvo\u010dni posnetek","invalid_file_empty_insert":"Izberite datoteko, do katere \u017eelite vstaviti povezavo.","invalid_file_single_insert":"Izberite eno samo datoteko.","invalid_image_empty_insert":"Izberite slike za vstavljanje.","invalid_video_empty_insert":"Izberite video posnetek za vstavljanje.","invalid_audio_empty_insert":"Izberite zvo\u010dni posnetek za vstavljanje."},"alert":{"confirm_button_text":"V redu","cancel_button_text":"Prekli\u010di","widget_remove_confirm":"Odstrani ta vti\u010dnik?"},"datepicker":{"previousMonth":"Prej\u0161nji mesec","nextMonth":"Naslednji mesec","months":["Januar","Februar","Marec","April","Maj","Junij","Julij","Avgust","September","Oktober","November","December"],"weekdays":["Nedelja","Ponedeljek","Torek","Sreda","\u010cetrtek","Petek","Sobota"],"weekdaysShort":["Ned","Pon","Tor","Sre","\u010cet","Pet","Sob"]},"colorpicker":{"choose":"Ok"},"filter":{"group":{"all":"vsi"},"scopes":{"apply_button_text":"Uporabi","clear_button_text":"Po\u010disti"},"dates":{"all":"vsi","filter_button_text":"Filtriraj","reset_button_text":"Ponastavi","date_placeholder":"Datum","after_placeholder":"Po","before_placeholder":"Pred"},"numbers":{"all":"vsi","filter_button_text":"Filtriraj","reset_button_text":"Ponastavi","min_placeholder":"Min","max_placeholder":"Max"}},"eventlog":{"show_stacktrace":"Prika\u017ei sled dogodkov","hide_stacktrace":"Skrij sled dogodkov","tabs":{"formatted":"Oblikovano","raw":"Brez oblikovanja"},"editor":{"title":"Urejevalnik izvorne kode","description":"Va\u0161 operacijski sistem mora biti nastavljen tako, da upo\u0161teva eno od teh URL shem.","openWith":"Za odpiranje uporabi","remember_choice":"Zapomni si izbrane nastavitve za to sejo","open":"Odpri","cancel":"Prekli\u010di"}}} +); + +//! moment.js locale configuration v2.22.2 + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + function translate(number, withoutSuffix, key, isFuture) { + var result = number + ' '; + switch (key) { + case 's': // a few seconds / in a few seconds / a few seconds ago + return (withoutSuffix || isFuture) ? 'nekaj sekund' : 'nekaj sekundami'; + case 'ss': // 9 seconds / in 9 seconds / 9 seconds ago + if (withoutSuffix) { + if (number == 1) { + return result + 'sekunda'; + } else if (number == 2) { + return result + 'sekundi'; + } else if (number == 3 || number == 4) { + return result + 'sekunde'; + } else { + return result + 'sekund'; + } + } else if (isFuture) { + if (number == 1) { + return result + 'sekundo'; + } else if (number == 2) { + return result + 'sekundi'; + } else if (number == 3 || number == 4) { + return result + 'sekunde'; + } else { + return result + 'sekund'; + } + } else { + if (number == 1) { + return result + 'sekundo'; + } else if (number == 2) { + return result + 'sekundama'; + } else { + return result + 'sekundami'; + } + } + break; + case 'm': // a minute / in a minute / a minute ago + return withoutSuffix ? 'minuta' : 'minuto'; + case 'mm': // 9 minutes / in 9 minutes / 9 minutes ago + if (withoutSuffix) { + if (number == 1) { + return result + 'minuta'; + } else if (number == 2) { + return result + 'minuti'; + } else if (number == 3 || number == 4) { + return result + 'minute'; + } else { + return result + 'minut'; + } + } else if (isFuture) { + if (number == 1) { + return result + 'minuto'; + } else if (number == 2) { + return result + 'minuti'; + } else if (number == 3 || number == 4) { + return result + 'minute'; + } else { + return result + 'minut'; + } + } else { + if (number == 1) { + return result + 'minuto'; + } else if (number == 2) { + return result + 'minutama'; + } else { + return result + 'minutami'; + } + } + break; + case 'h': // an hour / in an hour / an hour ago + return withoutSuffix ? 'ura' : 'eno uro'; + case 'hh': // 9 hours / in 9 hours / 9 hours ago + if (withoutSuffix) { + if (number == 1) { + return result + 'ura'; + } else if (number == 2) { + return result + 'uri'; + } else if (number == 3 || number == 4) { + return result + 'ure'; + } else { + return result + 'ur'; + } + } else if (isFuture) { + if (number == 1) { + return result + 'uro'; + } else if (number == 2) { + return result + 'uri'; + } else if (number == 3 || number == 4) { + return result + 'ure'; + } else { + return result + 'ur'; + } + } else { + if (number == 1) { + return result + 'uro'; + } else if (number == 2) { + return result + 'urama'; + } else { + return result + 'urami'; + } + } + break; + case 'd': // a day / in a day / a day ago + return withoutSuffix ? 'dan' : (isFuture ? 'en dan' : 'enim dnem'); + case 'dd': // 9 days / in 9 days / 9 days ago + if (withoutSuffix) { + if (number == 1) { + return result + 'dan'; + } else if (number == 2) { + return result + 'dneva'; + } else if (number == 3 || number == 4) { + return result + 'dnevi'; + } else { + return result + 'dni'; + } + } else if (isFuture) { + if (number == 1) { + return result + 'dan'; + } else { + return result + 'dni'; + } + } else { + if (number == 1) { + return result + 'dnevom'; + } else if (number == 2) { + return result + 'dnevoma'; + } else { + return result + 'dnevi'; + } + } + break; + case 'M': // a month / in a month / a month ago + return withoutSuffix ? 'mesec' : (isFuture ? 'en mesec' : 'enim mesecem'); + case 'MM': // 9 months / in 9 months / 9 months ago + if (withoutSuffix) { + if (number == 1) { + return result + 'mesec'; + } else if (number == 2) { + return result + 'meseca'; + } else if (number == 3 || number == 4) { + return result + 'meseci'; + } else { + return result + 'mesecev'; + } + } else if (isFuture) { + if (number == 1) { + return result + 'mesec'; + } else if (number == 2) { + return result + 'meseca'; + } else if (number == 3 || number == 4) { + return result + 'mesece'; + } else { + return result + 'mesecev'; + } + } else { + if (number == 1) { + return result + 'mesecom'; + } else if (number == 2) { + return result + 'mesecema'; + } else { + return result + 'meseci'; + } + } + break; + case 'y': // a year / in a year / a year ago + return withoutSuffix ? 'leto' : (isFuture ? 'eno leto' : 'enim letom'); + case 'yy': // 9 years / in 9 years / 9 years ago + if (withoutSuffix) { + if (number == 1) { + return result + 'leto'; + } else if (number == 2) { + return result + 'leti'; + } else if (number == 3 || number == 4) { + return result + 'leta'; + } else { + return result + 'let'; + } + } else if (isFuture) { + if (number == 1) { + return result + 'leto'; + } else if (number == 2) { + return result + 'leti'; + } else if (number == 3 || number == 4) { + return result + 'leta'; + } else { + return result + 'let'; + } + } else { + if (number == 1) { + return result + 'letom'; + } else if (number == 2) { + return result + 'letoma'; + } else { + return result + 'leti'; + } + } + break; + } + } + + var sl = moment.defineLocale('sl', { + months : 'januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december'.split('_'), + monthsShort : 'jan_feb_mar_apr_maj_jun_jul_avg_sep_okt_nov_dec'.split('_'), + weekdays : 'nedelja_ponedeljek_torek_sreda_\u010detrtek_petek_sobota'.split('_'), + weekdaysShort : 'ned_pon_tor_sre_\u010det_pet_sob'.split('_'), + weekdaysMin : 'ne_po_to_sr_\u010de_pe_so'.split('_'), + longDateFormat : { + LT : 'H:mm', + LTS : 'H:mm:ss', + L : 'DD.MM.YYYY', + LL : 'D. MMMM YYYY', + LLL : 'D. MMMM YYYY H:mm', + LLLL : 'dddd, D. MMMM YYYY H:mm' + }, + calendar : { + sameDay : '[danes ob] LT', + nextDay : '[jutri ob] LT', + nextWeek: function () { + switch (this.day()) { + case 0: + return '[v nedeljo ob] LT'; + case 1: + case 2: + return '[v] dddd [ob] LT'; + case 3: + return '[v sredo ob] LT'; + case 4: + case 5: + return '[v] dddd [ob] LT'; + case 6: + return '[v soboto ob] LT'; + } + }, + lastDay : '[včeraj ob] LT', + lastWeek: function () { + switch (this.day()) { + case 0: + return '[prej\u0161njo nedeljo ob] LT'; + case 1: + case 2: + return '[prej\u0161nji] dddd [ob] LT'; + case 3: + return '[prej\u0161njo sredo ob] LT'; + case 4: + case 5: + return '[prej\u0161nji] dddd [ob] LT'; + case 6: + return '[prej\u0161njo soboto ob] LT'; + } + }, + sameElse : 'L' + }, + relativeTime : { + future : '\u010dez %s', + past : 'pred %s', + s : translate, + ss : translate, + m : translate, + mm : translate, + h : translate, + hh : translate, + d : translate, + dd : translate, + M : translate, + MM : translate, + y : translate, + yy : translate + }, + dayOfMonthOrdinalParse: /\d{1,2}\./, + ordinal : '%d.', + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. + } + }); + + return sl; + +}))); + diff --git a/modules/system/lang/ar/lang.php b/modules/system/lang/ar/lang.php index 74814f6c7..91ca67bd6 100644 --- a/modules/system/lang/ar/lang.php +++ b/modules/system/lang/ar/lang.php @@ -40,6 +40,7 @@ return [ 'fi' => 'Suomi', 'sv' => 'Svenska', 'sk' => 'Slovenský', + 'sl' => 'Slovenščina', 'tr' => 'Türkçe', 'uk' => 'Українська мова', 'zh-cn' => '简体中文', diff --git a/modules/system/lang/en/lang.php b/modules/system/lang/en/lang.php index 028f5feed..4ff42f4d8 100644 --- a/modules/system/lang/en/lang.php +++ b/modules/system/lang/en/lang.php @@ -41,6 +41,7 @@ return [ 'fi' => 'Suomi', 'sv' => 'Svenska', 'sk' => 'Slovenský', + 'sl' => 'Slovenščina', 'th' => 'ไทย', 'tr' => 'Türkçe', 'uk' => 'Українська мова', diff --git a/modules/system/lang/et/lang.php b/modules/system/lang/et/lang.php index 5ca2e0542..85ff20188 100644 --- a/modules/system/lang/et/lang.php +++ b/modules/system/lang/et/lang.php @@ -36,6 +36,7 @@ return [ 'ru' => 'Русский', 'sv' => 'Svenska', 'sk' => 'Slovenský', + 'sl' => 'Slovenščina', 'tr' => 'Türkçe', 'zh-cn' => '简体中文', 'zh-tw' => '繁體中文', diff --git a/modules/system/lang/fa/lang.php b/modules/system/lang/fa/lang.php index ef6472bd6..fc65f4d8a 100644 --- a/modules/system/lang/fa/lang.php +++ b/modules/system/lang/fa/lang.php @@ -39,6 +39,7 @@ return [ 'fi' => 'Suomi', 'sv' => 'Svenska', 'sk' => 'Slovenský', + 'sl' => 'Slovenščina', 'tr' => 'Türkçe', 'uk' => 'Українська мова', 'zh-cn' => '简体中文', diff --git a/modules/system/lang/fi/lang.php b/modules/system/lang/fi/lang.php index 2add07a9f..0bb43eed5 100644 --- a/modules/system/lang/fi/lang.php +++ b/modules/system/lang/fi/lang.php @@ -39,6 +39,7 @@ return [ 'ru' => 'Русский', 'sv' => 'Svenska', 'sk' => 'Slovenský', + 'sl' => 'Slovenščina', 'tr' => 'Türkçe', 'uk' => 'Українська мова', 'zh-cn' => '简体中文', diff --git a/modules/system/lang/lt/lang.php b/modules/system/lang/lt/lang.php index 0e481812e..45ad1bd02 100644 --- a/modules/system/lang/lt/lang.php +++ b/modules/system/lang/lt/lang.php @@ -35,6 +35,7 @@ return [ 'ru' => 'Русский', 'sv' => 'Svenska', 'sk' => 'Slovenský', + 'sl' => 'Slovenščina', 'tr' => 'Türk', 'zh-cn' => '简体中文', 'zh-tw' => '繁體中文', diff --git a/modules/system/lang/nl/lang.php b/modules/system/lang/nl/lang.php index 74cb99a47..de4064a6b 100644 --- a/modules/system/lang/nl/lang.php +++ b/modules/system/lang/nl/lang.php @@ -41,6 +41,7 @@ return [ 'fi' => 'Suomi', 'sv' => 'Svenska', 'sk' => 'Slovenský', + 'sl' => 'Slovenščina', 'tr' => 'Türkçe', 'uk' => 'Українська мова', 'zh-cn' => '简体中文', diff --git a/modules/system/lang/pt-pt/lang.php b/modules/system/lang/pt-pt/lang.php index e534d21c1..fe8836c12 100644 --- a/modules/system/lang/pt-pt/lang.php +++ b/modules/system/lang/pt-pt/lang.php @@ -35,6 +35,7 @@ return [ 'ru' => 'Russo', 'sv' => 'Suéco', 'sk' => 'Esloveno', + 'sl' => 'Slovenščina', 'tr' => 'Turco', 'zh-cn' => 'Chinês', 'zh-tw' => 'Tailandês', diff --git a/modules/system/lang/sk/lang.php b/modules/system/lang/sk/lang.php index 18e32360e..fd2209164 100644 --- a/modules/system/lang/sk/lang.php +++ b/modules/system/lang/sk/lang.php @@ -39,6 +39,7 @@ return [ 'fi' => 'Suomi', 'sv' => 'Svenska', 'sk' => 'Slovenský', + 'sl' => 'Slovenščina', 'tr' => 'Türkçe', 'uk' => 'Українська мова', 'zh-cn' => '简体中文', diff --git a/modules/system/lang/sl/client.php b/modules/system/lang/sl/client.php new file mode 100644 index 000000000..84ffa7103 --- /dev/null +++ b/modules/system/lang/sl/client.php @@ -0,0 +1,115 @@ + [ + 'formatting' => 'Oblikovanje', + 'quote' => 'Citat', + 'code' => 'Koda', + 'header1' => 'Naslov 1', + 'header2' => 'Naslov 2', + 'header3' => 'Naslov 3', + 'header4' => 'Naslov 4', + 'header5' => 'Naslov 5', + 'header6' => 'Naslov 6', + 'bold' => 'Krepko', + 'italic' => 'Ležeče', + 'unorderedlist' => 'Neštevilčni seznam', + 'orderedlist' => 'Številčni seznam', + 'video' => 'Video', + 'image' => 'Slika', + 'link' => 'Povezava', + 'horizontalrule' => 'Vstavi vodoravno črto', + 'fullscreen' => 'Celozaslonski način', + 'preview' => 'Predogled', + ], + 'mediamanager' => [ + 'insert_link' => 'Vstavi povezavo', + 'insert_image' => 'Vstavi sliko', + 'insert_video' => 'Vstavi video posnetek', + 'insert_audio' => 'Vstavi zvočni posnetek', + 'invalid_file_empty_insert' => 'Izberite datoteko, do katere želite vstaviti povezavo.', + 'invalid_file_single_insert' => 'Izberite eno samo datoteko.', + 'invalid_image_empty_insert' => 'Izberite slike za vstavljanje.', + 'invalid_video_empty_insert' => 'Izberite video posnetek za vstavljanje.', + 'invalid_audio_empty_insert' => 'Izberite zvočni posnetek za vstavljanje.', + ], + 'alert' => [ + 'confirm_button_text' => 'V redu', + 'cancel_button_text' => 'Prekliči', + 'widget_remove_confirm' => 'Odstrani ta vtičnik?', + ], + 'datepicker' => [ + 'previousMonth' => 'Prejšnji mesec', + 'nextMonth' => 'Naslednji mesec', + 'months' => [ + 'Januar', + 'Februar', + 'Marec', + 'April', + 'Maj', + 'Junij', + 'Julij', + 'Avgust', + 'September', + 'Oktober', + 'November', + 'December' + ], + 'weekdays' => ['Nedelja', 'Ponedeljek', 'Torek', 'Sreda', 'Četrtek', 'Petek', 'Sobota'], + 'weekdaysShort' => ['Ned', 'Pon', 'Tor', 'Sre', 'Čet', 'Pet', 'Sob'], + ], + 'colorpicker' => [ + 'choose' => 'Ok', + ], + 'filter' => [ + 'group' => [ + 'all' => 'vsi', + ], + 'scopes' => [ + 'apply_button_text' => 'Uporabi', + 'clear_button_text' => 'Počisti', + ], + 'dates' => [ + 'all' => 'vsi', + 'filter_button_text' => 'Filtriraj', + 'reset_button_text' => 'Ponastavi', + 'date_placeholder' => 'Datum', + 'after_placeholder' => 'Po', + 'before_placeholder' => 'Pred', + ], + 'numbers' => [ + 'all' => 'vsi', + 'filter_button_text' => 'Filtriraj', + 'reset_button_text' => 'Ponastavi', + 'min_placeholder' => 'Min', + 'max_placeholder' => 'Max', + ] + ], + 'eventlog' => [ + 'show_stacktrace' => 'Prikaži sled dogodkov', + 'hide_stacktrace' => 'Skrij sled dogodkov', + 'tabs' => [ + 'formatted' => 'Oblikovano', + 'raw' => 'Brez oblikovanja', + ], + 'editor' => [ + 'title' => 'Urejevalnik izvorne kode', + 'description' => 'Vaš operacijski sistem mora biti nastavljen tako, da upošteva eno od teh URL shem.', + 'openWith' => 'Za odpiranje uporabi', + 'remember_choice' => 'Zapomni si izbrane nastavitve za to sejo', + 'open' => 'Odpri', + 'cancel' => 'Prekliči', + ], + ], +]; diff --git a/modules/system/lang/sl/lang.php b/modules/system/lang/sl/lang.php new file mode 100644 index 000000000..7d3c7bba6 --- /dev/null +++ b/modules/system/lang/sl/lang.php @@ -0,0 +1,478 @@ + [ + 'name' => 'OctoberCMS', + 'tagline' => 'Nazaj k osnovam', + ], + 'locale' => [ + 'ar' => 'العربية', + 'be' => 'Беларуская', + 'bg' => 'Български', + 'ca' => 'Català', + 'cs' => 'Čeština', + 'da' => 'Dansk', + 'en' => 'English (United States)', + 'en-au' => 'English (Australia)', + 'en-ca' => 'English (Canada)', + 'en-gb' => 'English (United Kingdom)', + 'et' => 'Eesti', + 'de' => 'Deutsch', + 'el' => 'Ελληνικά', + 'es' => 'Español', + 'es-ar' => 'Español (Argentina)', + 'fa' => 'فارسی', + 'fr' => 'Français', + 'fr-ca' => 'Français (Canada)', + 'hu' => 'Magyar', + 'id' => 'Bahasa Indonesia', + 'it' => 'Italiano', + 'ja' => '日本語', + 'kr' => '한국어', + 'lt' => 'Lietuvių', + 'lv' => 'Latviešu', + 'nb-no' => 'Norsk (Bokmål)', + 'nl' => 'Nederlands', + 'pl' => 'Polski', + 'pt-br' => 'Português (Brasil)', + 'pt-pt' => 'Português (Portugal)', + 'ro' => 'Română', + 'ru' => 'Русский', + 'fi' => 'Suomi', + 'sv' => 'Svenska', + 'sk' => 'Slovenský', + 'sl' => 'Slovenščina', + 'th' => 'ไทย', + 'tr' => 'Türkçe', + 'uk' => 'Українська мова', + 'zh-cn' => '简体中文', + 'zh-tw' => '繁體中文', + 'vn' => 'Tiếng việt', + ], + 'directory' => [ + 'create_fail' => 'Mape :name ni mogoče ustvariti', + ], + 'file' => [ + 'create_fail' => 'Datoteke :name ni mogoče ustvariti', + ], + 'combiner' => [ + 'not_found' => "Datoteke za kombiniranje ':name' ni mogoče najti.", + ], + 'system' => [ + 'name' => 'Sistem', + 'menu_label' => 'Sistem', + 'categories' => [ + 'misc' => 'Razno', + 'logs' => 'Dnevniki', + 'mail' => 'E-pošta', + 'shop' => 'Trgovina', + 'team' => 'Ekipa', + 'users' => 'Uporabniki', + 'system' => 'Sistem', + 'social' => 'Družbeno', + 'backend' => 'Administracija', + 'events' => 'Dogodki', + 'customers' => 'Stranke', + 'my_settings' => 'Moje nastavitve', + 'notifications' => 'Obvestila', + ], + ], + 'theme' => [ + 'label' => 'Tema', + 'unnamed' => 'Neimenovana tema', + 'name' => [ + 'label' => 'Ime teme', + 'help' => 'Poimenujte temo po njeni unikatni kodi, npr. RainLab.Vanilla', + ], + ], + 'themes' => [ + 'install' => 'Namesti teme', + 'search' => 'išči teme za namestitev...', + 'installed' => 'Nameščene teme', + 'no_themes' => 'Ni nameščenih tem iz trga.', + 'recommended' => 'Priporočeno', + 'remove_confirm' => 'Ali ste prepričani, da želite odstraniti to temo?', + ], + 'plugin' => [ + 'label' => 'Vtičnik', + 'unnamed' => 'Neimenovan vtičnik', + 'name' => [ + 'label' => 'Ime vtičnika', + 'help' => 'Poimenujte vtičnik po njegovi unikatni kodi, npr. RainLab.Blog', + ], + 'by_author' => 'Od :name', + ], + 'plugins' => [ + 'manage' => 'Upravljanje vtičnikov', + 'install' => 'Namesti vtičnike in teme', + 'install_products' => 'Namesti produkte', + 'search' => 'išči vtičnike za namestitev...', + 'installed' => 'Nameščeni vtičniki', + 'no_plugins' => 'Ni nameščenih vtičnikov iz trga.', + 'recommended' => 'Priporočeno', + 'plugin_label' => 'Vtičnik', + 'unknown_plugin' => 'Vtičnik je bil odstranjen iz datotečnega sistema.', + 'select_label' => 'Izberite dejanje...', + 'bulk_actions_label' => 'Skupna dejanja', + 'check_yes' => 'Da', + 'check_no' => 'Ne', + 'unfrozen' => 'Posodobitve so omogočene', + 'enabled' => 'Vtičnik je omogočen', + 'freeze' => 'onemogoči posodobitve za', + 'unfreeze' => 'omogoči posodobitve za', + 'enable' => 'omogoči', + 'disable' => 'onemogoči', + 'refresh' => 'ponastavi', + 'remove' => 'Odstrani', + 'freeze_label' => 'Onemogoči posodobitve', + 'unfreeze_label' => 'Omogoči posodobitve', + 'enable_label' => 'Omogoči vtičnike', + 'disable_label' => 'Onemogoči vtičnike', + 'refresh_label' => 'Ponastavi podatke vtičnikov', + 'action_confirm' => 'Ali ste prepričani, da želite :action-ti te vtičnike?', + 'freeze_success' => 'Posodobitve za izbrane vtičnike so onemogočene.', + 'unfreeze_success' => 'Posodobitve za izbrane vtičnike so omogočene.', + 'enable_success' => 'Izbrani vtičniki so omogočeni.', + 'disable_success' => 'Izbrani vtičniki so onemogočeni.', + 'refresh_confirm' => 'Ali ste prepričani, da želite ponastaviti izbrane vtičnike? S tem boste ponastavili podatke vsakega posameznega vtičnika in ga obnovili v začetno stanje namestitve.', + 'refresh_success' => 'Izbrani vtičniki so bili ponastavljeni.', + 'remove_confirm' => 'Ali ste prepričani, da želite odstraniti izbrane vtičnike? S tem boste odstranili tudi vse povezane podatke.', + 'remove_success' => 'Izbrani vtičniki so bili odstranjeni.', + ], + 'project' => [ + 'name' => 'Projekt', + 'owner_label' => 'Lastnik', + 'attach' => 'Pripni projekt', + 'detach' => 'Odpni projekt', + 'none' => 'Nobeden', + 'id' => [ + 'label' => 'ID projekta', + 'help' => 'Kako poiskati ID vašega projekta', + 'missing' => 'Določite ID projekta za uporabo.', + ], + 'detach_confirm' => 'Ali ste prepričani, da želite odpeti ta projekt?', + 'unbind_success' => 'Projekt je bil odpet.', + ], + 'settings' => [ + 'menu_label' => 'Nastavitve', + 'not_found' => 'Navedenih nastavitev ni mogoče najti.', + 'missing_model' => 'Na strani z nastavitvami manjka definicija Modela.', + 'update_success' => 'Nastavitve za ":name" so shranjene.', + 'return' => 'Vrni se na sistemske nastavitve', + 'search' => 'Iskanje', + ], + 'mail' => [ + 'log_file' => 'Dnevniška datoteka', + 'menu_label' => 'Nastavitve e-pošte', + 'menu_description' => 'Upravljanje z nastavitvami za pošiljanje e-pošte.', + 'general' => 'Splošno', + 'method' => 'Način za e-pošto', + 'sender_name' => 'Ime pošiljatelja', + 'sender_email' => 'E-pošta pošiljatelja', + 'php_mail' => 'PHP mail', + 'smtp' => 'SMTP', + 'smtp_address' => 'SMTP naslov', + 'smtp_authorization' => 'Obvezna SMTP avtorizacija', + 'smtp_authorization_comment' => 'Označite, če vaš SMTP strežnik zahteva avtorizacijo.', + 'smtp_username' => 'Uporabniško ime', + 'smtp_password' => 'Geslo', + 'smtp_port' => 'SMTP vrata (port)', + 'smtp_ssl' => 'Obvezna SSL povezava', + 'smtp_encryption' => 'Enkripcijski protokol za SMTP', + 'smtp_encryption_none' => 'Brez enkripcije', + 'smtp_encryption_tls' => 'TLS', + 'smtp_encryption_ssl' => 'SSL', + 'sendmail' => 'Sendmail', + 'sendmail_path' => 'Sendmail lokacija', + 'sendmail_path_comment' => 'Določite lokacijo Sendmail programa.', + 'mailgun' => 'Mailgun', + 'mailgun_domain' => 'Mailgun domena', + 'mailgun_domain_comment' => 'Določite domensko ime za Mailgun (Mailgun domain name).', + 'mailgun_secret' => 'Mailgun geslo', + 'mailgun_secret_comment' => 'Vnesite API ključ za Mailgun (Mailgun API key).', + 'mandrill' => 'Mandrill', + 'mandrill_secret' => 'Mandrill geslo', + 'mandrill_secret_comment' => 'Vnesite API ključ za Mandrill (Mandrill API key).', + 'ses' => 'SES', + 'ses_key' => 'SES ključ', + 'ses_key_comment' => 'Vnesite API ključ za SES.', + 'ses_secret' => 'SES geslo', + 'ses_secret_comment' => 'Vnesite API ključ za SES (SES API secret key).', + 'sparkpost' => 'SparkPost', + 'sparkpost_secret' => 'SparkPost geslo', + 'sparkpost_secret_comment' => 'Vnesite skrivni API ključ za SparkPost (SparkPost API secret key).', + 'ses_region' => 'SES regija', + 'ses_region_comment' => 'Vnesite SES regijo (SES region, npr. us-east-1).', + 'drivers_hint_header' => 'Gonilniki niso nameščeni', + 'drivers_hint_content' => 'Ta način pošiljanja potrebuje namestitev vtičnika ":plugin".', + ], + 'mail_templates' => [ + 'menu_label' => 'E-poštne predloge', + 'menu_description' => 'Urejanje predlog in vsebin, ki se pošiljajo uporabnikom.', + 'new_template' => 'Nova predloga', + 'new_layout' => 'Nova postavitev', + 'new_partial' => 'Nova podpredloga', + 'template' => 'Predloga', + 'templates' => 'Predloge', + 'partial' => 'Podpredloga', + 'partials' => 'Podpredloge', + 'menu_layouts_label' => 'E-poštne postavitve', + 'menu_partials_label' => 'E-poštne predloge', + 'layout' => 'Postavitev', + 'layouts' => 'Postavitve', + 'no_layout' => '-- brez postavitve --', + 'name' => 'Ime', + 'name_comment' => 'Unikatno ime, ki se nanaša na to predlogo.', + 'code' => 'Koda', + 'code_comment' => 'Unikatna koda, ki se nanaša na to predlogo.', + 'subject' => 'Zadeva', + 'subject_comment' => 'Zadeva e-poštnega sporočila.', + 'description' => 'Opis', + 'content_html' => 'HTML', + 'content_css' => 'CSS', + 'content_text' => 'Neformatirano', + 'test_send' => 'Pošlji testno sporočilo', + 'test_success' => 'Testno sporočilo poslano.', + 'test_confirm' => 'Testno sporočilo bo poslano na :email. Nadaljujem?', + 'creating' => 'Ustvarjanje predloge...', + 'creating_layout' => 'Ustvarjanje postavitve...', + 'saving' => 'Shranjevanje predloge...', + 'saving_layout' => 'Shranjevane postavitve...', + 'delete_confirm' => 'Želite izbristi to predlogo?', + 'delete_layout_confirm' => 'Želite izbrisati to postavitev?', + 'deleting' => 'Brisanje predloge...', + 'deleting_layout' => 'Brisanje postavitve...', + 'sending' => 'Pošiljanje testnega sporočila...', + 'return' => 'Vrni se na seznam predlog', + 'options' => 'Možnosti', + 'disable_auto_inline_css' => 'Onemogoči vgrajeni CSS (inline)', + ], + 'mail_brand' => [ + 'menu_label' => 'Oblikovanje e-pošte', + 'menu_description' => 'Spremeninjanje barvne sheme in izgleda e-poštnih predlog.', + 'page_title' => 'Prilagoditev videza e-pošte', + 'sample_template' => [ + 'heading' => 'Naslov', + 'paragraph' => 'To je odstavek, ki vsebuje Lorem Ipsum tekst in povezavo. Cumque dicta
    doloremque eaque, enim error laboriosam pariatur possimus tenetur veritatis voluptas.', + 'table' => [ + 'item' => 'Element', + 'description' => 'Opis', + 'price' => 'Cena', + 'centered' => 'Centrirano', + 'right_aligned' => 'Desno-ravnano', + ], + 'buttons' => [ + 'primary' => 'Primarni gumb', + 'positive' => 'Pozitivni gumb', + 'negative' => 'Negativni gumb', + ], + 'panel' => 'Kako dobro izgleda ta barvni okvir?', + 'more' => 'Še nekaj besedila za konec.', + 'promotion' => 'Koda kupona: OCTOBER', + 'subcopy' => 'Noga sporočila na koncu e-poštne predloge.', + 'thanks' => 'Hvala', + ], + 'fields' => [ + '_section_background' => 'Ozadje', + 'body_bg' => 'Ozadje strani', + 'content_bg' => 'Ozadje vsebine', + 'content_inner_bg' => 'Ozadje notranje vsebine', + '_section_buttons' => 'Gumbi', + 'button_text_color' => 'Barva pisave gumba', + 'button_primary_bg' => 'Ozadje primarnega gumba', + 'button_positive_bg' => 'Ozadje pozitivnega gumba', + 'button_negative_bg' => 'Ozadje negativnega gumba', + '_section_type' => 'Tipografija', + 'header_color' => 'Barva glave', + 'heading_color' => 'Barva naslovov', + 'text_color' => 'Barva besedila', + 'link_color' => 'Barva povezav', + 'footer_color' => 'Barva noge', + '_section_borders' => 'Robovi', + 'body_border_color' => 'Barva roba strani', + 'subcopy_border_color' => 'Barva roba noge sporočila', + 'table_border_color' => 'Barva robov tabel', + '_section_components' => 'Komponente', + 'panel_bg' => 'Ozadje barvnega okvira', + 'promotion_bg' => 'Ozadje promocije', + 'promotion_border_color' => 'Barva robov promocije', + ], + ], + 'install' => [ + 'project_label' => 'Pripni na projekt', + 'plugin_label' => 'Namesti vtičnik', + 'theme_label' => 'Namesti temo', + 'missing_plugin_name' => 'Podajte ime vtičnika, ki ga želite namestiti.', + 'missing_theme_name' => 'Podajte ime teme, ki jo želite namestiti.', + 'install_completing' => 'Zaključevanje namestitvenega postopka', + 'install_success' => 'Produkt je bil uspešno nameščen', + ], + 'updates' => [ + 'title' => 'Upravljanje posodobitev', + 'name' => 'Posodobitev programske opreme', + 'menu_label' => 'Posodobitve in vtičniki', + 'menu_description' => 'Posodobitev sistema, upravljanje in nameščanje vtičnikov in tem.', + 'return_link' => 'Vrni se na sistemske posodobitve', + 'check_label' => 'Preveri za nove posodobitve', + 'retry_label' => 'Poskusi ponovno', + 'plugin_name' => 'Ime', + 'plugin_code' => 'Koda', + 'plugin_description' => 'Opis', + 'plugin_version' => 'Različica', + 'plugin_author' => 'Avtor', + 'plugin_not_found' => 'Vtičnika ni mogoče najti', + 'core_current_build' => 'Trenutna različica', + 'core_view_changelog' => 'Pokaži dnevnik sprememb', + 'core_build' => 'Različica :build', + 'core_build_help' => 'Na voljo je najnovejša različica.', + 'core_downloading' => 'Prenašanje datotek aplikacije', + 'core_extracting' => 'Ekstrahiranje datotek aplikacije', + 'core_set_build' => 'Nastavljanje številke različice', + 'changelog' => 'Dnevnik sprememb', + 'changelog_view_details' => 'Pokaži podrobnosti', + 'plugins' => 'Vtičniki', + 'themes' => 'Teme', + 'disabled' => 'Onemogočenih', + 'plugin_downloading' => 'Prenašanje vtičnika :name', + 'plugin_extracting' => 'Ekstrahiranje vtičnika :name', + 'plugin_version_none' => 'Nov vtičnik', + 'plugin_current_version' => 'Trenutna verzija', + 'theme_new_install' => 'Namestitev nove teme.', + 'theme_downloading' => 'Prenašanje teme :name', + 'theme_extracting' => 'Ekstrahiranje teme :name', + 'update_label' => 'Posodobitev programske opreme', + 'update_completing' => 'Zaključevanje posodabljanja', + 'update_loading' => 'Nalaganje razpoložljivih posodobitev...', + 'update_success' => 'Posodabljanje končano', + 'update_failed_label' => 'Posodabljanje ni bilo uspešno', + 'force_label' => 'Vsili posodobitev', + 'found' => [ + 'label' => 'Nove posodobitve so na voljo!', + 'help' => "Kliknite 'Posodobitev programske opreme' za začetek posodabljanja.", + ], + 'none' => [ + 'label' => 'Ni posodobitev', + 'help' => 'Nove posodobitve niso na voljo.', + ], + 'important_action' => [ + 'empty' => 'Izberite dejanje', + 'confirm' => 'Potrdi posodobitev', + 'skip' => 'Preskoči to posodobitev (le tokrat)', + 'ignore' => 'Preskoči to posodobitev (vedno)', + ], + 'important_action_required' => 'Potrebno je ukrepanje', + 'important_view_guide' => 'Oglejte si navodila za nadgradnjo', + 'important_view_release_notes' => 'Oglejte si opombe ob izdaji', + 'important_alert_text' => 'Nekatere posodobitve potrebujejo vašo pozornost.', + 'details_title' => 'Podrobnosti vtičnika', + 'details_view_homepage' => 'Odpri spletno stran', + 'details_readme' => 'Dokumentacija', + 'details_readme_missing' => 'Dokumentacija ni na voljo.', + 'details_changelog' => 'Dnevnik sprememb', + 'details_changelog_missing' => 'Dnevnik sprememb ni na voljo.', + 'details_upgrades' => 'Navodila za nadgradnjo', + 'details_upgrades_missing' => 'Navodila za nadgradnjo niso na voljo.', + 'details_licence' => 'Licenca', + 'details_licence_missing' => 'Licenca ni na voljo.', + 'details_current_version' => 'Trenutna različica', + 'details_author' => 'Avtor', + ], + 'server' => [ + 'connect_error' => 'Napaka pri povezavi s strežnikom.', + 'response_not_found' => 'Strežnika za posodobitve ni bilo mogoče najti.', + 'response_invalid' => 'Neveljaven odgovor s strani strežnika.', + 'response_empty' => 'Prazen odgovor s strani strežnika.', + 'file_error' => 'Strežniku ni uspelo dostaviti paketa.', + 'file_corrupt' => 'Datoteka s strežnika je poškodovana.', + ], + 'behavior' => [ + 'missing_property' => 'Objekt :class mora imeti definirano lastnost $:property, ki jo uporablja :behavior.', + ], + 'config' => [ + 'not_found' => 'Datoteke z nastavitvami :file, definirane za :location ni mogoče najti.', + 'required' => "Nastavitve uporabljene v :location morajo vsebovati vrednost ':property'.", + ], + 'zip' => [ + 'extract_failed' => "Datoteke ':file' ni mogoče ekstrahirati.", + ], + 'event_log' => [ + 'hint' => 'Ta dnevnik prikaže napake, ki se lahko pojavijo v aplikaciji in informacije za odpravljanje napak.', + 'menu_label' => 'Dnevnik dogodkov', + 'menu_description' => 'Prikaz dnevnika sistemskih sporočil z zabeležinimi časi in podrobnostmi.', + 'empty_link' => 'Počisti dnevnik dogodkov', + 'empty_loading' => 'Čiščenje dnevnika dogodkov...', + 'empty_success' => 'Dnevnik dogodkov je izpraznjen', + 'return_link' => 'Vrni se na dnevnik dogokov', + 'id' => 'ID', + 'id_label' => 'ID dogodka', + 'created_at' => 'Datum in čas', + 'message' => 'Sporočilo', + 'level' => 'Tip dogodka', + 'preview_title' => 'Dogodek', + ], + 'request_log' => [ + 'hint' => 'Ta dnevnik prikaže zahteve brskalnika, ki lahko potrebujejo pozornost. Če npr. obiskovalec odpre stran v CMS, ki je ni mogoče najti, se ustvari zapis s statusno kodo 404.', + 'menu_label' => 'Dnevnik zahtev', + 'menu_description' => 'Ogled slabih ali preusmerjenih zahtev, kot npr. Strani ni bilo mogoče najti (404).', + 'empty_link' => 'Počisti dnevnik zahtev', + 'empty_loading' => 'Čiščenje dnevnika zahtev...', + 'empty_success' => 'Dnevnik zahtev je izpraznjen', + 'return_link' => 'Vrni se na seznam zahtev', + 'id' => 'ID', + 'id_label' => 'ID dnevnika', + 'count' => 'Števec', + 'referer' => 'Reference', + 'url' => 'URL', + 'status_code' => 'Status', + 'preview_title' => 'Zahteva', + ], + 'permissions' => [ + 'name' => 'Sistem', + 'manage_system_settings' => 'Upravljanje sistemskih nastavitev', + 'manage_software_updates' => 'Upravljanje posodobitev programske opreme', + 'access_logs' => 'Prikaz sistemskih dnevnikov', + 'manage_mail_templates' => 'Upravljanje e-poštnih predlog', + 'manage_mail_settings' => 'Upravljanje e-poštnih nastavitev', + 'manage_other_administrators' => 'Upravljanje ostalih administratorjev', + 'impersonate_users' => 'Oponašanje uporabnikov', + 'manage_preferences' => 'Upravljanje z nastavitvami administracije', + 'manage_editor' => 'Upravljanje z nastavitvami urejevalnika kode', + 'view_the_dashboard' => 'Prikaz nadzorne plošče', + 'manage_default_dashboard' => 'Upravljanje s privzeto nadzorno ploščo', + 'manage_branding' => 'Prilaganje administracije', + ], + 'log' => [ + 'menu_label' => 'Nastavitve dnevnikov', + 'menu_description' => 'Določanje področji, ki naj uporabljajo dnevnike.', + 'default_tab' => 'Dnevniki', + 'log_events' => 'Dnevnik sistemskih dogodkov', + 'log_events_comment' => 'Shrani sistemske dogodke tudi v podatkovno bazo in ne samo v dnevniško datoteko.', + 'log_requests' => 'Beleži neuspešne zahteve', + 'log_requests_comment' => 'Zahteve brskalnika, ki lahko potrebujejo pozornost, kot na primer napake 404.', + 'log_theme' => 'Beleži spremembe tem', + 'log_theme_comment' => 'Kadar je sprememba teme narejena v administraciji.', + ], + 'media' => [ + 'invalid_path' => "Podana je nepravilna lokacija datoteke: ':path'.", + 'folder_size_items' => 'element(i)', + ], + 'page' => [ + 'custom_error' => [ + 'label' => 'Napaka strani', + 'help' => "Žal je prišlo do napake in strani ni mogoče prikazati.", + ], + 'invalid_token' => [ + 'label' => 'Neveljaven varnostni ključ', + ], + 'maintenance' => [ + 'label' => "Takoj bomo nazaj!", + 'help' => "Trenutno potekajo vzdrževalna dela, kmalu se vrnemo!", + 'message' => "Sporočilo:", + 'available_at' => "Poskusite znova po:", + ], + ], + 'pagination' => [ + 'previous' => 'Prejšnja', + 'next' => 'Naslednja', + ], +]; diff --git a/modules/system/lang/sl/validation.php b/modules/system/lang/sl/validation.php new file mode 100644 index 000000000..0a934db29 --- /dev/null +++ b/modules/system/lang/sl/validation.php @@ -0,0 +1,121 @@ + '":attribute" mora biti sprejet.', + 'active_url' => '":attribute" ni veljaven URL naslov.', + 'after' => '":attribute" mora biti datum po :date.', + 'after_or_equal' => '":attribute" mora biti datum, enak ali kasnejši kot :date.', + 'alpha' => '":attribute" lahko vsebuje le črke.', + 'alpha_dash' => '":attribute" lahko vsebuje le črke, številke in pomišljaje.', + 'alpha_num' => '":attribute" lahko vsebuje le črke in številke.', + 'array' => '":attribute" mora biti niz elementov.', + 'before' => '":attribute" mora biti datum pred :date.', + 'before_or_equal' => '":attribute" mora biti datum pred ali enak datumu :date.', + 'between' => [ + 'numeric' => '":attribute" mora biti med :min in :max.', + 'file' => '":attribute" mora biti med :min in max: kB.', + 'string' => '":attribute" mora vsebovati med :min in :max znakov.', + 'array' => '":attribute" mora vsebovati med :min in :max elementov.', + ], + 'boolean' => '":attribute" mora biti Da (true) ali Ne (false).', + 'confirmed' => 'Potrditev polja ":attribute" se ne ujema.', + 'date' => '":attribute" ni veljaven datum.', + 'date_format' => '":attribute" se ne ujema s formatom :format.', + 'different' => '":attribute" in :other morata biti različna.', + 'digits' => '":attribute" mora vsebovati :digits številk.', + 'digits_between' => '":attribute" mora vsebovati med :min in :max številk.', + 'dimensions' => '":attribute" vsebuje neveljavne dimenzije slike.', + 'distinct' => '":attribute" polje ima podvojeno vrednost.', + 'email' => '":attribute" mora biti veljaven e-poštni naslov.', + 'exists' => 'Izbran element ":attribute" je neveljaven.', + 'file' => '":attribute" mora biti datoteka.', + 'filled' => '":attribute" polje mora vsebovati vrednost.', + 'image' => '":attribute" mora biti slika.', + 'in' => 'Izbran element ":attribute" je neveljaven.', + 'in_array' => 'Element ":attribute" ne obstaja v ":other".', + 'integer' => '":attribute" mora biti celo število.', + 'ip' => '":attribute" mora biti veljaven IP naslov.', + 'ipv4' => '":attribute" mora biti veljaven IPv4 naslov.', + 'ipv6' => '":attribute" mora biti veljaven IPv6 naslov.', + 'json' => '":attribute" mora biti veljaven JSON format.', + 'max' => [ + 'numeric' => ':attribute ne sme biti večji od :max.', + 'file' => ':attribute ne sme biti večji od :max kB.', + 'string' => ':attribute ne sme biti večji od :max znakov.', + 'array' => ':attribute ne sme vsebovati več kot :max elementov.', + ], + 'mimes' => ':attribute mora biti datoteka tipa :values.', + 'mimetypes' => ':attribute mora biti datoteka tipa :values.', + 'min' => [ + 'numeric' => ':attribute mora biti vsaj :min.', + 'file' => ':attribute mora imeti vsaj :min kB.', + 'string' => ':attribute mora imeti vsaj :min znakov.', + 'array' => ':attribute mora vsebovati vsaj :min elementov.', + ], + 'not_in' => 'Izbrani element ":attribute" je neveljaven.', + 'numeric' => '":attribute" mora biti število.', + 'present' => 'Polje za ":attribute" mora obstajati.', + 'regex' => 'Format za ":attribute" je neveljaven.', + 'required' => 'Polje za ":attribute" je obvezno.', + 'required_if' => 'Polje za ":attribute" je obvezno, kadar ima :other vrednost :value.', + 'required_unless' => 'Polje za ":attribute" je obvezno, razen kadar ima :other vrednosti :values.', + 'required_with' => 'Polje za ":attribute" je obvezno, kadar :values obstaja.', + 'required_with_all' => 'Polje za ":attribute" je obvezno, kadar :values obstaja.', + 'required_without' => 'Polje za ":attribute" je obvezno, kadar :values ne obstaja.', + 'required_without_all' => 'Polje za ":attribute" je obvezno, kadar :values ne obstaja.', + 'same' => '":attribute" in ":other" se morata ujemati.', + 'size' => [ + 'numeric' => ':attribute mora biti :size.', + 'file' => ':attribute mora imeti :size kB.', + 'string' => ':attribute mora vsebovati :size znakov.', + 'array' => ':attribute mora vsebovati :size elementov.', + ], + 'string' => ':attribute mora biti veljaven znakovni niz.', + 'timezone' => ':attribute mora biti veljavno območje.', + 'unique' => 'Element :attribute je že uporabljen.', + 'uploaded' => 'Elementa :attribute ni bilo mogoče naložiti.', + 'url' => 'Format elementa :attribute je neveljaven.', + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => [ + 'attribute-name' => [ + 'rule-name' => 'custom-message', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap attribute place-holders + | with something more reader friendly such as E-Mail Address instead + | of "email". This simply helps us make messages a little cleaner. + | + */ + + 'attributes' => [], + +]; diff --git a/modules/system/lang/tr/lang.php b/modules/system/lang/tr/lang.php index 38521a03a..b057f50a0 100644 --- a/modules/system/lang/tr/lang.php +++ b/modules/system/lang/tr/lang.php @@ -41,6 +41,7 @@ return [ 'fi' => 'Suomi', 'sv' => 'Svenska', 'sk' => 'Slovenský', + 'sl' => 'Slovenščina', 'tr' => 'Türkçe', 'uk' => 'Українська мова', 'zh-cn' => '简体中文', diff --git a/modules/system/lang/vn/lang.php b/modules/system/lang/vn/lang.php index c828f908e..83a83c4bd 100644 --- a/modules/system/lang/vn/lang.php +++ b/modules/system/lang/vn/lang.php @@ -39,6 +39,7 @@ return [ 'fi' => 'Suomi', 'sv' => 'Svenska', 'sk' => 'Slovenský', + 'sl' => 'Slovenščina', 'tr' => 'Türkçe', 'uk' => 'Українська мова', 'zh-cn' => '简体中文', From d4d18743119f61a9812d23f755e166dddb7ee32b Mon Sep 17 00:00:00 2001 From: Philipp Lang Date: Tue, 10 Dec 2019 10:21:56 +0100 Subject: [PATCH 39/88] Allow setting customview path for relation list (#4680) --- modules/backend/behaviors/RelationController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/backend/behaviors/RelationController.php b/modules/backend/behaviors/RelationController.php index 2306b5ffb..deb84260f 100644 --- a/modules/backend/behaviors/RelationController.php +++ b/modules/backend/behaviors/RelationController.php @@ -666,6 +666,7 @@ class RelationController extends ControllerBehavior $config->recordsPerPage = $this->getConfig('view[recordsPerPage]'); $config->showCheckboxes = $this->getConfig('view[showCheckboxes]', !$this->readOnly); $config->recordUrl = $this->getConfig('view[recordUrl]', null); + $config->customViewPath = $this->getConfig('view[customViewPath]', null); $defaultOnClick = sprintf( "$.oc.relationBehavior.clickViewListRecord(':%s', '%s', '%s')", From 5839b6869a76d0d4a79ae635fa2d6789e8dfbd59 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Tue, 10 Dec 2019 20:41:19 +1100 Subject: [PATCH 40/88] Recompile assets --- modules/system/assets/js/lang/lang.cs.js | 2 +- modules/system/assets/js/lang/lang.fr.js | 95 ++++--- modules/system/assets/js/lang/lang.sl.js | 332 ++++++++--------------- 3 files changed, 156 insertions(+), 273 deletions(-) diff --git a/modules/system/assets/js/lang/lang.cs.js b/modules/system/assets/js/lang/lang.cs.js index 4c573e9a6..adb700628 100644 --- a/modules/system/assets/js/lang/lang.cs.js +++ b/modules/system/assets/js/lang/lang.cs.js @@ -5,7 +5,7 @@ if ($.oc === undefined) $.oc = {} if ($.oc.langMessages === undefined) $.oc.langMessages = {} $.oc.langMessages['cs'] = $.extend( $.oc.langMessages['cs'] || {}, - {"markdowneditor":{"formatting":"Form\u00e1tov\u00e1n\u00ed","quote":"Citace","code":"K\u00f3d","header1":"Nadpis 1","header2":"Nadpis 2","header3":"Nadpis 3","header4":"Nadpis 4","header5":"Nadpis 5","header6":"Nadpis 6","bold":"Tu\u010dn\u011b","italic":"Kurz\u00edvou","unorderedlist":"Ne\u010d\u00edslovan\u00fd seznam","orderedlist":"\u010c\u00edslovan\u00fd seznam","video":"Video","image":"Obr\u00e1zek","link":"Odkaz","horizontalrule":"Vlo\u017eit horizont\u00e1ln\u00ed linku","fullscreen":"Cel\u00e1 obrazovka","preview":"N\u00e1hled"},"mediamanager":{"insert_link":"Vlo\u017eit odkaz","insert_image":"Vlo\u017eit obr\u00e1zek","insert_video":"Vlo\u017eit video","insert_audio":"Vlo\u017eit zvuk","invalid_file_empty_insert":"Pros\u00edm vyberte soubor, na kter\u00fd se vlo\u017e\u00ed odkaz.","invalid_file_single_insert":"Vyberte jeden soubor.","invalid_image_empty_insert":"Vyberte soubor(y) pro vlo\u017een\u00ed.","invalid_video_empty_insert":"Vyberte video soubor pro vlo\u017een\u00ed.","invalid_audio_empty_insert":"Vyberte audio soubor pro vlo\u017een\u00ed."},"alert":{"confirm_button_text":"OK","cancel_button_text":"Zru\u0161it","widget_remove_confirm":"Odstranit widget?"},"datepicker":{"previousMonth":"P\u0159edchoz\u00ed m\u011bs\u00edc","nextMonth":"N\u00e1sleduj\u00edc\u00ed m\u011bs\u00edc","months":["Leden","\u00danor","B\u0159ezen","Duben","Kv\u011bten","\u010cerven","\u010cervenec","Srpen","Z\u00e1\u0159\u00ed","\u0158\u00edjen","Listopad","Prosinec"],"weekdays":["Ned\u011ble","Pond\u011bl\u00ed","\u00dater\u00fd","St\u0159eda","\u010ctvrtek","P\u00e1tek","Sobota"],"weekdaysShort":["Ne","Po","\u00dat","St","\u010ct","P\u00e1","So"]},"colorpicker":{"choose":"Ok"},"filter":{"group":{"all":"V\u0161e"},"scopes":{"apply_button_text":"Apply","clear_button_text":"Clear"},"dates":{"all":"V\u0161e","filter_button_text":"Filtrovat","reset_button_text":"Zru\u0161it","date_placeholder":"Datum","after_placeholder":"Po","before_placeholder":"P\u0159ed"},"numbers":{"all":"all","filter_button_text":"Filter","reset_button_text":"Reset","min_placeholder":"Min","max_placeholder":"Max"}},"eventlog":{"show_stacktrace":"Zobrazit stacktrace","hide_stacktrace":"Skr\u00fdt stacktrace","tabs":{"formatted":"Form\u00e1tov\u00e1no","raw":"P\u016fvodn\u00ed (raw)"},"editor":{"title":"Editor zdrojov\u00e9ho k\u00f3du","description":"V\u00e1\u0161 opera\u010dn\u00ed syst\u00e9m by m\u011bl b\u00fdt konfigurov\u00e1n tak, aby naslouchal jednomu z t\u011bchto sch\u00e9mat adres URL.","openWith":"Otev\u0159\u00edt v","remember_choice":"Zapamatovat si vybranou volbu pro tuto relaci","open":"Otev\u0159\u00edt","cancel":"Zru\u0161it"}}} + {"markdowneditor":{"formatting":"Form\u00e1tov\u00e1n\u00ed","quote":"Citace","code":"K\u00f3d","header1":"Nadpis 1","header2":"Nadpis 2","header3":"Nadpis 3","header4":"Nadpis 4","header5":"Nadpis 5","header6":"Nadpis 6","bold":"Tu\u010dn\u011b","italic":"Kurz\u00edvou","unorderedlist":"Ne\u010d\u00edslovan\u00fd seznam","orderedlist":"\u010c\u00edslovan\u00fd seznam","video":"Video","image":"Obr\u00e1zek","link":"Odkaz","horizontalrule":"Vlo\u017eit horizont\u00e1ln\u00ed linku","fullscreen":"Cel\u00e1 obrazovka","preview":"N\u00e1hled"},"mediamanager":{"insert_link":"Vlo\u017eit odkaz","insert_image":"Vlo\u017eit obr\u00e1zek","insert_video":"Vlo\u017eit video","insert_audio":"Vlo\u017eit zvuk","invalid_file_empty_insert":"Pros\u00edm vyberte soubor, na kter\u00fd se vlo\u017e\u00ed odkaz.","invalid_file_single_insert":"Vyberte jeden soubor.","invalid_image_empty_insert":"Vyberte soubor(y) pro vlo\u017een\u00ed.","invalid_video_empty_insert":"Vyberte video soubor pro vlo\u017een\u00ed.","invalid_audio_empty_insert":"Vyberte audio soubor pro vlo\u017een\u00ed."},"alert":{"confirm_button_text":"OK","cancel_button_text":"Zru\u0161it","widget_remove_confirm":"Odstranit widget?"},"datepicker":{"previousMonth":"P\u0159edchoz\u00ed m\u011bs\u00edc","nextMonth":"N\u00e1sleduj\u00edc\u00ed m\u011bs\u00edc","months":["Leden","\u00danor","B\u0159ezen","Duben","Kv\u011bten","\u010cerven","\u010cervenec","Srpen","Z\u00e1\u0159\u00ed","\u0158\u00edjen","Listopad","Prosinec"],"weekdays":["Ned\u011ble","Pond\u011bl\u00ed","\u00dater\u00fd","St\u0159eda","\u010ctvrtek","P\u00e1tek","Sobota"],"weekdaysShort":["Ne","Po","\u00dat","St","\u010ct","P\u00e1","So"]},"colorpicker":{"choose":"Ok"},"filter":{"group":{"all":"V\u0161e"},"scopes":{"apply_button_text":"Filtrovat","clear_button_text":"Zru\u0161it"},"dates":{"all":"V\u0161e","filter_button_text":"Filtrovat","reset_button_text":"Zru\u0161it","date_placeholder":"Datum","after_placeholder":"Po","before_placeholder":"P\u0159ed"},"numbers":{"all":"V\u0161e","filter_button_text":"Filtrovat","reset_button_text":"Zru\u0161it","min_placeholder":"Minimum","max_placeholder":"Maximum"}},"eventlog":{"show_stacktrace":"Zobrazit stacktrace","hide_stacktrace":"Skr\u00fdt stacktrace","tabs":{"formatted":"Form\u00e1tov\u00e1no","raw":"P\u016fvodn\u00ed (raw)"},"editor":{"title":"Editor zdrojov\u00e9ho k\u00f3du","description":"V\u00e1\u0161 opera\u010dn\u00ed syst\u00e9m by m\u011bl b\u00fdt konfigurov\u00e1n tak, aby naslouchal jednomu z t\u011bchto sch\u00e9mat adres URL.","openWith":"Otev\u0159\u00edt v","remember_choice":"Zapamatovat si vybranou volbu pro tuto relaci","open":"Otev\u0159\u00edt","cancel":"Zru\u0161it"}}} ); //! moment.js locale configuration v2.22.2 diff --git a/modules/system/assets/js/lang/lang.fr.js b/modules/system/assets/js/lang/lang.fr.js index 912c23504..f383b27e5 100644 --- a/modules/system/assets/js/lang/lang.fr.js +++ b/modules/system/assets/js/lang/lang.fr.js @@ -5,62 +5,61 @@ if ($.oc === undefined) $.oc = {} if ($.oc.langMessages === undefined) $.oc.langMessages = {} $.oc.langMessages['fr'] = $.extend( $.oc.langMessages['fr'] || {}, - { "markdowneditor": { "formatting": "Formatage", "quote": "Citation", "code": "Code", "header1": "Ent\u00eate 1", "header2": "Ent\u00eate 2", "header3": "Ent\u00eate 3", "header4": "Ent\u00eate 4", "header5": "Ent\u00eate 5", "header6": "Ent\u00eate 6", "bold": "Gras", "italic": "Italique", "unorderedlist": "Liste non ordonn\u00e9e", "orderedlist": "Liste ordonn\u00e9e", "video": "Vid\u00e9o", "image": "Image", "link": "Lien", "horizontalrule": "Ins\u00e9rer la r\u00e8gle horizontalement", "fullscreen": "Plein \u00e9cran", "preview": "Aper\u00e7u" }, "mediamanager": { "insert_link": "Ins\u00e9rer un lien vers un fichier du gestionnaire de m\u00e9dia", "insert_image": "Ins\u00e9rer une image du gestionnaire de m\u00e9dia", "insert_video": "Ins\u00e9rer une vid\u00e9o du gestionnaire de m\u00e9dia", "insert_audio": "Ins\u00e9rer un document audio du gestionnaire de m\u00e9dia", "invalid_file_empty_insert": "Veuillez s\u00e9lectionner un fichier \u00e0 lier.", "invalid_file_single_insert": "Veuillez s\u00e9lectionner un seul fichier.", "invalid_image_empty_insert": "Veuillez s\u00e9lectionner au moins une image \u00e0 ins\u00e9rer.", "invalid_video_empty_insert": "Veuillez s\u00e9lectionner une vid\u00e9o \u00e0 ins\u00e9rer.", "invalid_audio_empty_insert": "Veuillez s\u00e9lectionner un document audio \u00e0 ins\u00e9rer." }, "alert": { "confirm_button_text": "OK", "cancel_button_text": "Annuler", "widget_remove_confirm": "Retirer ce widget ?" }, "datepicker": { "previousMonth": "Mois pr\u00e9c\u00e9dent", "nextMonth": "Mois suivant", "months": ["Janvier", "F\u00e9vrier", "Mars", "Avril", "Mai", "Juin", "Juillet", "Ao\u00fbt", "Septembre", "Octobre", "Novembre", "D\u00e9cembre"], "weekdays": ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"], "weekdaysShort": ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"] }, "colorpicker": { "choose": "Ok" }, "filter": { "group": { "all": "tous" }, "scopes": { "apply_button_text": "Appliquer", "clear_button_text": "Annuler" }, "dates": { "all": "toute la p\u00e9riode", "filter_button_text": "Filtrer", "reset_button_text": "Effacer", "date_placeholder": "Date", "after_placeholder": "Apr\u00e8s le", "before_placeholder": "Avant le" }, "numbers": { "all": "tous", "filter_button_text": "Filtres", "reset_button_text": "R\u00e9initialiser", "min_placeholder": "Min", "max_placeholder": "Max" } }, "eventlog": { "show_stacktrace": "Afficher la pile d\u2019ex\u00e9cution", "hide_stacktrace": "Masquer la pile d\u2019ex\u00e9cution", "tabs": { "formatted": "Message format\u00e9", "raw": "Message brut" }, "editor": { "title": "S\u00e9lectionnez l\u2019\u00e9diteur de code source \u00e0 utiliser", "description": "L\u2019environnement de votre syst\u00e8me d\u2019exploitation doit \u00eatre configur\u00e9 pour ouvrir l\u2019un des sch\u00e9mas d\u2019URL ci-dessous.", "openWith": "Ouvrir avec", "remember_choice": "Se souvenir de la s\u00e9lection pour la dur\u00e9e de la session dans ce navigateur", "open": "Ouvrir", "cancel": "Annuler" } } } + {"markdowneditor":{"formatting":"Formatage","quote":"Citation","code":"Code","header1":"Ent\u00eate 1","header2":"Ent\u00eate 2","header3":"Ent\u00eate 3","header4":"Ent\u00eate 4","header5":"Ent\u00eate 5","header6":"Ent\u00eate 6","bold":"Gras","italic":"Italique","unorderedlist":"Liste non ordonn\u00e9e","orderedlist":"Liste ordonn\u00e9e","video":"Vid\u00e9o","image":"Image","link":"Lien","horizontalrule":"Ins\u00e9rer la r\u00e8gle horizontalement","fullscreen":"Plein \u00e9cran","preview":"Aper\u00e7u"},"mediamanager":{"insert_link":"Ins\u00e9rer un lien vers un fichier du gestionnaire de m\u00e9dia","insert_image":"Ins\u00e9rer une image du gestionnaire de m\u00e9dia","insert_video":"Ins\u00e9rer une vid\u00e9o du gestionnaire de m\u00e9dia","insert_audio":"Ins\u00e9rer un document audio du gestionnaire de m\u00e9dia","invalid_file_empty_insert":"Veuillez s\u00e9lectionner un fichier \u00e0 lier.","invalid_file_single_insert":"Veuillez s\u00e9lectionner un seul fichier.","invalid_image_empty_insert":"Veuillez s\u00e9lectionner au moins une image \u00e0 ins\u00e9rer.","invalid_video_empty_insert":"Veuillez s\u00e9lectionner une vid\u00e9o \u00e0 ins\u00e9rer.","invalid_audio_empty_insert":"Veuillez s\u00e9lectionner un document audio \u00e0 ins\u00e9rer."},"alert":{"confirm_button_text":"OK","cancel_button_text":"Annuler","widget_remove_confirm":"Retirer ce widget ?"},"datepicker":{"previousMonth":"Mois pr\u00e9c\u00e9dent","nextMonth":"Mois suivant","months":["Janvier","F\u00e9vrier","Mars","Avril","Mai","Juin","Juillet","Ao\u00fbt","Septembre","Octobre","Novembre","D\u00e9cembre"],"weekdays":["Dimanche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi"],"weekdaysShort":["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"]},"colorpicker":{"choose":"Ok"},"filter":{"group":{"all":"tous"},"scopes":{"apply_button_text":"Appliquer","clear_button_text":"Annuler"},"dates":{"all":"toute la p\u00e9riode","filter_button_text":"Filtrer","reset_button_text":"Effacer","date_placeholder":"Date","after_placeholder":"Apr\u00e8s le","before_placeholder":"Avant le"},"numbers":{"all":"tous","filter_button_text":"Filtres","reset_button_text":"R\u00e9initialiser","min_placeholder":"Min","max_placeholder":"Max"}},"eventlog":{"show_stacktrace":"Afficher la pile d\u2019ex\u00e9cution","hide_stacktrace":"Masquer la pile d\u2019ex\u00e9cution","tabs":{"formatted":"Message format\u00e9","raw":"Message brut"},"editor":{"title":"S\u00e9lectionnez l\u2019\u00e9diteur de code source \u00e0 utiliser","description":"L\u2019environnement de votre syst\u00e8me d\u2019exploitation doit \u00eatre configur\u00e9 pour ouvrir l\u2019un des sch\u00e9mas d\u2019URL ci-dessous.","openWith":"Ouvrir avec","remember_choice":"Se souvenir de la s\u00e9lection pour la dur\u00e9e de la session dans ce navigateur","open":"Ouvrir","cancel":"Annuler"}}} ); //! moment.js locale configuration v2.22.2 -; (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' - && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['../moment'], factory) : - factory(global.moment) -}(this, (function (moment) { - 'use strict'; +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; var fr = moment.defineLocale('fr', { - months: 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'), - monthsShort: 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'), - monthsParseExact: true, - weekdays: 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'), - weekdaysShort: 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'), - weekdaysMin: 'di_lu_ma_me_je_ve_sa'.split('_'), - weekdaysParseExact: true, - longDateFormat: { - LT: 'HH:mm', - LTS: 'HH:mm:ss', - L: 'DD/MM/YYYY', - LL: 'D MMMM YYYY', - LLL: 'D MMMM YYYY HH:mm', - LLLL: 'dddd D MMMM YYYY HH:mm' + months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'), + monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'), + monthsParseExact : true, + weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'), + weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'), + weekdaysMin : 'di_lu_ma_me_je_ve_sa'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'HH:mm', + LTS : 'HH:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY HH:mm', + LLLL : 'dddd D MMMM YYYY HH:mm' }, - calendar: { - sameDay: '[Aujourd’hui à] LT', - nextDay: '[Demain à] LT', - nextWeek: 'dddd [à] LT', - lastDay: '[Hier à] LT', - lastWeek: 'dddd [dernier à] LT', - sameElse: 'L' + calendar : { + sameDay : '[Aujourd’hui à] LT', + nextDay : '[Demain à] LT', + nextWeek : 'dddd [à] LT', + lastDay : '[Hier à] LT', + lastWeek : 'dddd [dernier à] LT', + sameElse : 'L' }, - relativeTime: { - future: 'dans %s', - past: 'il y a %s', - s: 'quelques secondes', - ss: '%d secondes', - m: 'une minute', - mm: '%d minutes', - h: 'une heure', - hh: '%d heures', - d: 'un jour', - dd: '%d jours', - M: 'un mois', - MM: '%d mois', - y: 'un an', - yy: '%d ans' + relativeTime : { + future : 'dans %s', + past : 'il y a %s', + s : 'quelques secondes', + ss : '%d secondes', + m : 'une minute', + mm : '%d minutes', + h : 'une heure', + hh : '%d heures', + d : 'un jour', + dd : '%d jours', + M : 'un mois', + MM : '%d mois', + y : 'un an', + yy : '%d ans' }, dayOfMonthOrdinalParse: /\d{1,2}(er|)/, - ordinal: function (number, period) { + ordinal : function (number, period) { switch (period) { // TODO: Return 'e' when day of month > 1. Move this case inside // block for masculine words below. @@ -82,9 +81,9 @@ $.oc.langMessages['fr'] = $.extend( return number + (number === 1 ? 're' : 'e'); } }, - week: { - dow: 1, // Monday is the first day of the week. - doy: 4 // The week that contains Jan 4th is the first week of the year. + week : { + dow : 1, // Monday is the first day of the week. + doy : 4 // The week that contains Jan 4th is the first week of the year. } }); diff --git a/modules/system/assets/js/lang/lang.sl.js b/modules/system/assets/js/lang/lang.sl.js index 0caba5199..cd1b00afd 100644 --- a/modules/system/assets/js/lang/lang.sl.js +++ b/modules/system/assets/js/lang/lang.sl.js @@ -11,218 +11,103 @@ $.oc.langMessages['sl'] = $.extend( //! moment.js locale configuration v2.22.2 ;(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' - && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['../moment'], factory) : - factory(global.moment) + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) }(this, (function (moment) { 'use strict'; - function translate(number, withoutSuffix, key, isFuture) { + + function processRelativeTime(number, withoutSuffix, key, isFuture) { var result = number + ' '; switch (key) { - case 's': // a few seconds / in a few seconds / a few seconds ago - return (withoutSuffix || isFuture) ? 'nekaj sekund' : 'nekaj sekundami'; - case 'ss': // 9 seconds / in 9 seconds / 9 seconds ago - if (withoutSuffix) { - if (number == 1) { - return result + 'sekunda'; - } else if (number == 2) { - return result + 'sekundi'; - } else if (number == 3 || number == 4) { - return result + 'sekunde'; - } else { - return result + 'sekund'; - } - } else if (isFuture) { - if (number == 1) { - return result + 'sekundo'; - } else if (number == 2) { - return result + 'sekundi'; - } else if (number == 3 || number == 4) { - return result + 'sekunde'; - } else { - return result + 'sekund'; - } + case 's': + return withoutSuffix || isFuture ? 'nekaj sekund' : 'nekaj sekundami'; + case 'ss': + if (number === 1) { + result += withoutSuffix ? 'sekundo' : 'sekundi'; + } else if (number === 2) { + result += withoutSuffix || isFuture ? 'sekundi' : 'sekundah'; + } else if (number < 5) { + result += withoutSuffix || isFuture ? 'sekunde' : 'sekundah'; } else { - if (number == 1) { - return result + 'sekundo'; - } else if (number == 2) { - return result + 'sekundama'; - } else { - return result + 'sekundami'; - } + result += withoutSuffix || isFuture ? 'sekund' : 'sekund'; } - break; - case 'm': // a minute / in a minute / a minute ago - return withoutSuffix ? 'minuta' : 'minuto'; - case 'mm': // 9 minutes / in 9 minutes / 9 minutes ago - if (withoutSuffix) { - if (number == 1) { - return result + 'minuta'; - } else if (number == 2) { - return result + 'minuti'; - } else if (number == 3 || number == 4) { - return result + 'minute'; - } else { - return result + 'minut'; - } - } else if (isFuture) { - if (number == 1) { - return result + 'minuto'; - } else if (number == 2) { - return result + 'minuti'; - } else if (number == 3 || number == 4) { - return result + 'minute'; - } else { - return result + 'minut'; - } + return result; + case 'm': + return withoutSuffix ? 'ena minuta' : 'eno minuto'; + case 'mm': + if (number === 1) { + result += withoutSuffix ? 'minuta' : 'minuto'; + } else if (number === 2) { + result += withoutSuffix || isFuture ? 'minuti' : 'minutama'; + } else if (number < 5) { + result += withoutSuffix || isFuture ? 'minute' : 'minutami'; } else { - if (number == 1) { - return result + 'minuto'; - } else if (number == 2) { - return result + 'minutama'; - } else { - return result + 'minutami'; - } + result += withoutSuffix || isFuture ? 'minut' : 'minutami'; } - break; - case 'h': // an hour / in an hour / an hour ago - return withoutSuffix ? 'ura' : 'eno uro'; - case 'hh': // 9 hours / in 9 hours / 9 hours ago - if (withoutSuffix) { - if (number == 1) { - return result + 'ura'; - } else if (number == 2) { - return result + 'uri'; - } else if (number == 3 || number == 4) { - return result + 'ure'; - } else { - return result + 'ur'; - } - } else if (isFuture) { - if (number == 1) { - return result + 'uro'; - } else if (number == 2) { - return result + 'uri'; - } else if (number == 3 || number == 4) { - return result + 'ure'; - } else { - return result + 'ur'; - } + return result; + case 'h': + return withoutSuffix ? 'ena ura' : 'eno uro'; + case 'hh': + if (number === 1) { + result += withoutSuffix ? 'ura' : 'uro'; + } else if (number === 2) { + result += withoutSuffix || isFuture ? 'uri' : 'urama'; + } else if (number < 5) { + result += withoutSuffix || isFuture ? 'ure' : 'urami'; } else { - if (number == 1) { - return result + 'uro'; - } else if (number == 2) { - return result + 'urama'; - } else { - return result + 'urami'; - } + result += withoutSuffix || isFuture ? 'ur' : 'urami'; } - break; - case 'd': // a day / in a day / a day ago - return withoutSuffix ? 'dan' : (isFuture ? 'en dan' : 'enim dnem'); - case 'dd': // 9 days / in 9 days / 9 days ago - if (withoutSuffix) { - if (number == 1) { - return result + 'dan'; - } else if (number == 2) { - return result + 'dneva'; - } else if (number == 3 || number == 4) { - return result + 'dnevi'; - } else { - return result + 'dni'; - } - } else if (isFuture) { - if (number == 1) { - return result + 'dan'; - } else { - return result + 'dni'; - } + return result; + case 'd': + return withoutSuffix || isFuture ? 'en dan' : 'enim dnem'; + case 'dd': + if (number === 1) { + result += withoutSuffix || isFuture ? 'dan' : 'dnem'; + } else if (number === 2) { + result += withoutSuffix || isFuture ? 'dni' : 'dnevoma'; } else { - if (number == 1) { - return result + 'dnevom'; - } else if (number == 2) { - return result + 'dnevoma'; - } else { - return result + 'dnevi'; - } + result += withoutSuffix || isFuture ? 'dni' : 'dnevi'; } - break; - case 'M': // a month / in a month / a month ago - return withoutSuffix ? 'mesec' : (isFuture ? 'en mesec' : 'enim mesecem'); - case 'MM': // 9 months / in 9 months / 9 months ago - if (withoutSuffix) { - if (number == 1) { - return result + 'mesec'; - } else if (number == 2) { - return result + 'meseca'; - } else if (number == 3 || number == 4) { - return result + 'meseci'; - } else { - return result + 'mesecev'; - } - } else if (isFuture) { - if (number == 1) { - return result + 'mesec'; - } else if (number == 2) { - return result + 'meseca'; - } else if (number == 3 || number == 4) { - return result + 'mesece'; - } else { - return result + 'mesecev'; - } + return result; + case 'M': + return withoutSuffix || isFuture ? 'en mesec' : 'enim mesecem'; + case 'MM': + if (number === 1) { + result += withoutSuffix || isFuture ? 'mesec' : 'mesecem'; + } else if (number === 2) { + result += withoutSuffix || isFuture ? 'meseca' : 'mesecema'; + } else if (number < 5) { + result += withoutSuffix || isFuture ? 'mesece' : 'meseci'; } else { - if (number == 1) { - return result + 'mesecom'; - } else if (number == 2) { - return result + 'mesecema'; - } else { - return result + 'meseci'; - } + result += withoutSuffix || isFuture ? 'mesecev' : 'meseci'; } - break; - case 'y': // a year / in a year / a year ago - return withoutSuffix ? 'leto' : (isFuture ? 'eno leto' : 'enim letom'); - case 'yy': // 9 years / in 9 years / 9 years ago - if (withoutSuffix) { - if (number == 1) { - return result + 'leto'; - } else if (number == 2) { - return result + 'leti'; - } else if (number == 3 || number == 4) { - return result + 'leta'; - } else { - return result + 'let'; - } - } else if (isFuture) { - if (number == 1) { - return result + 'leto'; - } else if (number == 2) { - return result + 'leti'; - } else if (number == 3 || number == 4) { - return result + 'leta'; - } else { - return result + 'let'; - } + return result; + case 'y': + return withoutSuffix || isFuture ? 'eno leto' : 'enim letom'; + case 'yy': + if (number === 1) { + result += withoutSuffix || isFuture ? 'leto' : 'letom'; + } else if (number === 2) { + result += withoutSuffix || isFuture ? 'leti' : 'letoma'; + } else if (number < 5) { + result += withoutSuffix || isFuture ? 'leta' : 'leti'; } else { - if (number == 1) { - return result + 'letom'; - } else if (number == 2) { - return result + 'letoma'; - } else { - return result + 'leti'; - } + result += withoutSuffix || isFuture ? 'let' : 'leti'; } - break; + return result; } } var sl = moment.defineLocale('sl', { months : 'januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december'.split('_'), - monthsShort : 'jan_feb_mar_apr_maj_jun_jul_avg_sep_okt_nov_dec'.split('_'), - weekdays : 'nedelja_ponedeljek_torek_sreda_\u010detrtek_petek_sobota'.split('_'), - weekdaysShort : 'ned_pon_tor_sre_\u010det_pet_sob'.split('_'), - weekdaysMin : 'ne_po_to_sr_\u010de_pe_so'.split('_'), + monthsShort : 'jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.'.split('_'), + monthsParseExact: true, + weekdays : 'nedelja_ponedeljek_torek_sreda_četrtek_petek_sobota'.split('_'), + weekdaysShort : 'ned._pon._tor._sre._čet._pet._sob.'.split('_'), + weekdaysMin : 'ne_po_to_sr_če_pe_so'.split('_'), + weekdaysParseExact : true, longDateFormat : { LT : 'H:mm', LTS : 'H:mm:ss', @@ -232,64 +117,63 @@ $.oc.langMessages['sl'] = $.extend( LLLL : 'dddd, D. MMMM YYYY H:mm' }, calendar : { - sameDay : '[danes ob] LT', - nextDay : '[jutri ob] LT', - nextWeek: function () { + sameDay : '[danes ob] LT', + nextDay : '[jutri ob] LT', + + nextWeek : function () { switch (this.day()) { case 0: - return '[v nedeljo ob] LT'; + return '[v] [nedeljo] [ob] LT'; + case 3: + return '[v] [sredo] [ob] LT'; + case 6: + return '[v] [soboto] [ob] LT'; case 1: case 2: - return '[v] dddd [ob] LT'; - case 3: - return '[v sredo ob] LT'; case 4: case 5: return '[v] dddd [ob] LT'; - case 6: - return '[v soboto ob] LT'; } }, - lastDay : '[včeraj ob] LT', - lastWeek: function () { + lastDay : '[včeraj ob] LT', + lastWeek : function () { switch (this.day()) { case 0: - return '[prej\u0161njo nedeljo ob] LT'; + return '[prejšnjo] [nedeljo] [ob] LT'; + case 3: + return '[prejšnjo] [sredo] [ob] LT'; + case 6: + return '[prejšnjo] [soboto] [ob] LT'; case 1: case 2: - return '[prej\u0161nji] dddd [ob] LT'; - case 3: - return '[prej\u0161njo sredo ob] LT'; case 4: case 5: - return '[prej\u0161nji] dddd [ob] LT'; - case 6: - return '[prej\u0161njo soboto ob] LT'; + return '[prejšnji] dddd [ob] LT'; } }, sameElse : 'L' }, relativeTime : { - future : '\u010dez %s', - past : 'pred %s', - s : translate, - ss : translate, - m : translate, - mm : translate, - h : translate, - hh : translate, - d : translate, - dd : translate, - M : translate, - MM : translate, - y : translate, - yy : translate + future : 'čez %s', + past : 'pred %s', + s : processRelativeTime, + ss : processRelativeTime, + m : processRelativeTime, + mm : processRelativeTime, + h : processRelativeTime, + hh : processRelativeTime, + d : processRelativeTime, + dd : processRelativeTime, + M : processRelativeTime, + MM : processRelativeTime, + y : processRelativeTime, + yy : processRelativeTime }, dayOfMonthOrdinalParse: /\d{1,2}\./, ordinal : '%d.', week : { dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. + doy : 7 // The week that contains Jan 1st is the first week of the year. } }); From c17cb58aa121d06174f93ce5024a7946fb774dd9 Mon Sep 17 00:00:00 2001 From: Ben Thomson Date: Thu, 12 Dec 2019 18:44:10 +0800 Subject: [PATCH 41/88] More strict checking of addItem request for child repeaters (#4814) Similarly named repeater fields being used in viewBag variables were being assigned aliases which succeeded the `strpos` check on line 407. This will more clearly look for a child repeater form and index. Fixes #4808 --- modules/backend/formwidgets/Repeater.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/backend/formwidgets/Repeater.php b/modules/backend/formwidgets/Repeater.php index e25df461f..f9fa3a6fb 100644 --- a/modules/backend/formwidgets/Repeater.php +++ b/modules/backend/formwidgets/Repeater.php @@ -98,7 +98,7 @@ class Repeater extends FormWidgetBase public function init() { $this->prompt = Lang::get('backend::lang.repeater.add_new_item'); - + $this->fillFromConfig([ 'form', 'prompt', @@ -404,15 +404,15 @@ class Repeater extends FormWidgetBase if ($this->alias === $widgetName) { // This repeater has made the AJAX request self::$onAddItemCalled = true; - } else if (strpos($widgetName, $this->alias) === 0) { + } else if (strpos($widgetName, $this->alias . 'Form') === 0) { // A child repeater has made the AJAX request // Get index from AJAX handler $handlerSuffix = str_replace($this->alias . 'Form', '', $widgetName); - preg_match('/^[0-9]+/', $handlerSuffix, $matches); - - $this->childAddItemCalled = true; - $this->childIndexCalled = (int) $matches[0]; + if (preg_match('/^[0-9]+/', $handlerSuffix, $matches)) { + $this->childAddItemCalled = true; + $this->childIndexCalled = (int) $matches[0]; + } } } From 22db1299ae9fd169745dc6f947ffb4a64c977991 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Thu, 12 Dec 2019 22:02:39 +1100 Subject: [PATCH 42/88] Exception handling $widget->secondaryTabs['fields'] may not always be present --- modules/cms/controllers/Index.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/cms/controllers/Index.php b/modules/cms/controllers/Index.php index 4f9d2e78d..07af395c1 100644 --- a/modules/cms/controllers/Index.php +++ b/modules/cms/controllers/Index.php @@ -68,7 +68,12 @@ class Index extends Controller if (!$widget->model instanceof CmsCompoundObject) { return; } - if (key_exists('code', $widget->secondaryTabs['fields']) && CmsHelpers::safeModeEnabled()) { + + if (empty($widget->secondaryTabs['fields'])) { + return; + } + + if (array_key_exists('code', $widget->secondaryTabs['fields']) && CmsHelpers::safeModeEnabled()) { $widget->secondaryTabs['fields']['safemode_notice']['hidden'] = false; $widget->secondaryTabs['fields']['code']['readOnly'] = true; }; From e1a698ee52a2aff7b0509c2e6cd74e0e59c28659 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Thu, 12 Dec 2019 22:47:07 +1100 Subject: [PATCH 43/88] Add premium support, contributor badges + minor changes --- README.md | 72 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index de8b75333..97f3c6d79 100644 --- a/README.md +++ b/README.md @@ -29,38 +29,24 @@ php artisan october:install ## Learning October -The best place to learn October is by [reading the documentation](https://octobercms.com/docs) or [following some tutorials](https://octobercms.com/support/articles/tutorials). +The best place to learn October is by [reading the documentation](https://octobercms.com/docs), [watching some screencasts](https://octobercms.com/support/topic/screencast) or [following some tutorials](https://octobercms.com/support/articles/tutorials). -You may also watch these introductory videos for [beginners](https://vimeo.com/79963873) and [advanced users](https://vimeo.com/172202661). There is also the excellent video series by [Watch & Learn](https://watch-learn.com/series/making-websites-with-october-cms). - -## Contributing - -Before sending a Pull Request, be sure to review the [Contributing Guidelines](.github/CONTRIBUTING.md) first. - -### Help and support this project - -You can also help the project by reviewing and testing open Pull Requests with the "**Status: Testing Needed**" tag. -[Read more...](https://github.com/octobercms/october/blob/master/.github/CONTRIBUTING.md#testing-pull-requests) - -## Coding standards - -Please follow the following guides and code standards: - -* [PSR 4 Coding Standards](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md) -* [PSR 2 Coding Style Guide](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) -* [PSR 1 Coding Standards](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md) - -## Code of Conduct - -In order to ensure that the OctoberCMS community is welcoming to all, please review and abide by the [Code of Conduct](CODE_OF_CONDUCT.md). - -## Security Vulnerabilities - -Please review [our security policy](https://github.com/octobercms/october/security/policy) on how to report security vulnerabilities. +You may also watch these introductory videos for [beginners](https://vimeo.com/79963873) and [advanced users](https://vimeo.com/172202661). ## Development Team -October was created by [Alexey Bobkov](http://ca.linkedin.com/pub/aleksey-bobkov/2b/ba0/232) and [Samuel Georges](https://www.linkedin.com/in/samuel-georges-0a964131/). The core maintainer is [Luke Towers](https://luketowers.ca/) and other maintainers include [Ben Thomson](https://github.com/bennothommo) and [Denis Denisov](https://github.com/w20k). +October was created by [Alexey Bobkov](http://ca.linkedin.com/pub/aleksey-bobkov/2b/ba0/232) and [Samuel Georges](https://www.linkedin.com/in/samuel-georges-0a964131/), who both continue to develop the platform. + +The maintenance of October is lead by [Luke Towers](https://luketowers.ca/), along with many wonderful people that dedicate their time to help support and grow the community. + + + + + + + + +
    Luke Towers
    Luke Towers
    Ben Thomson
    Ben Thomson
    Denis Denisov
    Denis Denisov
    Marc Jauvin
    Marc Jauvin
    ## Foundation library @@ -72,9 +58,33 @@ You can communicate with us using the following mediums: * [Follow us on Twitter](https://twitter.com/octobercms) for announcements and updates. * [Follow us on Facebook](https://facebook.com/octobercms) for announcements and updates. -* [Join us on Slack](https://octobercms-slack.herokuapp.com/) to chat with us. -* [Join us on IRC](https://octobercms.com/chat) to chat with us. +* [Join the Official Forum](https://octobercms.com/forum) to engage with the community. +* [Join us on Discord](https://octobercms.com/chat) to chat with us. + +### Premium Support + +October CMS can provide premium support for a monthly fee. Find out more via the [Premium Support Program](https://octobercms.com/premium-support). + +## Contributing + +Before sending or reviewing Pull Requests, be sure to review the [Contributing Guidelines](.github/CONTRIBUTING.md) first. + +### Coding standards + +Please follow the following guides and code standards: + +* [PSR 4 Coding Standards](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md) +* [PSR 2 Coding Style Guide](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) +* [PSR 1 Coding Standards](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md) + +### Code of Conduct + +In order to ensure that the October CMS community is welcoming to all, please review and abide by the [Code of Conduct](CODE_OF_CONDUCT.md). ## License -The OctoberCMS platform is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). +The October CMS platform is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). + +## Security Vulnerabilities + +Please review [our security policy](https://github.com/octobercms/october/security/policy) on how to report security vulnerabilities. From 1e287d4391188ad58324363e82ebc95d8b675b63 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Thu, 12 Dec 2019 22:48:14 +1100 Subject: [PATCH 44/88] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 97f3c6d79..eafabf24e 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ The maintenance of October is lead by [Luke Towers](https://luketowers.ca/), alo Luke Towers
    Luke Towers
    Ben Thomson
    Ben Thomson
    - Denis Denisov
    Denis Denisov
    + Denis Denisov
    Denis Denisov
    Marc Jauvin
    Marc Jauvin
    From ed2eec6afb97141003c443586e6370d379e2dc5c Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Fri, 13 Dec 2019 21:34:59 +1100 Subject: [PATCH 45/88] Move placeholder upon mouse scroll This is one step closer to fixing the sorting issues when a scrollbar is present. It still doesn't quite fix the issue, still need to find a way to get the container dimensions to update Refs https://github.com/rainlab/pages-plugin/issues/384 Refs 1d91c221b0b32592ec93c9f4824d108fc2a5cc5e --- modules/backend/assets/js/october.treeview.js | 97 +++++++++++++------ 1 file changed, 65 insertions(+), 32 deletions(-) diff --git a/modules/backend/assets/js/october.treeview.js b/modules/backend/assets/js/october.treeview.js index 1a22d60b9..278394646 100644 --- a/modules/backend/assets/js/october.treeview.js +++ b/modules/backend/assets/js/october.treeview.js @@ -4,13 +4,13 @@ * Data attributes: * - data-group-status-handler - AJAX handler to execute when an item is collapsed or expanded by a user * - data-reorder-handler - AJAX handler to execute when items are reordered - * + * * Events * - open.oc.treeview - this event is triggered on the list element when an item is clicked. - * + * * Dependences: * - Tree list (october.treelist.js) - * + * */ +function ($) { "use strict"; var Base = $.oc.foundation.base, @@ -65,8 +65,11 @@ * Mark previously active item, if it was set */ var dataId = this.$el.data('oc.active-item') - if (dataId !== undefined) + if (dataId !== undefined) { this.markActive(dataId) + } + + this.$scrollbar.on('oc.scrollEnd', this.proxy(this.onScroll)) } TreeView.prototype.dispose = function() { @@ -83,6 +86,7 @@ } TreeView.prototype.unregisterHandlers = function() { + this.$scrollbar.on('oc.scrollEnd', this.proxy(this.onScroll)) this.$el.off('.treeview') this.$el.off('move.oc.treelist', this.proxy(this.onNodeMove)) this.$el.off('aftermove.oc.treelist', this.proxy(this.onAfterNodeMove)) @@ -163,9 +167,9 @@ TreeView.prototype.toggleGroup = function(group) { var $group = $(group); - $group.attr('data-status') == 'expanded' ? - this.collapseGroup($group) : - this.expandGroup($group) + $group.attr('data-status') == 'expanded' + ? this.collapseGroup($group) + : this.expandGroup($group) } TreeView.prototype.sendGroupStatusRequest = function($group, status) { @@ -229,7 +233,7 @@ } // It seems the method is not used anymore as we re-create the control - // instead of updating it. Remove later if nothing weird is noticed. + // instead of updating it. Remove later if nothing weird is noticed. // -ab Apr 26 2015 // TreeView.prototype.update = function() { @@ -238,8 +242,9 @@ //this.initSortable() var dataId = this.$el.data('oc.active-item') - if (dataId !== undefined) + if (dataId !== undefined) { this.markActive(dataId) + } } TreeView.prototype.handleMovedNode = function() { @@ -250,11 +255,13 @@ } TreeView.prototype.tweakCursorAdjustment = function(adjustment) { - if (!adjustment) + if (!adjustment) { return adjustment + } - if (this.$scrollbar.length > 0) + if (this.$scrollbar.length > 0) { adjustment.top -= this.$scrollbar.scrollTop() + } return adjustment } @@ -297,46 +304,71 @@ this.toggleGroup($(ev.currentTarget).closest('li')) return false } - + // TREEVIEW SCROLL ON DRAG // ============================ - + + TreeView.prototype.onScroll = function () { + if (!$('body').hasClass('dragging')) { + return + } + + var changed = this.lastScrollPos - this.$scrollbar.scrollTop() + + this.$el.children('ol').each(function() { + var sortable = $(this).data('oc.sortable') + + sortable.refresh() + sortable.cursorAdjustment.top += changed // Keep cursor adjustment in sync with scroll + }); + + this.dragCallback() + + this.lastScrollPos = this.$scrollbar.scrollTop() + } + TreeView.prototype.onDrag = function ($item, position, _super, event) { - + this.lastScrollPos = this.$scrollbar.scrollTop() + this.dragCallback = function() { _super($item, position, null, event) }; - + this.clearScrollTimeout() this.dragCallback() - + if (!this.$scrollbar || this.$scrollbar.length === 0) return - - if (position.top < 0) + + if (position.top < 0) { this.scrollOffset = -10 + Math.floor(position.top / 5) - else if (position.top > this.$scrollbar.height()) + } + else if (position.top > this.$scrollbar.height()) { this.scrollOffset = 10 + Math.ceil((position.top - this.$scrollbar.height()) / 5) - else + } + else { return - - this.scrollMax = function() { - return this.$el.height() - this.$scrollbar.height() - }; - + } + this.dragScroll() } - + + TreeView.prototype.scrollMax = function() { + return this.$el.height() - this.$scrollbar.height() + } + TreeView.prototype.dragScroll = function() { var startScrollTop = this.$scrollbar.scrollTop() var changed this.scrollTimeout = null - this.$scrollbar.scrollTop( Math.min(startScrollTop + this.scrollOffset, this.scrollMax()) ) + this.$scrollbar.scrollTop(Math.min(startScrollTop + this.scrollOffset, this.scrollMax())) + changed = this.$scrollbar.scrollTop() - startScrollTop - if (changed === 0) + if (changed === 0) { return + } this.$el.children('ol').each(function() { var sortable = $(this).data('oc.sortable') @@ -346,11 +378,12 @@ }); this.dragCallback() + this.$scrollbar.data('oc.scrollbar').setThumbPosition() // Update scrollbar position - + this.scrollTimeout = window.setTimeout(this.proxy(this.dragScroll), 100) } - + TreeView.prototype.clearScrollTimeout = function() { if (this.scrollTimeout) { window.clearTimeout(this.scrollTimeout) @@ -372,7 +405,7 @@ var options = $.extend({}, TreeView.DEFAULTS, $this.data(), typeof option == 'object' && option) if (!data) $this.data('oc.treeView', (data = new TreeView(this, options))) - if (typeof option == 'string' && data) { + if (typeof option == 'string' && data) { var methodArgs = []; for (var i=1; i Date: Fri, 13 Dec 2019 21:44:37 +1100 Subject: [PATCH 46/88] Remove GPU enablement It is unsure why this was ever needed, but it appears to fix the overflow issues with the sortable plugin Refs https://github.com/rainlab/pages-plugin/issues/384 Refs 11be3fede39024f285309f61cbf32ee4d0d5cc28 --- modules/backend/assets/css/october.css | 3 +-- modules/backend/assets/js/october.treeview.js | 2 -- modules/backend/assets/less/controls/scrollbar.less | 7 +------ 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/modules/backend/assets/css/october.css b/modules/backend/assets/css/october.css index fb572f7c5..3ee63a995 100644 --- a/modules/backend/assets/css/october.css +++ b/modules/backend/assets/css/october.css @@ -115,8 +115,7 @@ .control-simplelist.is-selectable-box li a:hover .image >i {color:rgba(0,0,0,0.45)} .list-preview .control-simplelist.is-selectable ul {margin-bottom:0} .drag-noselect {-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} -.control-scrollbar {position:relative;overflow:hidden;height:100%;-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0)} -.control-scrollbar >div {-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0)} +.control-scrollbar {position:relative;overflow:hidden;height:100%} .control-scrollbar >.scrollbar-scrollbar {position:absolute;z-index:100} .control-scrollbar >.scrollbar-scrollbar .scrollbar-track {background-color:transparent;position:relative;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px} .control-scrollbar >.scrollbar-scrollbar .scrollbar-track .scrollbar-thumb {background-color:rgba(0,0,0,0.35);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;cursor:pointer;overflow:hidden;position:absolute} diff --git a/modules/backend/assets/js/october.treeview.js b/modules/backend/assets/js/october.treeview.js index 278394646..15bf14cc7 100644 --- a/modules/backend/assets/js/october.treeview.js +++ b/modules/backend/assets/js/october.treeview.js @@ -317,7 +317,6 @@ this.$el.children('ol').each(function() { var sortable = $(this).data('oc.sortable') - sortable.refresh() sortable.cursorAdjustment.top += changed // Keep cursor adjustment in sync with scroll }); @@ -372,7 +371,6 @@ this.$el.children('ol').each(function() { var sortable = $(this).data('oc.sortable') - sortable.refresh() sortable.cursorAdjustment.top -= changed // Keep cursor adjustment in sync with scroll }); diff --git a/modules/backend/assets/less/controls/scrollbar.less b/modules/backend/assets/less/controls/scrollbar.less index 92f6bd444..d4966e5d7 100644 --- a/modules/backend/assets/less/controls/scrollbar.less +++ b/modules/backend/assets/less/controls/scrollbar.less @@ -12,11 +12,6 @@ position: relative; overflow: hidden; height: 100%; - .transform( ~'translateZ(0)'); - - > div { - .transform( ~'translateZ(0)'); - } >.scrollbar-scrollbar { position: absolute; @@ -127,4 +122,4 @@ html.mobile { } } } -} \ No newline at end of file +} From 6277f9b44c8b6be168fe0a380947d8b6c19aa259 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Fri, 13 Dec 2019 21:56:39 +1100 Subject: [PATCH 47/88] Event goes off not on --- modules/backend/assets/js/october.treeview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/backend/assets/js/october.treeview.js b/modules/backend/assets/js/october.treeview.js index 15bf14cc7..f443429c1 100644 --- a/modules/backend/assets/js/october.treeview.js +++ b/modules/backend/assets/js/october.treeview.js @@ -86,7 +86,7 @@ } TreeView.prototype.unregisterHandlers = function() { - this.$scrollbar.on('oc.scrollEnd', this.proxy(this.onScroll)) + this.$scrollbar.off('oc.scrollEnd', this.proxy(this.onScroll)) this.$el.off('.treeview') this.$el.off('move.oc.treelist', this.proxy(this.onNodeMove)) this.$el.off('aftermove.oc.treelist', this.proxy(this.onAfterNodeMove)) From 80f870c3136023a05585ec660030ef3cff89cee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20K=C3=BCndig?= Date: Sat, 14 Dec 2019 02:22:30 +0100 Subject: [PATCH 48/88] Allow partial overrides in subfolders (#4652) * Allow partial overrides in subfolders + security checker --- modules/cms/classes/ComponentPartial.php | 33 +++++++++++++++++++----- modules/cms/classes/Controller.php | 14 +++++++--- modules/cms/helpers/File.php | 23 +++++++++++++++++ 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/modules/cms/classes/ComponentPartial.php b/modules/cms/classes/ComponentPartial.php index 68a444b2c..1b8f74a78 100644 --- a/modules/cms/classes/ComponentPartial.php +++ b/modules/cms/classes/ComponentPartial.php @@ -107,6 +107,10 @@ class ComponentPartial extends Extendable implements CmsObjectContract $partial = Partial::loadCached($theme, $component->alias . '/' . $fileName); } + if ($partial !== null) { + static::ensureValidPartialPath($partial->getFileName(), $partial->getFilePath(), null); + } + return $partial; } @@ -160,19 +164,36 @@ class ComponentPartial extends Extendable implements CmsObjectContract */ protected function validateFileName($fileName) { - if (!FileHelper::validatePath($fileName, $this->maxNesting)) { - throw new ApplicationException(Lang::get('cms::lang.cms_object.invalid_file', [ - 'name' => $fileName - ])); - } - if (!strlen(File::extension($fileName))) { $fileName .= '.'.$this->defaultExtension; } + static::ensureValidPartialPath($fileName, $this->getFilePath($fileName), $this->maxNesting); + return $fileName; } + /** + * Ensures that a partial path is valid and local to the application. + * + * @param string $fileName + * @param $realpath + * @param int $maxNesting + * + * @return bool + * @throws ApplicationException + */ + protected static function ensureValidPartialPath($fileName, $realpath, $maxNesting = 2) + { + if (FileHelper::validatePath($fileName, $maxNesting) && FileHelper::validateIsLocalFile($realpath)) { + return true; + } + + throw new ApplicationException(Lang::get('cms::lang.cms_object.invalid_file', [ + 'name' => $fileName, + ])); + } + /** * Returns the file content. * @return string diff --git a/modules/cms/classes/Controller.php b/modules/cms/classes/Controller.php index ecb60c448..d02ad33a5 100644 --- a/modules/cms/classes/Controller.php +++ b/modules/cms/classes/Controller.php @@ -24,6 +24,7 @@ use System\Classes\CombineAssets; use System\Twig\Extension as SystemTwigExtension; use October\Rain\Exception\AjaxException; use October\Rain\Exception\ValidationException; +use October\Rain\Exception\ApplicationException; use October\Rain\Parse\Bracket as TextParser; use Illuminate\Http\RedirectResponse; @@ -996,15 +997,20 @@ class Controller /* * Check if the theme has an override */ - if (strpos($partialName, '/') === false) { - $partial = ComponentPartial::loadOverrideCached($this->theme, $componentObj, $partialName); - } + $partial = ComponentPartial::loadOverrideCached($this->theme, $componentObj, $partialName); /* * Check the component partial */ if ($partial === null) { - $partial = ComponentPartial::loadCached($componentObj, $partialName); + try { + $partial = ComponentPartial::loadCached($componentObj, $partialName); + } catch (ApplicationException $e) { + if ($throwException) { + throw $e; + } + return false; + } } if ($partial === null) { diff --git a/modules/cms/helpers/File.php b/modules/cms/helpers/File.php index aa10b35d7..83c39a85f 100644 --- a/modules/cms/helpers/File.php +++ b/modules/cms/helpers/File.php @@ -1,5 +1,8 @@ Date: Sat, 14 Dec 2019 12:37:44 +1100 Subject: [PATCH 49/88] Simplify security check Logic in ComponentPartial was rolled back and moved to the Controller. Since there are issues with throwing exceptions inside the component partial lookup logic (exceptions are conditionally suppressed), it seems like it would be better to bubble up the security logic to the controller level as a simple base dir security check, which is no longer concerned about any suppression logic. This looks to have logic parity with the previous solution Refs #4652 --- modules/cms/classes/ComponentPartial.php | 33 +++++------------------- modules/cms/classes/Controller.php | 18 +++++++------ 2 files changed, 16 insertions(+), 35 deletions(-) diff --git a/modules/cms/classes/ComponentPartial.php b/modules/cms/classes/ComponentPartial.php index 1b8f74a78..68a444b2c 100644 --- a/modules/cms/classes/ComponentPartial.php +++ b/modules/cms/classes/ComponentPartial.php @@ -107,10 +107,6 @@ class ComponentPartial extends Extendable implements CmsObjectContract $partial = Partial::loadCached($theme, $component->alias . '/' . $fileName); } - if ($partial !== null) { - static::ensureValidPartialPath($partial->getFileName(), $partial->getFilePath(), null); - } - return $partial; } @@ -164,36 +160,19 @@ class ComponentPartial extends Extendable implements CmsObjectContract */ protected function validateFileName($fileName) { + if (!FileHelper::validatePath($fileName, $this->maxNesting)) { + throw new ApplicationException(Lang::get('cms::lang.cms_object.invalid_file', [ + 'name' => $fileName + ])); + } + if (!strlen(File::extension($fileName))) { $fileName .= '.'.$this->defaultExtension; } - static::ensureValidPartialPath($fileName, $this->getFilePath($fileName), $this->maxNesting); - return $fileName; } - /** - * Ensures that a partial path is valid and local to the application. - * - * @param string $fileName - * @param $realpath - * @param int $maxNesting - * - * @return bool - * @throws ApplicationException - */ - protected static function ensureValidPartialPath($fileName, $realpath, $maxNesting = 2) - { - if (FileHelper::validatePath($fileName, $maxNesting) && FileHelper::validateIsLocalFile($realpath)) { - return true; - } - - throw new ApplicationException(Lang::get('cms::lang.cms_object.invalid_file', [ - 'name' => $fileName, - ])); - } - /** * Returns the file content. * @return string diff --git a/modules/cms/classes/Controller.php b/modules/cms/classes/Controller.php index d02ad33a5..74cb8e269 100644 --- a/modules/cms/classes/Controller.php +++ b/modules/cms/classes/Controller.php @@ -1003,14 +1003,7 @@ class Controller * Check the component partial */ if ($partial === null) { - try { - $partial = ComponentPartial::loadCached($componentObj, $partialName); - } catch (ApplicationException $e) { - if ($throwException) { - throw $e; - } - return false; - } + $partial = ComponentPartial::loadCached($componentObj, $partialName); } if ($partial === null) { @@ -1037,6 +1030,15 @@ class Controller return false; } + /* + * Security check + */ + if (!\Cms\Helpers\File::validateIsLocalFile($partial->getFilePath())) { + throw new CmsException(Lang::get('cms::lang.cms_object.invalid_file', [ + 'name' => $partial->getFileName() + ])); + } + /* * Run functions for CMS partials only (Cms\Classes\Partial) */ From 56eab502606c2c1bb2e64f08e27db1467d211b4f Mon Sep 17 00:00:00 2001 From: Ayumi <57409060+ayumi-cloud@users.noreply.github.com> Date: Sat, 14 Dec 2019 17:14:23 +0000 Subject: [PATCH 50/88] Documented session.http_only (#4743) Credit to @ayumi-cloud --- config/session.php | 13 +++++++++++++ modules/backend/classes/Controller.php | 2 ++ 2 files changed, 15 insertions(+) diff --git a/config/session.php b/config/session.php index bfb16513b..ab762f221 100644 --- a/config/session.php +++ b/config/session.php @@ -137,6 +137,19 @@ return [ 'domain' => null, + /* + |-------------------------------------------------------------------------- + | HTTP Access Only + |-------------------------------------------------------------------------- + | + | Setting this value to true will prevent JavaScript from accessing the + | value of the cookie and the cookie will only be accessible through + | the HTTP protocol. You are free to modify this option if needed. + | + */ + + 'http_only' => true, + /* |-------------------------------------------------------------------------- | HTTPS Only Cookies diff --git a/modules/backend/classes/Controller.php b/modules/backend/classes/Controller.php index 01ac494f5..c4e64b6ba 100644 --- a/modules/backend/classes/Controller.php +++ b/modules/backend/classes/Controller.php @@ -277,6 +277,7 @@ class Controller extends ControllerBase if ($ajaxResponse = $this->execAjaxHandlers()) { $result = $ajaxResponse; } + /* * Execute postback handler */ @@ -287,6 +288,7 @@ class Controller extends ControllerBase ) { $result = $handlerResponse; } + /* * Execute page action */ From e19f20bcb7ad7a28b3296bf759bddbe3323c66ae Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Fri, 13 Dec 2019 15:35:44 +1100 Subject: [PATCH 51/88] Update FUNDING.yml --- .github/FUNDING.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index bc7c4b4be..a29ace930 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,5 +1,4 @@ # These are supported funding model platforms -custom: ['https://octobercms.com/fundraising'] +custom: ['https://octobercms.com/premium-support'] open_collective: octobercms github: LukeTowers -patreon: LukeTowers From a53cc52752d642ae1e09fdb2bf76f9454369be96 Mon Sep 17 00:00:00 2001 From: Ben Thomson Date: Tue, 17 Dec 2019 22:43:44 +0800 Subject: [PATCH 52/88] Correctly display HTML entities in event log (#4566) This changes the event log to use a partial for the log message which double-encodes the data. When using formatted view in the log viewer widget, the HTML entites are allowed by decoding back a step. When in raw view, the HTML entities are kept double-encoded. Fixes #4558. --- .../system/assets/js/eventlogs/exception-beautifier.js | 9 ++++++--- modules/system/controllers/eventlogs/_field_message.htm | 1 + modules/system/models/eventlog/fields.yaml | 3 ++- 3 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 modules/system/controllers/eventlogs/_field_message.htm diff --git a/modules/system/assets/js/eventlogs/exception-beautifier.js b/modules/system/assets/js/eventlogs/exception-beautifier.js index 66b85ed4d..f8d876db7 100644 --- a/modules/system/assets/js/eventlogs/exception-beautifier.js +++ b/modules/system/assets/js/eventlogs/exception-beautifier.js @@ -40,7 +40,7 @@ } }) - markup = self.parseSource(self.$el.text()) + markup = self.parseSource(self.$el.html()) self.$el .addClass('plugin-exception-beautifier') @@ -274,7 +274,10 @@ } } else { - markup += $.oc.escapeHtmlString(str) + // Allow HTML entities + str = str.replace(/&([^\s&;]+?);/g, '&$1;') + + markup += str .replace(/\{x-newline\}/g, '
    ') .replace(/\{x-tabulation\}/g, '  ') } @@ -353,7 +356,7 @@ tabs.find('#beautifier-tab-formatted').append(markup) } - tabs.find('#beautifier-tab-raw').append('
    ' + $.oc.escapeHtmlString(source.trim()).replace(/\r\n|\r|\n/g, '
    ').replace(/ {2}/g, '  ') + '
    ') + tabs.find('#beautifier-tab-raw').append('
    ' + source.trim().replace(/\r\n|\r|\n/g, '
    ').replace(/ {2}/g, '  ') + '
    ') tabs.ocTab({ closable: false diff --git a/modules/system/controllers/eventlogs/_field_message.htm b/modules/system/controllers/eventlogs/_field_message.htm new file mode 100644 index 000000000..0176604b6 --- /dev/null +++ b/modules/system/controllers/eventlogs/_field_message.htm @@ -0,0 +1 @@ + diff --git a/modules/system/models/eventlog/fields.yaml b/modules/system/models/eventlog/fields.yaml index 44ac077bc..1a9c11ee9 100644 --- a/modules/system/models/eventlog/fields.yaml +++ b/modules/system/models/eventlog/fields.yaml @@ -5,6 +5,7 @@ fields: message: - type: textarea + type: partial + path: field_message containerAttributes: data-plugin: exception-beautifier From 3fc7f6aa7672acb381a712e83b6a2f68f28ccfca Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Tue, 17 Dec 2019 15:16:55 +0000 Subject: [PATCH 53/88] Refresh Relation Manager on Unlink and Delete (#4741) Credit to @DanHarrin. Fixes #3470 and #4718. Replaces #3476. --- modules/backend/behaviors/RelationController.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/backend/behaviors/RelationController.php b/modules/backend/behaviors/RelationController.php index deb84260f..4f72e224d 100644 --- a/modules/backend/behaviors/RelationController.php +++ b/modules/backend/behaviors/RelationController.php @@ -1187,6 +1187,9 @@ class RelationController extends ControllerBehavior $relatedModel->delete(); } + // Reinitialise the form with a blank model + $this->initRelation($this->model); + $this->viewWidget->setFormValues([]); $this->viewModel = $this->relationModel; } @@ -1281,6 +1284,12 @@ class RelationController extends ControllerBehavior if ($this->relationType == 'belongsTo') { $this->relationObject->dissociate(); $this->relationObject->getParent()->save(); + + // If the relation manager isn't using deferred binding, reinitialise the form with a blank model + if (is_null($sessionKey)) { + $this->model->refresh(); + $this->initRelation($this->model); + } } elseif ($this->relationType == 'hasOne' || $this->relationType == 'morphOne') { if ($obj = $relatedModel->find($recordId)) { From 25fb9ce1c05d6d7016bd9adc45ae41528cdff965 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Wed, 18 Dec 2019 07:37:35 +1100 Subject: [PATCH 54/88] Broken link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eafabf24e..6683efb96 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ You may also watch these introductory videos for [beginners](https://vimeo.com/7 ## Development Team -October was created by [Alexey Bobkov](http://ca.linkedin.com/pub/aleksey-bobkov/2b/ba0/232) and [Samuel Georges](https://www.linkedin.com/in/samuel-georges-0a964131/), who both continue to develop the platform. +October was created by [Alexey Bobkov](https://www.linkedin.com/in/alexey-bobkov-232ba02b/) and [Samuel Georges](https://www.linkedin.com/in/samuel-georges-0a964131/), who both continue to develop the platform. The maintenance of October is lead by [Luke Towers](https://luketowers.ca/), along with many wonderful people that dedicate their time to help support and grow the community. From 8303e0dbb2aea4d7f3d01a0520322335480a5bdc Mon Sep 17 00:00:00 2001 From: Nick Khaetsky Date: Wed, 18 Dec 2019 19:16:36 +0300 Subject: [PATCH 55/88] Added additional robots meta fields to CMS pages (#4685) Credit to @FlusherDock1 --- modules/cms/classes/Page.php | 16 ++++++++++++++++ modules/cms/classes/page/fields.yaml | 25 +++++++++++++++++++++++++ modules/cms/lang/en/lang.php | 4 ++++ modules/cms/lang/ru/lang.php | 4 ++++ 4 files changed, 49 insertions(+) diff --git a/modules/cms/classes/Page.php b/modules/cms/classes/Page.php index f58d15af1..648baf958 100644 --- a/modules/cms/classes/Page.php +++ b/modules/cms/classes/Page.php @@ -28,6 +28,9 @@ class Page extends CmsCompoundObject 'is_hidden', 'meta_title', 'meta_description', + 'meta_robots_noindex', + 'meta_robots_nofollow', + 'meta_robots_noarchive', 'markup', 'settings', 'code' @@ -112,6 +115,19 @@ class Page extends CmsCompoundObject return $result; } + /** + * Helper that returns formatted robots tags. + * @return string + */ + public function getMetaRobotsAttribute() + { + $options = []; + $options[] = $this->meta_robots_noindex ? 'noindex' : 'index'; + $options[] = $this->meta_robots_nofollow ? 'nofollow' : 'follow'; + $options[] = $this->meta_robots_noarchive ? 'noarchive' : 'archive'; + return implode(',', $options); + } + /** * Helper that makes a URL for a page in the active theme. * @param mixed $page Specifies the Cms Page file name. diff --git a/modules/cms/classes/page/fields.yaml b/modules/cms/classes/page/fields.yaml index ac754d31e..ab29e4b4b 100644 --- a/modules/cms/classes/page/fields.yaml +++ b/modules/cms/classes/page/fields.yaml @@ -59,6 +59,31 @@ tabs: type: textarea size: tiny + settings[meta_robots_noindex]: + tab: cms::lang.editor.meta + label: cms::lang.editor.meta_robots_noindex + on: backend::lang.list.column_switch_true + off: backend::lang.list.column_switch_false + span: auto + type: switch + comment: cms::lang.editor.meta_robots_noindex_comment + + settings[meta_robots_noarchive]: + tab: cms::lang.editor.meta + label: cms::lang.editor.meta_robots_noarchive + on: backend::lang.list.column_switch_true + off: backend::lang.list.column_switch_false + span: auto + type: switch + + settings[meta_robots_nofollow]: + tab: cms::lang.editor.meta + label: cms::lang.editor.meta_robots_nofollow + on: backend::lang.list.column_switch_true + off: backend::lang.list.column_switch_false + span: auto + type: switch + settings[is_hidden]: tab: cms::lang.editor.settings label: cms::lang.editor.hidden diff --git a/modules/cms/lang/en/lang.php b/modules/cms/lang/en/lang.php index d3e28b27e..bfacd56b6 100644 --- a/modules/cms/lang/en/lang.php +++ b/modules/cms/lang/en/lang.php @@ -175,6 +175,10 @@ return [ 'meta' => 'Meta', 'meta_title' => 'Meta Title', 'meta_description' => 'Meta Description', + 'meta_robots_noindex' => 'Prevent search engines from indexing this page?', + 'meta_robots_noindex_comment' => 'Regular users will still be able to visit a page hidden from search engines.', + 'meta_robots_nofollow' => 'Prevent search engines from following the links on this page?', + 'meta_robots_noarchive' => 'Prevent search engines from archiving the page?', 'markup' => 'Markup', 'code' => 'Code', 'content' => 'Content', diff --git a/modules/cms/lang/ru/lang.php b/modules/cms/lang/ru/lang.php index 6d2b99fd4..0513e969b 100644 --- a/modules/cms/lang/ru/lang.php +++ b/modules/cms/lang/ru/lang.php @@ -175,6 +175,10 @@ return [ 'meta' => 'Метатеги', 'meta_title' => 'Заголовок (meta title)', 'meta_description' => 'Описание (meta description)', + 'meta_robots_noindex' => 'Запретить поисковым системам индексировать эту страницу?', + 'meta_robots_noindex_comment' => 'Обычные пользователи по-прежнему смогут посещать страницы, скрытые от поисковых систем.', + 'meta_robots_nofollow' => 'Запретить поисковым системам следовать по ссылкам на этой странице?', + 'meta_robots_noarchive' => 'Запретить поисковым системам архивировать эту страницу?', 'markup' => 'Разметка', 'code' => 'Код', 'content' => 'Содержание', From a51215b9b30c8d1eda7336947d460fe745c622a5 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 18 Dec 2019 13:51:50 -0600 Subject: [PATCH 56/88] Revert "Added additional robots meta fields to CMS pages (#4685)" (#4832) This reverts commit 8303e0dbb2aea4d7f3d01a0520322335480a5bdc. Reverts #4685. Should be implemented as a plugin instead. --- modules/cms/classes/Page.php | 16 ---------------- modules/cms/classes/page/fields.yaml | 25 ------------------------- modules/cms/lang/en/lang.php | 4 ---- modules/cms/lang/ru/lang.php | 4 ---- 4 files changed, 49 deletions(-) diff --git a/modules/cms/classes/Page.php b/modules/cms/classes/Page.php index 648baf958..f58d15af1 100644 --- a/modules/cms/classes/Page.php +++ b/modules/cms/classes/Page.php @@ -28,9 +28,6 @@ class Page extends CmsCompoundObject 'is_hidden', 'meta_title', 'meta_description', - 'meta_robots_noindex', - 'meta_robots_nofollow', - 'meta_robots_noarchive', 'markup', 'settings', 'code' @@ -115,19 +112,6 @@ class Page extends CmsCompoundObject return $result; } - /** - * Helper that returns formatted robots tags. - * @return string - */ - public function getMetaRobotsAttribute() - { - $options = []; - $options[] = $this->meta_robots_noindex ? 'noindex' : 'index'; - $options[] = $this->meta_robots_nofollow ? 'nofollow' : 'follow'; - $options[] = $this->meta_robots_noarchive ? 'noarchive' : 'archive'; - return implode(',', $options); - } - /** * Helper that makes a URL for a page in the active theme. * @param mixed $page Specifies the Cms Page file name. diff --git a/modules/cms/classes/page/fields.yaml b/modules/cms/classes/page/fields.yaml index ab29e4b4b..ac754d31e 100644 --- a/modules/cms/classes/page/fields.yaml +++ b/modules/cms/classes/page/fields.yaml @@ -59,31 +59,6 @@ tabs: type: textarea size: tiny - settings[meta_robots_noindex]: - tab: cms::lang.editor.meta - label: cms::lang.editor.meta_robots_noindex - on: backend::lang.list.column_switch_true - off: backend::lang.list.column_switch_false - span: auto - type: switch - comment: cms::lang.editor.meta_robots_noindex_comment - - settings[meta_robots_noarchive]: - tab: cms::lang.editor.meta - label: cms::lang.editor.meta_robots_noarchive - on: backend::lang.list.column_switch_true - off: backend::lang.list.column_switch_false - span: auto - type: switch - - settings[meta_robots_nofollow]: - tab: cms::lang.editor.meta - label: cms::lang.editor.meta_robots_nofollow - on: backend::lang.list.column_switch_true - off: backend::lang.list.column_switch_false - span: auto - type: switch - settings[is_hidden]: tab: cms::lang.editor.settings label: cms::lang.editor.hidden diff --git a/modules/cms/lang/en/lang.php b/modules/cms/lang/en/lang.php index bfacd56b6..d3e28b27e 100644 --- a/modules/cms/lang/en/lang.php +++ b/modules/cms/lang/en/lang.php @@ -175,10 +175,6 @@ return [ 'meta' => 'Meta', 'meta_title' => 'Meta Title', 'meta_description' => 'Meta Description', - 'meta_robots_noindex' => 'Prevent search engines from indexing this page?', - 'meta_robots_noindex_comment' => 'Regular users will still be able to visit a page hidden from search engines.', - 'meta_robots_nofollow' => 'Prevent search engines from following the links on this page?', - 'meta_robots_noarchive' => 'Prevent search engines from archiving the page?', 'markup' => 'Markup', 'code' => 'Code', 'content' => 'Content', diff --git a/modules/cms/lang/ru/lang.php b/modules/cms/lang/ru/lang.php index 0513e969b..6d2b99fd4 100644 --- a/modules/cms/lang/ru/lang.php +++ b/modules/cms/lang/ru/lang.php @@ -175,10 +175,6 @@ return [ 'meta' => 'Метатеги', 'meta_title' => 'Заголовок (meta title)', 'meta_description' => 'Описание (meta description)', - 'meta_robots_noindex' => 'Запретить поисковым системам индексировать эту страницу?', - 'meta_robots_noindex_comment' => 'Обычные пользователи по-прежнему смогут посещать страницы, скрытые от поисковых систем.', - 'meta_robots_nofollow' => 'Запретить поисковым системам следовать по ссылкам на этой странице?', - 'meta_robots_noarchive' => 'Запретить поисковым системам архивировать эту страницу?', 'markup' => 'Разметка', 'code' => 'Код', 'content' => 'Содержание', From 9bfdf6236b6c3f7b298fb3bf378d2a64bdd583e3 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 18 Dec 2019 16:29:57 -0600 Subject: [PATCH 57/88] Bump minimum version of Laravel required Laravel made a breaking change to the EncryptCookies middleware, this makes it explicit that we require at least 5.5.40. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 0a7777605..b638da9d7 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "october/system": "~1.0", "october/backend": "~1.0", "october/cms": "~1.0", - "laravel/framework": "5.5.*", + "laravel/framework": "^5.5.40", "wikimedia/composer-merge-plugin": "dev-master" }, "require-dev": { From cbc620c3e8e28b9f71362dc456748d568df4c771 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Thu, 19 Dec 2019 17:42:25 +1100 Subject: [PATCH 58/88] Rollback for Build 462 This change should be revisited since it doesn't account for database-based templates which have no file path. Upon revisit, we might want to consider adding this logic in to the afterFetch() event with detection of file based mode, or even at the lower levels where the file is first extracted from the filesystem. TBA --- modules/cms/classes/Controller.php | 9 --------- modules/cms/helpers/File.php | 20 -------------------- 2 files changed, 29 deletions(-) diff --git a/modules/cms/classes/Controller.php b/modules/cms/classes/Controller.php index 74cb8e269..77237ed13 100644 --- a/modules/cms/classes/Controller.php +++ b/modules/cms/classes/Controller.php @@ -1030,15 +1030,6 @@ class Controller return false; } - /* - * Security check - */ - if (!\Cms\Helpers\File::validateIsLocalFile($partial->getFilePath())) { - throw new CmsException(Lang::get('cms::lang.cms_object.invalid_file', [ - 'name' => $partial->getFileName() - ])); - } - /* * Run functions for CMS partials only (Cms\Classes\Partial) */ diff --git a/modules/cms/helpers/File.php b/modules/cms/helpers/File.php index 83c39a85f..8849927e1 100644 --- a/modules/cms/helpers/File.php +++ b/modules/cms/helpers/File.php @@ -71,24 +71,4 @@ class File return true; } - - /** - * Validates a CMS object path is inside the application's base directory. - * @param string $filePath Specifies a path to validate - * @return boolean Returns true if the file path is local. Otherwise returns false. - */ - public static function validateIsLocalFile($filePath) - { - $restrictBaseDir = Config::get('cms.restrictBaseDir', true); - - if ($restrictBaseDir && !Filesystem::isLocalPath($filePath)) { - return false; - } - - if (!$restrictBaseDir && realpath($filePath) === false) { - return false; - } - - return true; - } } From 21f8c5f272e0ab9a7acbccaf08feeb9e707d494a Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Sat, 21 Dec 2019 13:46:06 +1100 Subject: [PATCH 59/88] Fixes styling issue when quick select is forced Replaces #4827 --- modules/system/assets/ui/less/form.less | 9 ++++----- modules/system/assets/ui/storm.css | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/modules/system/assets/ui/less/form.less b/modules/system/assets/ui/less/form.less index 075b2fc0a..3ec8b9309 100644 --- a/modules/system/assets/ui/less/form.less +++ b/modules/system/assets/ui/less/form.less @@ -329,13 +329,15 @@ } .field-checkboxlist { - &:not(.is-scrollable) { + .field-checkboxlist-inner { .border-radius(@border-radius-base); background: @color-form-checkboxlist-background; border: 1px solid @color-form-checkboxlist-border; + } + &:not(.is-scrollable) { .field-checkboxlist-inner { - padding: 20px 20px 2px 20px; + padding: 15px 15px 2px 15px; } } @@ -368,10 +370,7 @@ } } - .field-checkboxlist-scrollable { - background: @color-form-checkboxlist-background; - border: 1px solid @color-form-checkboxlist-border; padding-left: 15px; height: @size-large + 100; diff --git a/modules/system/assets/ui/storm.css b/modules/system/assets/ui/storm.css index 78c309784..6a758570e 100644 --- a/modules/system/assets/ui/storm.css +++ b/modules/system/assets/ui/storm.css @@ -4242,14 +4242,14 @@ html.cssanimations .cursor-loading-indicator.hide {display:none} .field-textarea.size-large {min-height:200px} .field-textarea.size-huge {min-height:250px} .field-textarea.size-giant {min-height:350px} -.field-checkboxlist:not(.is-scrollable) {-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;background:#fff;border:1px solid #e2e2e2} -.field-checkboxlist:not(.is-scrollable) .field-checkboxlist-inner {padding:20px 20px 2px 20px} +.field-checkboxlist .field-checkboxlist-inner {-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;background:#fff;border:1px solid #e2e2e2} +.field-checkboxlist:not(.is-scrollable) .field-checkboxlist-inner {padding:15px 15px 2px 15px} .field-checkboxlist .checkboxlist-controls {display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:baseline;-ms-flex-align:baseline;align-items:baseline} .field-checkboxlist .checkboxlist-controls >div {padding-bottom:7px} .field-checkboxlist .checkboxlist-controls >div >a {font-size:13px;margin-right:20px;text-decoration:none} .field-checkboxlist .checkboxlist-controls >div >a >i {color:#999;margin:0 4px} .field-checkboxlist .checkboxlist-controls >div >a:hover >i {color:#2a3e51} -.field-checkboxlist-scrollable {background:#fff;border:1px solid #e2e2e2;padding-left:15px;height:300px} +.field-checkboxlist-scrollable {padding-left:15px;height:300px} .field-checkboxlist-scrollable .checkbox {margin-top:15px;margin-bottom:5px} .field-checkboxlist-scrollable .checkbox ~ .checkbox {margin-top:0} .field-recordfinder {background-color:#fff;border:1px solid #d1d6d9;overflow:hidden;position:relative;-webkit-box-shadow:inset 0 1px 0 rgba(209,214,217,0.25),0 1px 0 rgba(255,255,255,.5);box-shadow:inset 0 1px 0 rgba(209,214,217,0.25),0 1px 0 rgba(255,255,255,.5);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px} From adb303a53c5eabff0a41519f3b52638166decd99 Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Sat, 21 Dec 2019 20:50:28 +1100 Subject: [PATCH 60/88] Always sort plugins by key, then dependencies This has been benchmarked and appears to have minimal impact on performance and solves unnecessary randomness and race conditions during the app's registration and boot cycle Fixes #4826 --- modules/system/classes/PluginManager.php | 83 +++++++++++++++--------- modules/system/classes/UpdateManager.php | 6 +- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/modules/system/classes/PluginManager.php b/modules/system/classes/PluginManager.php index a92ea6490..0985f2c32 100644 --- a/modules/system/classes/PluginManager.php +++ b/modules/system/classes/PluginManager.php @@ -11,7 +11,7 @@ use Config; use Schema; use RecursiveIteratorIterator; use RecursiveDirectoryIterator; -use ApplicationException; +use SystemException; /** * Plugin manager @@ -108,6 +108,8 @@ class PluginManager $this->loadPlugin($namespace, $path); } + $this->sortDependencies(); + return $this->plugins; } @@ -674,43 +676,25 @@ class PluginManager } } - /** - * Returns the plugin identifiers that are required by the supplied plugin. - * @param string $plugin Plugin identifier, object or class - * @return array - */ - public function getDependencies($plugin) - { - if (is_string($plugin) && (!$plugin = $this->findByIdentifier($plugin))) { - return false; - } - - if (!isset($plugin->require) || !$plugin->require) { - return null; - } - - return is_array($plugin->require) ? $plugin->require : [$plugin->require]; - } - /** * Sorts a collection of plugins, in the order that they should be actioned, * according to their given dependencies. Least dependent come first. - * @param array $plugins Object collection to sort, or null to sort all. * @return array Collection of sorted plugin identifiers */ - public function sortByDependencies($plugins = null) + protected function sortDependencies() { - if (!is_array($plugins)) { - $plugins = $this->getPlugins(); - } + ksort($this->plugins); + /* + * Canvas the dependency tree + */ + $checklist = $this->plugins; $result = []; - $checklist = $plugins; $loopCount = 0; while (count($checklist)) { - if (++$loopCount > 999) { - throw new ApplicationException('Too much recursion'); + if (++$loopCount > 2048) { + throw new SystemException('Too much recursion! Check for circular dependencies in your plugins.'); } foreach ($checklist as $code => $plugin) { @@ -718,8 +702,8 @@ class PluginManager * Get dependencies and remove any aliens */ $depends = $this->getDependencies($plugin) ?: []; - $depends = array_filter($depends, function ($pluginCode) use ($plugins) { - return isset($plugins[$pluginCode]); + $depends = array_filter($depends, function ($pluginCode) { + return isset($this->plugins[$pluginCode]); }); /* @@ -746,7 +730,46 @@ class PluginManager unset($checklist[$code]); } } - return $result; + + /* + * Reassemble plugin map + */ + $sortedPlugins = []; + + foreach ($result as $code) { + $sortedPlugins[$code] = $this->plugins[$code]; + } + + return $this->plugins = $sortedPlugins; + } + + /** + * Returns the plugin identifiers that are required by the supplied plugin. + * @param string $plugin Plugin identifier, object or class + * @return array + */ + public function getDependencies($plugin) + { + if (is_string($plugin) && (!$plugin = $this->findByIdentifier($plugin))) { + return false; + } + + if (!isset($plugin->require) || !$plugin->require) { + return null; + } + + return is_array($plugin->require) ? $plugin->require : [$plugin->require]; + } + + /** + * @deprecated Plugins are now sorted by default. See getPlugins() + * Remove if year >= 2022 + */ + public function sortByDependencies($plugins = null) + { + traceLog('PluginManager::sortByDependencies is deprecated. Plugins are now sorted by default. Use PluginManager::getPlugins()'); + + return array_keys($plugins ?: $this->getPlugins()); } // diff --git a/modules/system/classes/UpdateManager.php b/modules/system/classes/UpdateManager.php index 0f8264199..53ba96468 100644 --- a/modules/system/classes/UpdateManager.php +++ b/modules/system/classes/UpdateManager.php @@ -149,9 +149,9 @@ class UpdateManager /* * Update plugins */ - $plugins = $this->pluginManager->sortByDependencies(); - foreach ($plugins as $plugin) { - $this->updatePlugin($plugin); + $plugins = $this->pluginManager->getPlugins(); + foreach ($plugins as $code => $plugin) { + $this->updatePlugin($code); } Parameter::set('system::update.count', 0); From 3a49b5fa7abe11e0b9762b0ab45c638ee30ae248 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 21 Dec 2019 23:24:42 -0500 Subject: [PATCH 61/88] allow loading of lazy tabs for secondary tabs as well (#4839) --- modules/backend/widgets/Form.php | 3 ++- modules/backend/widgets/form/assets/js/october.form.js | 1 + modules/backend/widgets/form/partials/_form_tabs.htm | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/backend/widgets/Form.php b/modules/backend/widgets/Form.php index efda2e017..e7028c501 100644 --- a/modules/backend/widgets/Form.php +++ b/modules/backend/widgets/Form.php @@ -465,8 +465,9 @@ class Form extends WidgetBase { $target = post('target'); $tabName = post('name'); + $tabSection = post('section'); - $fields = array_get(optional($this->getTab('primary'))->fields, $tabName); + $fields = array_get(optional($this->getTab($tabSection))->fields, $tabName); return [ $target => $this->makePartial('form_fields', ['fields' => $fields]), diff --git a/modules/backend/widgets/form/assets/js/october.form.js b/modules/backend/widgets/form/assets/js/october.form.js index 7b82f5759..9ad3801b6 100644 --- a/modules/backend/widgets/form/assets/js/october.form.js +++ b/modules/backend/widgets/form/assets/js/october.form.js @@ -172,6 +172,7 @@ data: { target: $el.data('target'), name: $el.data('tab-name'), + section: $el.data('tab-section'), }, success: function(data) { this.success(data) diff --git a/modules/backend/widgets/form/partials/_form_tabs.htm b/modules/backend/widgets/form/partials/_form_tabs.htm index 83d1fa25f..6f1adbe66 100644 --- a/modules/backend/widgets/form/partials/_form_tabs.htm +++ b/modules/backend/widgets/form/partials/_form_tabs.htm @@ -18,7 +18,7 @@ $lazy = in_array($name, $tabs->lazy); ?>
  • - + getIcon($name)): ?> From 953061797d44c78f6d0d976bb86a595503b6b14e Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Sun, 22 Dec 2019 16:36:04 +1100 Subject: [PATCH 62/88] Remove hard coded widget name for lazy tabs (#4839) - The widget alias may not always be form, so pass the handler name - No need to spam non-lazy tabs with useless data tags --- .../widgets/form/assets/js/october.form.js | 6 ++++-- .../backend/widgets/form/partials/_form_tabs.htm | 16 +++++++++++----- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/modules/backend/widgets/form/assets/js/october.form.js b/modules/backend/widgets/form/assets/js/october.form.js index 9ad3801b6..932b8bebf 100644 --- a/modules/backend/widgets/form/assets/js/october.form.js +++ b/modules/backend/widgets/form/assets/js/october.form.js @@ -167,8 +167,10 @@ */ FormWidget.prototype.bindLazyTabs = function() { this.$el.on('click', '.tab-lazy [data-toggle="tab"]', function() { - var $el = $(this) - $.request('form::onLazyLoadTab', { + var $el = $(this), + handlerName = $el.data('tab-lazy-handler') + + $.request(handlerName, { data: { target: $el.data('target'), name: $el.data('tab-name'), diff --git a/modules/backend/widgets/form/partials/_form_tabs.htm b/modules/backend/widgets/form/partials/_form_tabs.htm index 6f1adbe66..e63f78358 100644 --- a/modules/backend/widgets/form/partials/_form_tabs.htm +++ b/modules/backend/widgets/form/partials/_form_tabs.htm @@ -13,12 +13,18 @@ ?>