Merge branch 'develop' into wip/laravel-6
This commit is contained in:
commit
252c9b7f58
|
|
@ -4,6 +4,7 @@ use App;
|
|||
use Backend;
|
||||
use BackendMenu;
|
||||
use BackendAuth;
|
||||
use Backend\Models\UserRole;
|
||||
use Backend\Classes\WidgetManager;
|
||||
use System\Classes\MailManager;
|
||||
use System\Classes\CombineAssets;
|
||||
|
|
@ -168,7 +169,12 @@ class ServiceProvider extends ModuleServiceProvider
|
|||
'media.manage_media' => [
|
||||
'label' => 'backend::lang.permissions.manage_media',
|
||||
'tab' => 'system::lang.permissions.name',
|
||||
]
|
||||
],
|
||||
'backend.allow_unsafe_markdown' => [
|
||||
'label' => 'backend::lang.permissions.allow_unsafe_markdown',
|
||||
'tab' => 'system::lang.permissions.name',
|
||||
'roles' => UserRole::CODE_DEVELOPER,
|
||||
],
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="redirect:0"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
|
|
|
|||
|
|
@ -19,14 +19,16 @@
|
|||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.creating')) ?>"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.create')) ?>
|
||||
</button>
|
||||
<button
|
||||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.creating')) ?>"
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="redirect:0"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
|
|
@ -28,6 +29,7 @@
|
|||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
|
|
|
|||
|
|
@ -19,14 +19,16 @@
|
|||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.creating')) ?>"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.create')) ?>
|
||||
</button>
|
||||
<button
|
||||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.creating')) ?>"
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="redirect:0"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
|
|
@ -28,6 +29,7 @@
|
|||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.creating')) ?>"
|
||||
class="btn btn-primary">
|
||||
|
|
@ -28,6 +29,7 @@
|
|||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.creating')) ?>"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="redirect:0"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
|
|
@ -32,6 +33,7 @@
|
|||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="redirect:0"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
|
|
@ -33,6 +34,7 @@
|
|||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
<?php namespace Backend\FormWidgets;
|
||||
|
||||
use BackendAuth;
|
||||
use Html;
|
||||
use Markdown;
|
||||
use BackendAuth;
|
||||
use Backend\Classes\FormWidgetBase;
|
||||
|
||||
/**
|
||||
|
|
@ -42,12 +43,12 @@ class MarkdownEditor extends FormWidgetBase
|
|||
//
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected $defaultAlias = 'markdown';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
|
|
@ -60,7 +61,7 @@ class MarkdownEditor extends FormWidgetBase
|
|||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
|
|
@ -84,7 +85,7 @@ class MarkdownEditor extends FormWidgetBase
|
|||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function loadAssets()
|
||||
{
|
||||
|
|
@ -93,6 +94,34 @@ class MarkdownEditor extends FormWidgetBase
|
|||
$this->addJs('/modules/backend/formwidgets/codeeditor/assets/js/build-min.js', 'core');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the generated HTML should be cleaned to remove any potential XSS
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function shouldCleanHtml()
|
||||
{
|
||||
$user = BackendAuth::getUser();
|
||||
return !$user || !$user->hasAccess('backend.allow_unsafe_markdown');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getSaveValue($value)
|
||||
{
|
||||
if ($this->shouldCleanHtml()) {
|
||||
$value = Html::clean($value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler to render the markdown as HTML
|
||||
*
|
||||
* @return array ['preview' => $generatedHTML]
|
||||
*/
|
||||
public function onRefresh()
|
||||
{
|
||||
$value = post($this->getFieldName());
|
||||
|
|
@ -100,6 +129,10 @@ class MarkdownEditor extends FormWidgetBase
|
|||
? Markdown::parseSafe($value)
|
||||
: Markdown::parse($value);
|
||||
|
||||
if ($this->shouldCleanHtml()) {
|
||||
$previewHtml = Html::clean($previewHtml);
|
||||
}
|
||||
|
||||
return [
|
||||
'preview' => $previewHtml
|
||||
];
|
||||
|
|
|
|||
|
|
@ -567,6 +567,7 @@ return [
|
|||
],
|
||||
'permissions' => [
|
||||
'manage_media' => 'Upload and manage media contents - images, videos, sounds, documents',
|
||||
'allow_unsafe_markdown' => 'Use unsafe Markdown (Can include Javascript)',
|
||||
],
|
||||
'mediafinder' => [
|
||||
'label' => 'Media Finder',
|
||||
|
|
|
|||
|
|
@ -48,6 +48,10 @@ return [
|
|||
'impersonate_working' => 'Inloggen als...',
|
||||
'impersonating' => 'Inloggen als :full_name',
|
||||
'stop_impersonating' => 'Terugkeren',
|
||||
'unsuspend' => 'Blokkering opheffen',
|
||||
'unsuspend_confirm' => 'Weet je zeker dat je de blokkering voor deze gebruiker wilt opheffen?',
|
||||
'unsuspend_success' => 'De blokkade voor deze gebruiker is opgeheven.',
|
||||
'unsuspend_working' => 'Blokkering opheffen...',
|
||||
'signed_in_as' => 'Ingelogd als :full_name',
|
||||
'sign_out' => 'Uitloggen',
|
||||
'login' => 'Inloggen',
|
||||
|
|
@ -414,6 +418,12 @@ return [
|
|||
'line_breaker_tags_comment' => 'Een lijst van HTML-tags waartussen een line breaker element wordt geplaatst.',
|
||||
'toolbar_buttons' => 'Toolbar knoppen',
|
||||
'toolbar_buttons_comment' => 'De toolbar knoppen die standaard getoond worden door de Rich Editor.',
|
||||
'toolbar_buttons_preset' => 'Voeg preset toe voor toolbar knoppen:',
|
||||
'toolbar_buttons_presets' => [
|
||||
'default' => 'Standaard',
|
||||
'minimal' => 'Minimaal',
|
||||
'full' => 'Volledig',
|
||||
],
|
||||
],
|
||||
'tooltips' => [
|
||||
'preview_website' => 'Voorvertoning website',
|
||||
|
|
@ -440,9 +450,9 @@ return [
|
|||
'app_tagline' => 'Applicatie slogan',
|
||||
'app_tagline_description' => 'Deze slogan wordt weergegeven in het aanmeldvenster van de beheeromgeving.',
|
||||
'colors' => 'Kleuren',
|
||||
'primary_color' => 'Primair color',
|
||||
'secondary_color' => 'Secundair color',
|
||||
'accent_color' => 'Accent color',
|
||||
'primary_color' => 'Primaire kleur',
|
||||
'secondary_color' => 'Secundaire kleur',
|
||||
'accent_color' => 'Accentkleur',
|
||||
'styles' => 'Stijlen',
|
||||
'custom_stylesheet' => 'Aangepaste stylesheet',
|
||||
'navigation' => 'Navigatie',
|
||||
|
|
@ -550,12 +560,14 @@ return [
|
|||
'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_1250' => 'Windows-1250 (CP1250, Central and Eastern European)',
|
||||
'windows_1251' => 'Windows-1251 (CP1251)',
|
||||
'windows_1252' => 'Windows-1252 (CP1252)',
|
||||
],
|
||||
],
|
||||
'permissions' => [
|
||||
'manage_media' => 'Beheer media',
|
||||
'manage_media' => 'Beheer media (afbeeldingen, videos, geluiden, documenten)',
|
||||
'allow_unsafe_markdown' => 'Gebruik onveilige Markdown (Kan Javascript bevatten)',
|
||||
],
|
||||
'mediafinder' => [
|
||||
'label' => 'Media zoeker',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div
|
||||
class="control-filter"
|
||||
class="control-filter <?= $cssClasses ?>"
|
||||
data-control="filterwidget"
|
||||
data-options-handler="<?= $this->getEventHandler('onFilterGetOptions') ?>"
|
||||
data-update-handler="<?= $this->getEventHandler('onFilterUpdate') ?>">
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="redirect:0"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('cms::lang.theme.saving')) ?>"
|
||||
|
|
@ -28,6 +29,7 @@
|
|||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('cms::lang.theme.saving')) ?>"
|
||||
|
|
|
|||
|
|
@ -250,6 +250,8 @@ return [
|
|||
'no_records' => 'Geen componenten gevonden',
|
||||
'not_found' => 'Het component \':name\' is niet gevonden.',
|
||||
'method_not_found' => 'Het component \':name\' bevat geen \':method\' methode.',
|
||||
'soft_component' => 'Soft Component',
|
||||
'soft_component_description' => 'Dit component ontbreekt, maar is optioneel.',
|
||||
],
|
||||
'template' => [
|
||||
'invalid_type' => 'Onbekend type template.',
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ if(window.jQuery.request!==undefined){throw new Error('The OctoberCMS framework
|
|||
+function($){"use strict";var Request=function(element,handler,options){var $el=this.$el=$(element);this.options=options||{};if(handler===undefined){throw new Error('The request handler name is not specified.')}
|
||||
if(!handler.match(/^(?:\w+\:{2})?on*/)){throw new Error('Invalid handler name. The correct handler name format is: "onEvent".')}
|
||||
var $form=options.form?$(options.form):$el.closest('form'),$triggerEl=!!$form.length?$form:$el,context={handler:handler,options:options}
|
||||
if(typeof document.createElement('input').reportValidity=='function'&&$form&&$form[0]&&!$form[0].checkValidity()){$form[0].reportValidity();return false;}
|
||||
if((options.validate!==undefined)&&typeof document.createElement('input').reportValidity=='function'&&$form&&$form[0]&&!$form[0].checkValidity()){$form[0].reportValidity();return false;}
|
||||
$el.trigger('ajaxSetup',[context])
|
||||
var _event=jQuery.Event('oc.beforeRequest')
|
||||
$triggerEl.trigger(_event,context)
|
||||
|
|
@ -103,7 +103,7 @@ return result.join('&')}
|
|||
var old=$.fn.request
|
||||
$.fn.request=function(handler,option){var args=arguments
|
||||
var $this=$(this).first()
|
||||
var data={evalBeforeUpdate:$this.data('request-before-update'),evalSuccess:$this.data('request-success'),evalError:$this.data('request-error'),evalComplete:$this.data('request-complete'),ajaxGlobal:$this.data('request-ajax-global'),confirm:$this.data('request-confirm'),redirect:$this.data('request-redirect'),loading:$this.data('request-loading'),flash:$this.data('request-flash'),files:$this.data('request-files'),form:$this.data('request-form'),url:$this.data('request-url'),update:paramToObj('data-request-update',$this.data('request-update')),data:paramToObj('data-request-data',$this.data('request-data'))}
|
||||
var data={evalBeforeUpdate:$this.data('request-before-update'),evalSuccess:$this.data('request-success'),evalError:$this.data('request-error'),evalComplete:$this.data('request-complete'),ajaxGlobal:$this.data('request-ajax-global'),confirm:$this.data('request-confirm'),redirect:$this.data('request-redirect'),loading:$this.data('request-loading'),flash:$this.data('request-flash'),files:$this.data('request-files'),validate:$this.data('request-validate'),form:$this.data('request-form'),url:$this.data('request-url'),update:paramToObj('data-request-update',$this.data('request-update')),data:paramToObj('data-request-data',$this.data('request-data'))}
|
||||
if(!handler)handler=$this.data('request')
|
||||
var options=$.extend(true,{},Request.DEFAULTS,data,typeof option=='object'&&option)
|
||||
return new Request($this,handler,options)}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ if(window.jQuery.request!==undefined){throw new Error('The OctoberCMS framework
|
|||
+function($){"use strict";var Request=function(element,handler,options){var $el=this.$el=$(element);this.options=options||{};if(handler===undefined){throw new Error('The request handler name is not specified.')}
|
||||
if(!handler.match(/^(?:\w+\:{2})?on*/)){throw new Error('Invalid handler name. The correct handler name format is: "onEvent".')}
|
||||
var $form=options.form?$(options.form):$el.closest('form'),$triggerEl=!!$form.length?$form:$el,context={handler:handler,options:options}
|
||||
if(typeof document.createElement('input').reportValidity=='function'&&$form&&$form[0]&&!$form[0].checkValidity()){$form[0].reportValidity();return false;}
|
||||
if((options.validate!==undefined)&&typeof document.createElement('input').reportValidity=='function'&&$form&&$form[0]&&!$form[0].checkValidity()){$form[0].reportValidity();return false;}
|
||||
$el.trigger('ajaxSetup',[context])
|
||||
var _event=jQuery.Event('oc.beforeRequest')
|
||||
$triggerEl.trigger(_event,context)
|
||||
|
|
@ -103,7 +103,7 @@ return result.join('&')}
|
|||
var old=$.fn.request
|
||||
$.fn.request=function(handler,option){var args=arguments
|
||||
var $this=$(this).first()
|
||||
var data={evalBeforeUpdate:$this.data('request-before-update'),evalSuccess:$this.data('request-success'),evalError:$this.data('request-error'),evalComplete:$this.data('request-complete'),ajaxGlobal:$this.data('request-ajax-global'),confirm:$this.data('request-confirm'),redirect:$this.data('request-redirect'),loading:$this.data('request-loading'),flash:$this.data('request-flash'),files:$this.data('request-files'),form:$this.data('request-form'),url:$this.data('request-url'),update:paramToObj('data-request-update',$this.data('request-update')),data:paramToObj('data-request-data',$this.data('request-data'))}
|
||||
var data={evalBeforeUpdate:$this.data('request-before-update'),evalSuccess:$this.data('request-success'),evalError:$this.data('request-error'),evalComplete:$this.data('request-complete'),ajaxGlobal:$this.data('request-ajax-global'),confirm:$this.data('request-confirm'),redirect:$this.data('request-redirect'),loading:$this.data('request-loading'),flash:$this.data('request-flash'),files:$this.data('request-files'),validate:$this.data('request-validate'),form:$this.data('request-form'),url:$this.data('request-url'),update:paramToObj('data-request-update',$this.data('request-update')),data:paramToObj('data-request-data',$this.data('request-data'))}
|
||||
if(!handler)handler=$this.data('request')
|
||||
var options=$.extend(true,{},Request.DEFAULTS,data,typeof option=='object'&&option)
|
||||
return new Request($this,handler,options)}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ if (window.jQuery.request !== undefined) {
|
|||
/*
|
||||
* Validate the form client-side
|
||||
*/
|
||||
if (typeof document.createElement('input').reportValidity == 'function' && $form && $form[0] && !$form[0].checkValidity()) {
|
||||
if ((options.validate !== undefined) && typeof document.createElement('input').reportValidity == 'function' && $form && $form[0] && !$form[0].checkValidity()) {
|
||||
$form[0].reportValidity();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -446,6 +446,7 @@ if (window.jQuery.request !== undefined) {
|
|||
loading: $this.data('request-loading'),
|
||||
flash: $this.data('request-flash'),
|
||||
files: $this.data('request-files'),
|
||||
validate: $this.data('request-validate'),
|
||||
form: $this.data('request-form'),
|
||||
url: $this.data('request-url'),
|
||||
update: paramToObj('data-request-update', $this.data('request-update')),
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@
|
|||
return result
|
||||
}
|
||||
|
||||
AutocompleteEditor.prototype.onInspectorPropertyChanged = function(property, value) {
|
||||
AutocompleteEditor.prototype.onInspectorPropertyChanged = function(property) {
|
||||
if (!this.propertyDefinition.depends || this.propertyDefinition.depends.indexOf(property) === -1) {
|
||||
return
|
||||
}
|
||||
|
|
@ -219,7 +219,7 @@
|
|||
var itemsEvent = $.Event('autocompleteitems.oc.inspector')
|
||||
|
||||
$inspectable.trigger(itemsEvent, [{
|
||||
values: values,
|
||||
values: values,
|
||||
callback: this.proxy(this.itemsRequestDone),
|
||||
property: this.inspector.getPropertyPath(this.propertyDefinition.property),
|
||||
propertyDefinition: this.propertyDefinition
|
||||
|
|
@ -253,4 +253,4 @@
|
|||
}
|
||||
|
||||
$.oc.inspector.propertyEditors.autocomplete = AutocompleteEditor
|
||||
}(window.jQuery);
|
||||
}(window.jQuery);
|
||||
|
|
|
|||
|
|
@ -112,6 +112,9 @@
|
|||
dropdownCssClass: 'ocInspectorDropdown'
|
||||
}
|
||||
|
||||
if (this.propertyDefinition.emptyOption !== undefined) {
|
||||
options.placeholder = this.propertyDefinition.emptyOption
|
||||
}
|
||||
if (this.propertyDefinition.placeholder !== undefined) {
|
||||
options.placeholder = this.propertyDefinition.placeholder
|
||||
}
|
||||
|
|
@ -131,7 +134,7 @@
|
|||
}
|
||||
|
||||
DropdownEditor.prototype.createPlaceholder = function(select) {
|
||||
var placeholder = this.propertyDefinition.placeholder
|
||||
var placeholder = this.propertyDefinition.placeholder || this.propertyDefinition.emptyOption
|
||||
|
||||
if (placeholder !== undefined && !Modernizr.touchevents) {
|
||||
this.createOption(select, null, null)
|
||||
|
|
@ -202,7 +205,7 @@
|
|||
this.inspector.setPropertyValue(this.propertyDefinition.property, this.normalizeValue(select.value), this.initialization)
|
||||
}
|
||||
|
||||
DropdownEditor.prototype.onInspectorPropertyChanged = function(property, value) {
|
||||
DropdownEditor.prototype.onInspectorPropertyChanged = function(property) {
|
||||
if (!this.propertyDefinition.depends || this.propertyDefinition.depends.indexOf(property) === -1) {
|
||||
return
|
||||
}
|
||||
|
|
@ -260,7 +263,7 @@
|
|||
return false
|
||||
}
|
||||
|
||||
return BaseProto.isEmptyValue.call(this, value)
|
||||
return BaseProto.isEmptyValue.call(this, value)
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -307,7 +310,8 @@
|
|||
var currentValue = this.inspector.getPropertyValue(this.propertyDefinition.property),
|
||||
data = this.getRootSurface().getValues(),
|
||||
self = this,
|
||||
$form = $(this.getSelect()).closest('form')
|
||||
$form = $(this.getSelect()).closest('form'),
|
||||
dependents = this.inspector.findDependentProperties(this.propertyDefinition.property)
|
||||
|
||||
if (currentValue === undefined) {
|
||||
currentValue = this.propertyDefinition.default
|
||||
|
|
@ -316,6 +320,15 @@
|
|||
var callback = function dropdownOptionsRequestDoneClosure(data) {
|
||||
self.hideLoadingIndicator()
|
||||
self.optionsRequestDone(data, currentValue, true)
|
||||
|
||||
if (dependents.length > 0) {
|
||||
for (var i in dependents) {
|
||||
var editor = self.inspector.findPropertyEditor(dependents[i])
|
||||
if (editor && typeof editor.onInspectorPropertyChanged === 'function') {
|
||||
editor.onInspectorPropertyChanged(self.propertyDefinition.property)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.propertyDefinition.depends) {
|
||||
|
|
@ -347,7 +360,7 @@
|
|||
var optionsEvent = $.Event('dropdownoptions.oc.inspector')
|
||||
|
||||
$inspectable.trigger(optionsEvent, [{
|
||||
values: values,
|
||||
values: values,
|
||||
callback: callback,
|
||||
property: this.inspector.getPropertyPath(this.propertyDefinition.property),
|
||||
propertyDefinition: this.propertyDefinition
|
||||
|
|
@ -434,4 +447,4 @@
|
|||
}
|
||||
|
||||
$.oc.inspector.propertyEditors.dropdown = DropdownEditor
|
||||
}(window.jQuery);
|
||||
}(window.jQuery);
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@
|
|||
* - containerElement container DOM element
|
||||
* - properties array (array of objects)
|
||||
* - values - property values, an object
|
||||
* - inspectorUniqueId - a string containing the unique inspector identifier.
|
||||
* The identifier should be a constant for an inspectable element. Use
|
||||
* $.oc.inspector.helpers.generateElementUniqueId(element) to generate a persistent ID
|
||||
* - inspectorUniqueId - a string containing the unique inspector identifier.
|
||||
* The identifier should be a constant for an inspectable element. Use
|
||||
* $.oc.inspector.helpers.generateElementUniqueId(element) to generate a persistent ID
|
||||
* for an element. Use $.oc.inspector.helpers.generateUniqueId() to generate an ID
|
||||
* not associated with an element. Inspector uses the ID for storing configuration
|
||||
* related to an element in the document DOM.
|
||||
|
|
@ -138,7 +138,7 @@
|
|||
//
|
||||
|
||||
/**
|
||||
* Builds the Inspector table. The markup generated by this method looks
|
||||
* Builds the Inspector table. The markup generated by this method looks
|
||||
* like this:
|
||||
*
|
||||
* <div>
|
||||
|
|
@ -391,7 +391,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Surface.prototype.applyGroupLevelToRow = function(row, group) {
|
||||
if (row.hasAttribute('data-group-level')) {
|
||||
return
|
||||
|
|
@ -507,7 +507,7 @@
|
|||
row.removeChild(cell)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.editors.push(editor)
|
||||
}
|
||||
|
||||
|
|
@ -538,7 +538,7 @@
|
|||
if (!supressChangeEvents) {
|
||||
if (this.originalValues[property] === undefined || !this.comparePropertyValues(this.originalValues[property], value)) {
|
||||
this.markPropertyChanged(property, true)
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.markPropertyChanged(property, false)
|
||||
}
|
||||
|
|
@ -563,10 +563,10 @@
|
|||
|
||||
Surface.prototype.notifyEditorsPropertyChanged = function(propertyPath, value) {
|
||||
// Editors use this event to watch changes in properties
|
||||
// they depend on. All editors should be notified, including
|
||||
// they depend on. All editors should be notified, including
|
||||
// editors in nested surfaces. The property name is passed as a
|
||||
// path object.property (if the property is nested), so that
|
||||
// property depenencies could be defined as
|
||||
// path object.property (if the property is nested), so that
|
||||
// property depenencies could be defined as
|
||||
// ['property', 'object.property']
|
||||
|
||||
for (var i = 0, len = this.editors.length; i < len; i++) {
|
||||
|
|
@ -638,7 +638,7 @@
|
|||
}
|
||||
|
||||
if ($.oc.inspector.propertyEditors[type] === undefined) {
|
||||
throw new Error('The Inspector editor class "' + type +
|
||||
throw new Error('The Inspector editor class "' + type +
|
||||
'" is not defined in the $.oc.inspector.propertyEditors namespace.')
|
||||
}
|
||||
}
|
||||
|
|
@ -688,6 +688,24 @@
|
|||
return result.join('.')
|
||||
}
|
||||
|
||||
Surface.prototype.findDependentProperties = function(propertyName) {
|
||||
var dependents = []
|
||||
|
||||
for (var i in this.rawProperties) {
|
||||
var property = this.rawProperties[i]
|
||||
|
||||
if (!property.depends) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (property.depends.indexOf(propertyName) !== -1) {
|
||||
dependents.push(property.property)
|
||||
}
|
||||
}
|
||||
|
||||
return dependents
|
||||
}
|
||||
|
||||
//
|
||||
// Nested surfaces support
|
||||
//
|
||||
|
|
@ -847,7 +865,7 @@
|
|||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
value = externalParameterEditor.getValue()
|
||||
value = '{{ ' + value + ' }}'
|
||||
|
|
@ -952,4 +970,4 @@
|
|||
$.oc.inspector.surface = Surface
|
||||
$.oc.inspector.removedProperty = {removed: true}
|
||||
$.oc.inspector.invalidProperty = {invalid: true}
|
||||
}(window.jQuery);
|
||||
}(window.jQuery);
|
||||
|
|
|
|||
|
|
@ -596,14 +596,14 @@ class MediaLibrary
|
|||
}
|
||||
|
||||
/**
|
||||
* Initializes a library item from a path and item type.
|
||||
* @param string $path Specifies the item path relative to the storage disk root.
|
||||
* Initializes a library item from file metadata and item type.
|
||||
* @param array $item Specifies the file metadata as returned by the storage adapter.
|
||||
* @param string $itemType Specifies the item type.
|
||||
* @return mixed Returns the MediaLibraryItem object or NULL if the item is not visible.
|
||||
*/
|
||||
protected function initLibraryItem($path, $itemType)
|
||||
protected function initLibraryItem($item, $itemType)
|
||||
{
|
||||
$relativePath = $this->getMediaRelativePath($path);
|
||||
$relativePath = $this->getMediaRelativePath($item['path']);
|
||||
|
||||
if (!$this->isVisible($relativePath)) {
|
||||
return;
|
||||
|
|
@ -613,9 +613,11 @@ class MediaLibrary
|
|||
* S3 doesn't allow getting the last modified timestamp for folders,
|
||||
* so this feature is disabled - folders timestamp is always NULL.
|
||||
*/
|
||||
$lastModified = $itemType == MediaLibraryItem::TYPE_FILE
|
||||
? $this->getStorageDisk()->lastModified($path)
|
||||
: null;
|
||||
if ($itemType === MediaLibraryItem::TYPE_FILE) {
|
||||
$lastModified = $item['timestamp'] ?? $this->getStorageDisk()->lastModified($item['path']);
|
||||
} else {
|
||||
$lastModified = null;
|
||||
}
|
||||
|
||||
/*
|
||||
* The folder size (number of items) doesn't respect filters. That
|
||||
|
|
@ -623,9 +625,11 @@ class MediaLibrary
|
|||
* zero items for a folder that contains files not visible with a
|
||||
* currently applied filter. -ab
|
||||
*/
|
||||
$size = $itemType == MediaLibraryItem::TYPE_FILE
|
||||
? $this->getStorageDisk()->size($path)
|
||||
: $this->getFolderItemCount($path);
|
||||
if ($itemType === MediaLibraryItem::TYPE_FILE) {
|
||||
$size = $item['size'] ?? $this->getStorageDisk()->size($item['path']);
|
||||
} else {
|
||||
$size = $this->getFolderItemCount($item['path']);
|
||||
}
|
||||
|
||||
$publicUrl = $this->getPathUrl($relativePath);
|
||||
|
||||
|
|
@ -666,17 +670,20 @@ class MediaLibrary
|
|||
'folders' => []
|
||||
];
|
||||
|
||||
$files = $this->getStorageDisk()->files($fullFolderPath);
|
||||
foreach ($files as $file) {
|
||||
if ($libraryItem = $this->initLibraryItem($file, MediaLibraryItem::TYPE_FILE)) {
|
||||
$result['files'][] = $libraryItem;
|
||||
}
|
||||
}
|
||||
$contents = $this->getStorageDisk()->listContents($fullFolderPath);
|
||||
|
||||
$folders = $this->getStorageDisk()->directories($fullFolderPath);
|
||||
foreach ($folders as $folder) {
|
||||
if ($libraryItem = $this->initLibraryItem($folder, MediaLibraryItem::TYPE_FOLDER)) {
|
||||
$result['folders'][] = $libraryItem;
|
||||
foreach ($contents as $content) {
|
||||
if ($content['type'] === 'file') {
|
||||
$type = MediaLibraryItem::TYPE_FILE;
|
||||
$key = 'files';
|
||||
} elseif ($content['type'] === 'dir') {
|
||||
$type = MediaLibraryItem::TYPE_FOLDER;
|
||||
$key = 'folders';
|
||||
}
|
||||
|
||||
$libraryItem = $this->initLibraryItem($content, $type);
|
||||
if (!is_null($libraryItem)) {
|
||||
$result[$key][] = $libraryItem;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -655,7 +655,9 @@ class PluginManager
|
|||
|
||||
/**
|
||||
* Scans the system plugins to locate any dependencies that are not currently
|
||||
* installed. Returns an array of plugin codes that are needed.
|
||||
* installed. Returns an array of missing plugin codes keyed by the plugin that requires them.
|
||||
*
|
||||
* ['Author.Plugin' => ['Required.Plugin1', 'Required.Plugin2']
|
||||
*
|
||||
* PluginManager::instance()->findMissingDependencies();
|
||||
*
|
||||
|
|
@ -676,7 +678,7 @@ class PluginManager
|
|||
}
|
||||
|
||||
if (!in_array($require, $missing)) {
|
||||
$missing[] = $require;
|
||||
$missing[$this->getIdentifier($plugin)][] = $require;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ class Updates extends Controller
|
|||
if ($this->getAjaxHandler() == 'onExecuteStep') {
|
||||
$this->useSecurityToken = false;
|
||||
}
|
||||
|
||||
$this->vars['warnings'] = $this->getWarnings();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -227,6 +229,23 @@ class Updates extends Controller
|
|||
return $contents;
|
||||
}
|
||||
|
||||
protected function getWarnings()
|
||||
{
|
||||
$warnings = [];
|
||||
$missingDependencies = PluginManager::instance()->findMissingDependencies();
|
||||
|
||||
foreach ($missingDependencies as $pluginCode => $plugin) {
|
||||
foreach ($plugin as $missingPluginCode) {
|
||||
$warnings[] = Lang::get('system::lang.updates.update_warnings_plugin_missing', [
|
||||
'code' => '<strong>' . $missingPluginCode . '</strong>',
|
||||
'parent_code' => '<strong>' . $pluginCode . '</strong>'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override for ListController behavior.
|
||||
* Modifies the CSS class for each row in the list to
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="redirect:0"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('system::lang.mail_templates.creating_layout')) ?>"
|
||||
class="btn btn-primary">
|
||||
|
|
@ -26,6 +27,7 @@
|
|||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('system::lang.mail_templates.creating_layout')) ?>"
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="redirect:0"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('system::lang.mail_templates.saving_layout')) ?>"
|
||||
|
|
@ -38,6 +39,7 @@
|
|||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('system::lang.mail_templates.saving_layout')) ?>"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('system::lang.mail_templates.creating_partial')) ?>"
|
||||
class="btn btn-primary">
|
||||
|
|
@ -26,6 +27,7 @@
|
|||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('system::lang.mail_templates.creating_partial')) ?>"
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="redirect:0"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('system::lang.mail_templates.saving_layout')) ?>"
|
||||
|
|
@ -38,6 +39,7 @@
|
|||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('system::lang.mail_templates.saving_layout')) ?>"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('system::lang.mail_templates.creating')) ?>"
|
||||
class="btn btn-primary">
|
||||
|
|
@ -26,6 +27,7 @@
|
|||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('system::lang.mail_templates.creating')) ?>"
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="redirect:0"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('system::lang.mail_templates.saving')) ?>"
|
||||
|
|
@ -38,6 +39,7 @@
|
|||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('system::lang.mail_templates.saving')) ?>"
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="redirect:0"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
|
|
@ -20,6 +21,7 @@
|
|||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-validate
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
|
|
|
|||
|
|
@ -47,5 +47,18 @@
|
|||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (count($warnings)): ?>
|
||||
<div class="scoreboard">
|
||||
<div class="callout fade in callout-danger no-icon">
|
||||
<div class="header">
|
||||
<h3><?= e(trans('system::lang.updates.update_warnings_title')) ?></h3>
|
||||
<ul>
|
||||
<?php foreach ($warnings as $warning): ?>
|
||||
<li><?= $warning ?></li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<?= $this->listRender() ?>
|
||||
|
|
|
|||
|
|
@ -331,6 +331,8 @@ return [
|
|||
'core_downloading' => 'Downloading application files',
|
||||
'core_extracting' => 'Unpacking application files',
|
||||
'core_set_build' => 'Setting build number',
|
||||
'update_warnings_title' => 'Some issues have been detected and require attention:',
|
||||
'update_warnings_plugin_missing' => 'The :parent_code plugin requires :code to be installed before it will work',
|
||||
'changelog' => 'Changelog',
|
||||
'changelog_view_details' => 'View details',
|
||||
'plugins' => 'Plugins',
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ return [
|
|||
'sv' => 'Svenska',
|
||||
'sk' => 'Slovenský',
|
||||
'sl' => 'Slovenščina',
|
||||
'th' => 'ไทย',
|
||||
'tr' => 'Türkçe',
|
||||
'uk' => 'Українська мова',
|
||||
'zh-cn' => '简体中文',
|
||||
|
|
@ -143,8 +144,8 @@ return [
|
|||
'project' => [
|
||||
'name' => 'Project',
|
||||
'owner_label' => 'Eigenaar',
|
||||
'attach' => 'Project koppelen',
|
||||
'detach' => 'Project ontkoppelen',
|
||||
'attach' => 'Koppel project',
|
||||
'detach' => 'Ontkoppel project',
|
||||
'none' => 'Geen',
|
||||
'id' => [
|
||||
'label' => 'Project ID',
|
||||
|
|
@ -321,18 +322,19 @@ return [
|
|||
'plugin_description' => 'Omschrijving',
|
||||
'plugin_version' => 'Versie',
|
||||
'plugin_author' => 'Auteur',
|
||||
'plugin_not_found' => 'Plugin not found',
|
||||
'plugin_not_found' => 'Plugin niet gevonden',
|
||||
'plugin_version_not_found' => 'Pluginversie niet gevonden',
|
||||
'core_current_build' => 'Huidige build',
|
||||
'core_view_changelog' => 'Bekijk changelog',
|
||||
'core_build' => 'Build :build',
|
||||
'core_build_help' => 'De meest recente versie is beschikbaar.',
|
||||
'core_downloading' => 'Bestanden aan het downloaden',
|
||||
'core_extracting' => 'Bestanden aan het uitpakken',
|
||||
'core_set_build' => 'Build nummer bijwerken',
|
||||
'changelog' => 'Changelog',
|
||||
'core_set_build' => 'Bouwnummer bijwerken',
|
||||
'changelog' => 'Logboek van wijzigingen',
|
||||
'changelog_view_details' => 'Bekijk details',
|
||||
'plugins' => 'Plugins',
|
||||
'themes' => "Thema's",
|
||||
'themes' => 'Thema\'s',
|
||||
'disabled' => 'Uitgeschakeld',
|
||||
'plugin_downloading' => 'Plugin downloaden: :name',
|
||||
'plugin_extracting' => 'Plugin uitpakken: :name',
|
||||
|
|
@ -358,8 +360,8 @@ return [
|
|||
'important_action' => [
|
||||
'empty' => 'Selecteer actie',
|
||||
'confirm' => 'Bevestig update',
|
||||
'skip' => 'Sla deze plugin over (eenmalig)',
|
||||
'ignore' => 'Negeer deze plugin (altijd)',
|
||||
'skip' => 'Sla deze plugin eenmalig over',
|
||||
'ignore' => 'Negeer deze plugin',
|
||||
],
|
||||
'important_action_required' => 'Actie vereist!',
|
||||
'important_view_guide' => 'Toon upgrade handleiding',
|
||||
|
|
|
|||
|
|
@ -265,46 +265,48 @@ return [
|
|||
'install_success' => 'Плагин был успешно установлен.',
|
||||
],
|
||||
'updates' => [
|
||||
'title' => 'Менеджер обновлений',
|
||||
'name' => 'Обновление ПО',
|
||||
'menu_label' => 'Обновления и плагины',
|
||||
'menu_description' => 'Обновление системы, управление и установка плагинов и тем.',
|
||||
'return_link' => 'Вернуться к системе обновлений',
|
||||
'check_label' => 'Проверить обновления ',
|
||||
'retry_label' => 'Попробовать еще раз',
|
||||
'plugin_name' => 'Название',
|
||||
'plugin_code' => 'Код',
|
||||
'plugin_description' => 'Описание',
|
||||
'plugin_version' => 'Версия',
|
||||
'plugin_author' => 'Автор',
|
||||
'plugin_not_found' => 'Плагин не найден',
|
||||
'plugin_version_not_found' => 'Версия плагина не найдена',
|
||||
'core_current_build' => 'Текущая сборка',
|
||||
'core_view_changelog' => 'Просмотр списка изменений',
|
||||
'core_build' => 'Сборка :build',
|
||||
'core_build_help' => 'Последняя доступная сборка.',
|
||||
'core_downloading' => 'Загрузка файлов приложения',
|
||||
'core_extracting' => 'Распаковка файлов приложения',
|
||||
'core_set_build' => 'Установка номера сборки',
|
||||
'changelog' => 'Список изменений',
|
||||
'changelog_view_details' => 'Просмотр подробностей',
|
||||
'plugins' => 'Плагины',
|
||||
'themes' => 'Темы',
|
||||
'disabled' => 'Отключено',
|
||||
'plugin_downloading' => 'Загрузка плагина: :name',
|
||||
'plugin_extracting' => 'Распаковка плагина: :name',
|
||||
'plugin_version_none' => 'Новый плагин',
|
||||
'plugin_current_version' => 'Текущая версия',
|
||||
'theme_new_install' => 'Новая тема установлена.',
|
||||
'theme_downloading' => 'Загрузка темы: :name',
|
||||
'theme_extracting' => 'Распаковка темы: :name',
|
||||
'update_label' => 'Обновить',
|
||||
'update_completing' => 'Завершение процесса обновления',
|
||||
'update_loading' => 'Поиск доступных обновлений...',
|
||||
'update_success' => 'Процесс обновления был успешно завершен.',
|
||||
'update_failed_label' => 'Не удалось выполнить обновление',
|
||||
'force_label' => 'Принудительно обновить',
|
||||
'found' => [
|
||||
'title' => 'Менеджер обновлений',
|
||||
'name' => 'Обновление ПО',
|
||||
'menu_label' => 'Обновления и плагины',
|
||||
'menu_description' => 'Обновление системы, управление и установка плагинов и тем.',
|
||||
'return_link' => 'Вернуться к системе обновлений',
|
||||
'check_label' => 'Проверить обновления ',
|
||||
'retry_label' => 'Попробовать еще раз',
|
||||
'plugin_name' => 'Название',
|
||||
'plugin_code' => 'Код',
|
||||
'plugin_description' => 'Описание',
|
||||
'plugin_version' => 'Версия',
|
||||
'plugin_author' => 'Автор',
|
||||
'plugin_not_found' => 'Плагин не найден',
|
||||
'plugin_version_not_found' => 'Версия плагина не найдена',
|
||||
'core_current_build' => 'Текущая сборка',
|
||||
'core_view_changelog' => 'Просмотр списка изменений',
|
||||
'core_build' => 'Сборка :build',
|
||||
'core_build_help' => 'Последняя доступная сборка.',
|
||||
'core_downloading' => 'Загрузка файлов приложения',
|
||||
'core_extracting' => 'Распаковка файлов приложения',
|
||||
'core_set_build' => 'Установка номера сборки',
|
||||
'update_warnings_title' => 'Обнаружены проблемы требующие вашего внимания',
|
||||
'update_warnings_plugin_missing' => 'Плагин :code ялвяется зависисмостью плагина :parent_code, но он не обнаружен в системе. Пожалуйста установите этот плагин.',
|
||||
'changelog' => 'Список изменений',
|
||||
'changelog_view_details' => 'Просмотр подробностей',
|
||||
'plugins' => 'Плагины',
|
||||
'themes' => 'Темы',
|
||||
'disabled' => 'Отключено',
|
||||
'plugin_downloading' => 'Загрузка плагина: :name',
|
||||
'plugin_extracting' => 'Распаковка плагина: :name',
|
||||
'plugin_version_none' => 'Новый плагин',
|
||||
'plugin_current_version' => 'Текущая версия',
|
||||
'theme_new_install' => 'Новая тема установлена.',
|
||||
'theme_downloading' => 'Загрузка темы: :name',
|
||||
'theme_extracting' => 'Распаковка темы: :name',
|
||||
'update_label' => 'Обновить',
|
||||
'update_completing' => 'Завершение процесса обновления',
|
||||
'update_loading' => 'Поиск доступных обновлений...',
|
||||
'update_success' => 'Процесс обновления был успешно завершен.',
|
||||
'update_failed_label' => 'Не удалось выполнить обновление',
|
||||
'force_label' => 'Принудительно обновить',
|
||||
'found' => [
|
||||
'label' => 'Доступны новые обновления!',
|
||||
'help' => 'Выберите «Обновить», чтобы начать процесс обновления.',
|
||||
],
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ class Status extends ReportWidgetBase
|
|||
{
|
||||
$warnings = [];
|
||||
|
||||
$missingPlugins = PluginManager::instance()->findMissingDependencies();
|
||||
$missingDependencies = PluginManager::instance()->findMissingDependencies();
|
||||
|
||||
$writablePaths = [
|
||||
temp_path(),
|
||||
|
|
@ -127,8 +127,13 @@ class Status extends ReportWidgetBase
|
|||
}
|
||||
}
|
||||
|
||||
foreach ($missingPlugins as $pluginCode) {
|
||||
$warnings[] = Lang::get('backend::lang.warnings.plugin_missing', ['name' => '<strong>'.$pluginCode.'</strong>']);
|
||||
foreach ($missingDependencies as $pluginCode => $plugin) {
|
||||
foreach ($plugin as $missingPluginCode) {
|
||||
$warnings[] = Lang::get('system::lang.updates.update_warnings_plugin_missing', [
|
||||
'code' => '<strong>' . $missingPluginCode . '</strong>',
|
||||
'parent_code' => '<strong>' . $pluginCode . '</strong>'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $warnings;
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
THIS IS A TEXT DOCUMENT
|
||||
|
|
@ -4,6 +4,12 @@ use System\Classes\MediaLibrary;
|
|||
|
||||
class MediaLibraryTest extends TestCase // @codingStandardsIgnoreLine
|
||||
{
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->removeMedia();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function invalidPathsProvider()
|
||||
{
|
||||
return [
|
||||
|
|
@ -62,4 +68,63 @@ class MediaLibraryTest extends TestCase // @codingStandardsIgnoreLine
|
|||
$result = MediaLibrary::validatePath($path);
|
||||
$this->assertIsString($result);
|
||||
}
|
||||
|
||||
public function testListFolderContents()
|
||||
{
|
||||
$this->setUpStorage();
|
||||
$this->copyMedia();
|
||||
|
||||
$contents = MediaLibrary::instance()->listFolderContents();
|
||||
$this->assertNotEmpty($contents, 'Media library item is not discovered');
|
||||
|
||||
$item = reset($contents);
|
||||
$this->assertAttributeEquals('file', 'type', $item, 'Media library item does not have the right type');
|
||||
$this->assertAttributeEquals('/text.txt', 'path', $item, 'Media library item does not have the right path');
|
||||
$this->assertAttributeNotEmpty('lastModified', $item, 'Media library item last modified is empty');
|
||||
$this->assertAttributeNotEmpty('size', $item, 'Media library item size is empty');
|
||||
}
|
||||
|
||||
protected function setUpStorage()
|
||||
{
|
||||
$this->app->useStoragePath(base_path('storage/temp'));
|
||||
|
||||
config(['filesystems.disks.test_local' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app'),
|
||||
]]);
|
||||
|
||||
config(['cms.storage.media' => [
|
||||
'disk' => 'test_local',
|
||||
'folder' => 'media',
|
||||
'path' => '/storage/app/media',
|
||||
]]);
|
||||
}
|
||||
|
||||
protected function copyMedia()
|
||||
{
|
||||
$mediaPath = storage_path('app/media');
|
||||
|
||||
if (!is_dir($mediaPath)) {
|
||||
mkdir($mediaPath, 0777, true);
|
||||
}
|
||||
|
||||
foreach (glob(base_path('tests/fixtures/media/*')) as $file) {
|
||||
$path = pathinfo($file);
|
||||
copy($file, $mediaPath . DIRECTORY_SEPARATOR . $path['basename']);
|
||||
}
|
||||
}
|
||||
|
||||
protected function removeMedia()
|
||||
{
|
||||
if ($this->app->storagePath() !== base_path('storage/temp')) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (glob(storage_path('app/media/*')) as $file) {
|
||||
unlink($file);
|
||||
}
|
||||
|
||||
rmdir(storage_path('app/media'));
|
||||
rmdir(storage_path('app'));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue