diff --git a/plugins/ahmadfatoni/apigenerator/controllers/api/BlogPostsApiController.php b/plugins/ahmadfatoni/apigenerator/controllers/api/BlogPostsApiController.php index 3199197..5134906 100644 --- a/plugins/ahmadfatoni/apigenerator/controllers/api/BlogPostsApiController.php +++ b/plugins/ahmadfatoni/apigenerator/controllers/api/BlogPostsApiController.php @@ -10,6 +10,7 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Validator; use RainLab\Blog\Models\Post; use TPS\Birzha\Classes\BlogPostResource; +use TPS\Birzha\Models\SliderApp; class BlogPostsApiController extends Controller { @@ -42,6 +43,31 @@ class BlogPostsApiController extends Controller ->orderBy('published_at', $data['sort_order'] ?? 'desc') ->paginate($data['per_page'] ?? 7))->response()->getData(), 200); } + + public function getSliders(Request $request) + { + $path = Config::get('cms.storage.media.path'); + + $data = $request->all(); + $validator = Validator::make($data, [ + //'locale' => 'required|in:ru,en,tm', + 'type' => 'required' + ]); + if($validator->fails()) { + return $this->helpers->apiArrayResponseBuilder(400, 'fail', $validator->errors() ); + } + $dataSlider = SliderApp::where('type', $data['type'])->orderBy('order', 'desc')->select('id', 'img', 'type', 'order')->get(); + + //$dataSlider["img"] = $path.$dataSlider["img"]; + + if($dataSlider){ + $dataSlider->each(function ($item, $key) { + $item->img = 'http://78.111.88.8:9086'.Config::get('cms.storage.media.path').$item->img; + }); + } + + return response()->json($dataSlider, 200); + } //posts item public function show($id, Request $request) diff --git a/plugins/ahmadfatoni/apigenerator/controllers/api/CategoriesAPIController.php b/plugins/ahmadfatoni/apigenerator/controllers/api/CategoriesAPIController.php index 5e6b28a..26e6efd 100644 --- a/plugins/ahmadfatoni/apigenerator/controllers/api/CategoriesAPIController.php +++ b/plugins/ahmadfatoni/apigenerator/controllers/api/CategoriesAPIController.php @@ -20,20 +20,44 @@ class CategoriesAPIController extends Controller $this->helpers = $helpers; } - public function index(){ - - $data = $this->Category + public function index(Request $request){ + + $data = $request->all(); + $validator = Validator::make($data, [ + //'locale' => 'required|in:ru,en,tm', + 'featured' => 'required' + ]); + if($validator->fails()) { + return $this->helpers->apiArrayResponseBuilder(400, 'fail', $validator->errors() ); + } + + if($data["featured"] == 3){ + $dataCat = $this->Category + ->select('id', 'name', 'icon', 'status', 'slug', 'sort_order', 'is_featured') ->with('translations:locale,model_id,attribute_data') + ->orderBy('sort_order', 'asc') ->active() ->get(); + }else{ + $dataCat = $this->Category + ->select('id', 'name', 'icon', 'status', 'slug', 'sort_order', 'is_featured') + ->with('translations:locale,model_id,attribute_data') + ->where('is_featured', $data["featured"]) + ->orderBy('sort_order', 'asc') + ->active() + ->get(); + + } + + - if($data) { - $data->each(function ($item, $key) { + if($dataCat) { + $dataCat->each(function ($item, $key) { $item->icon = $this->pageUrl('index') . \Config::get('cms.storage.media.path') . $item->icon; }); } - return $this->helpers->apiArrayResponseBuilder(200, 'success', $data); + return $this->helpers->apiArrayResponseBuilder(200, 'success', $dataCat); } public function show($id){ diff --git a/plugins/ahmadfatoni/apigenerator/routes.php b/plugins/ahmadfatoni/apigenerator/routes.php index b675ca8..4f6b398 100644 --- a/plugins/ahmadfatoni/apigenerator/routes.php +++ b/plugins/ahmadfatoni/apigenerator/routes.php @@ -26,6 +26,8 @@ Route::group(['prefix' =>'api/v1','namespace' =>'AhmadFatoni\ApiGenerator\Contro Route::get('news', 'BlogPostsApiController@index'); Route::get('news/{id}', 'BlogPostsApiController@show')->where(['id' => '[0-9]+']); + + Route::get('sliders', 'BlogPostsApiController@getSliders'); Route::middleware(['\Tymon\JWTAuth\Middleware\GetUserFromToken'])->group(function () { diff --git a/plugins/indikator/devtools/LICENCE.md b/plugins/indikator/devtools/LICENCE.md new file mode 100644 index 0000000..38cee79 --- /dev/null +++ b/plugins/indikator/devtools/LICENCE.md @@ -0,0 +1,19 @@ +# MIT license + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugins/indikator/devtools/Plugin.php b/plugins/indikator/devtools/Plugin.php new file mode 100644 index 0000000..ae2275a --- /dev/null +++ b/plugins/indikator/devtools/Plugin.php @@ -0,0 +1,169 @@ + 'indikator.devtools::lang.plugin.name', + 'description' => 'indikator.devtools::lang.plugin.description', + 'author' => 'indikator.devtools::lang.plugin.author', + 'icon' => 'icon-wrench', + 'homepage' => 'https://github.com/gergo85/oc-devtools' + ]; + } + + public function registerSettings() + { + return [ + 'devtool' => [ + 'label' => 'indikator.devtools::lang.help.menu_label', + 'description' => 'indikator.devtools::lang.help.menu_description', + 'icon' => 'icon-wrench', + 'class' => 'Indikator\DevTools\Models\Settings', + 'category' => SettingsManager::CATEGORY_SYSTEM, + 'permissions' => ['indikator.devtools.settings'] + ] + ]; + } + + public function registerFormWidgets() + { + return [ + 'Indikator\DevTools\FormWidgets\Help' => [ + 'label' => 'Help', + 'code' => 'help' + ] + ]; + } + + public function registerPermissions() + { + return [ + 'indikator.devtools.editor' => [ + 'tab' => 'indikator.devtools::lang.plugin.name', + 'label' => 'indikator.devtools::lang.editor.permission', + 'order' => 100, + 'roles' => ['developer'] + ], + 'indikator.devtools.settings' => [ + 'tab' => 'indikator.devtools::lang.plugin.name', + 'label' => 'indikator.devtools::lang.help.permission', + 'order' => 200, + 'roles' => ['developer'] + ] + ]; + } + + public function boot() + { + // Add new menu + BackendMenu::registerCallback(function($manager) { + $manager->registerMenuItems('Indikator.DevTools', [ + 'editor' => [ + 'label' => 'indikator.devtools::lang.editor.menu_label', + 'url' => Backend::url('indikator/devtools/editor'), + 'icon' => 'icon-file-code-o', + 'iconSvg' => 'plugins/indikator/devtools/assets/images/devtools-icon.svg', + 'permissions' => ['indikator.devtools.editor'], + 'order' => 390, + + 'sideMenu' => [ + 'assets' => [ + 'label' => 'indikator.devtools::lang.editor.plugins', + 'icon' => 'icon-cubes', + 'url' => 'javascript:;', + 'attributes' => ['data-menu-item' => 'assets'], + 'counterLabel' => 'cms::lang.asset.unsaved_label', + 'order' => 100 + ] + ] + ] + ]); + }); + + // Add new features + Event::listen('backend.form.extendFields', function($form) + { + // Security check + if (!BackendAuth::check()) { + return; + } + + // Help docs + if ($this->tools_enabled('help') && (get_class($form->config->model) == 'Cms\Classes\Page' || get_class($form->config->model) == 'Cms\Classes\Partial' || get_class($form->config->model) == 'Cms\Classes\Layout') || get_class($form->config->model) == 'Indikator\DevTools\Classes\Asset') { + if (get_class($form->config->model) == 'Indikator\DevTools\Classes\Asset') { + $content = 'php'; + } + else { + $content = 'cms'; + } + + $form->addSecondaryTabFields([ + 'help' => [ + 'label' => '', + 'tab' => 'indikator.devtools::lang.help.tab', + 'type' => 'help', + 'content' => $content + ] + ]); + + return; + } + + // Wysiwyg editor + if ($this->tools_enabled('wysiwyg') && get_class($form->config->model) == 'Cms\Classes\Content') { + foreach ($form->getFields() as $field) { + if (!empty($field->config['type']) && $field->config['type'] == 'codeeditor') { + $field->config['type'] = $field->config['widget'] = 'richeditor'; + } + } + } + }); + } + + public function tools_enabled($name) + { + // Security check + if ($name != 'help' && $name != 'wysiwyg') { + return false; + } + + // Is enabled + if (!Tools::get($name.'_enabled', false)) { + return false; + } + + // My account + $admin = BackendAuth::getUser(); + + // Is superuser + if (Tools::get($name.'_superuser', false) && $admin->is_superuser == 1) { + return true; + } + + // Is admin group + if (Tools::get($name.'_admingroup', false) > 0 && Db::table('backend_users_groups')->where(['user_id' => $admin->id, 'user_group_id' => Tools::get($name.'_admingroup', false)])->count() == 1) { + return true; + } + + // Is current user + if (Tools::get($name.'_adminid', false) > 0 && $admin->id == Tools::get($name.'_adminid', false)) { + return true; + } + + // Finish + return false; + } +} diff --git a/plugins/indikator/devtools/README.md b/plugins/indikator/devtools/README.md new file mode 100644 index 0000000..1e84729 --- /dev/null +++ b/plugins/indikator/devtools/README.md @@ -0,0 +1,24 @@ +# Developer Tools Plugin +It is a must-have plugin for you, if you use the October's Docs a lot or you want to use the build-in wysiwyg editor on the Content page. + +## Main features +* __Edit directly plugins with the online code editor!__ +* Add a Help tab to the Pages, Partials or Layouts pages. +* Replace the code editor to wysiwyg editor on the Content page. +* Show the PHP's configuration if you logged as Superuser. +* Set a different permissions for the above features. + +## Available languages +* en - English +* hu - Magyar + +## Location of plugin +You can find it in the back-end: __Settings > System > Developer Tools__ + +## PHP's configuration +If you like to get the PHP's configuration of your server, use the following URL: www.yourwebsite.com/phpinfo + +## Installation +1. Go to the __Settings > Updates & Plugins__ page in the Backend. +1. Click on the __Install plugins__ button. +1. Type the __Developer Tools__ text in the search field. diff --git a/plugins/indikator/devtools/assets/images/devtools-icon.svg b/plugins/indikator/devtools/assets/images/devtools-icon.svg new file mode 100644 index 0000000..fe0b1cf --- /dev/null +++ b/plugins/indikator/devtools/assets/images/devtools-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/plugins/indikator/devtools/assets/october.cmspage.js b/plugins/indikator/devtools/assets/october.cmspage.js new file mode 100644 index 0000000..ba1fed0 --- /dev/null +++ b/plugins/indikator/devtools/assets/october.cmspage.js @@ -0,0 +1,706 @@ +/* + * Scripts for the CMS page. + */ ++function ($) { "use strict"; + + var Base = $.oc.foundation.base, + BaseProto = Base.prototype + + var CmsPage = function() { + + Base.call(this) + + // + // Initialization + // + + this.init() + } + + CmsPage.prototype = Object.create(BaseProto) + CmsPage.prototype.constructor = CmsPage + + CmsPage.prototype.init = function() { + $(document).ready(this.proxy(this.registerHandlers)) + } + + CmsPage.prototype.updateTemplateList = function(type) { + var $form = $('#cms-side-panel form[data-template-type='+type+']'), + templateList = type + 'List' + + $form.request(templateList + '::onUpdate', { + complete: function() { + $('button[data-control=delete-template]', $form).trigger('oc.triggerOn.update') + } + }) + } + + CmsPage.prototype.registerHandlers = function() { + var $document = $(document), + $masterTabs = $('#cms-master-tabs') + + $masterTabs.on('closed.oc.tab', this.proxy(this.onTabClosed)) + $masterTabs.on('beforeClose.oc.tab', this.proxy(this.onBeforeTabClose)) + $masterTabs.on('oc.beforeRequest', this.proxy(this.onBeforeRequest)) + $masterTabs.on('shown.bs.tab', this.proxy(this.onTabShown)) + $masterTabs.on('initTab.oc.tab', this.proxy(this.onInitTab)) + $masterTabs.on('afterAllClosed.oc.tab', this.proxy(this.onAfterAllTabsClosed)) + + $(window).on('ajaxInvalidField', this.proxy(this.ajaxInvalidField)) + $document.on('open.oc.list', '#cms-side-panel', this.proxy(this.onOpenDocument)) + $document.on('ajaxUpdate', '[data-control=filelist], [data-control=assetlist]', this.proxy(this.onAjaxUpdate)) + $document.on('ajaxError', '#cms-master-tabs form', this.proxy(this.onAjaxError)) + $document.on('ajaxSuccess', '#cms-master-tabs form', this.proxy(this.onAjaxSuccess)) + $document.on('click', '#cms-side-panel form button[data-control=create-template], #cms-side-panel form li a[data-control=create-template]', this.proxy(this.onCreateTemplateClick)) + $document.on('click', '#cms-side-panel form button[data-control=delete-template]', this.proxy(this.onDeleteTemplateClick)) + $document.on('showing.oc.inspector', '[data-inspectable]', this.proxy(this.onInspectorShowing)) + $document.on('hidden.oc.inspector', '[data-inspectable]', this.proxy(this.onInspectorHidden)) + $document.on('hiding.oc.inspector', '[data-inspectable]', this.proxy(this.onInspectorHiding)) + $document.on('click', '#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist a.remove', this.proxy(this.onComponentRemove)) + $document.on('click', '#cms-component-list [data-component]', this.proxy(this.onComponentClick)) + } + + // EVENT HANDLERS + // ============================ + + CmsPage.prototype.onOpenDocument = function(event) { + /* + * Open a document when it's clicked in the sidebar + */ + + var $item = $(event.relatedTarget), + $form = $item.closest('[data-template-type]'), + data = { + type: $form.data('template-type'), + theme: $item.data('item-theme'), + path: $item.data('item-path') + }, + tabId = data.type + '-' + data.theme + '-' + data.path + + if (data.type == 'asset' && $item.data('editable') === undefined) + return true + + if ($form.length == 0) + return false + + /* + * Find if the tab is already opened + */ + if ($('#cms-master-tabs').data('oc.tab').goTo(tabId)) + return false + + /* + * Open a new tab + */ + $.oc.stripeLoadIndicator.show() + + $form.request('onOpenTemplate', { + data: data + }).done(function(data) { + $.oc.stripeLoadIndicator.hide() + var fileType = data.tabTitle.split('.').pop() + if (fileType == 'php' || fileType == 'js' || fileType == 'css' || fileType == 'html' || fileType == 'htm') fileType = 'icon-file-code-o' + else fileType = 'icon-file-text-o' + $('#cms-master-tabs').ocTab('addTab', data.tabTitle, data.tab, tabId, fileType) + }).always(function() { + $.oc.stripeLoadIndicator.hide() + }).fail(function(jqXHR, textStatus, errorThrown) { + alert(jqXHR.responseText.length ? jqXHR.responseText : jqXHR.statusText) + $.oc.stripeLoadIndicator.hide() + }) + + return false + } + + CmsPage.prototype.ajaxInvalidField = function(ev, element, name, messages, isFirst) { + /* + * Detect invalid fields, uncollapse the panel + */ + if (!isFirst) + return + + ev.preventDefault() + + var $el = $(element), + $panel = $el.closest('.form-tabless-fields.collapsed'), + $primaryPanel = $el.closest('.control-tabs.primary-tabs.collapsed') + + if ($panel.length > 0) + $panel.removeClass('collapsed') + + if ($primaryPanel.length > 0) { + $primaryPanel.removeClass('collapsed') + + var pane = $primaryPanel.closest('.tab-pane'), + $secondaryPanel = $('.control-tabs.secondary-tabs', pane) + + $secondaryPanel.removeClass('primary-collapsed') + } + + $el.focus() + } + + CmsPage.prototype.onTabClosed = function(ev) { + this.updateModifiedCounter() + + if ($('> div.tab-content > div.tab-pane', '#cms-master-tabs').length == 0) + this.setPageTitle('') + } + + CmsPage.prototype.onBeforeTabClose = function(ev) { + if ($.fn.table !== undefined) + $('[data-control=table]', ev.relatedTarget).table('dispose') + + $.oc.foundation.controlUtils.disposeControls(ev.relatedTarget.get(0)) + } + + CmsPage.prototype.onBeforeRequest = function(ev) { + var $form = $(ev.target) + + if ($('.components .layout-cell.error-component', $form).length > 0) { + if (!confirm('The form contains unknown components. Their properties will be lost on save. Do you want to save the form?')) + ev.preventDefault() + } + } + + CmsPage.prototype.onTabShown = function(ev) { + /* + * Listen for the tabs "shown" event to track the current template in the list + */ + + var $target = $(ev.target) + + if ($target.closest('[data-control=tab]').attr('id') != 'cms-master-tabs') + return + + var dataId = $target.closest('li').attr('data-tab-id'), + title = $target.attr('title'), + $sidePanel = $('#cms-side-panel') + + if (title) + this.setPageTitle(title) + + $sidePanel.find('[data-control=filelist]').fileList('markActive', dataId) + $sidePanel.find('form').trigger('oc.list.setActiveItem', [dataId]) + } + + CmsPage.prototype.onInitTab = function(ev, data) { + /* + * Listen for the tabs "initTab" event to inject extra controls to the tab + */ + + if ($(ev.target).attr('id') != 'cms-master-tabs') + return + + var $collapseIcon = $(''), + $panel = $('.form-tabless-fields', data.pane) + + $panel.append($collapseIcon); + + $collapseIcon.click(function() { + $panel.toggleClass('collapsed') + + if (typeof(localStorage) !== 'undefined') + localStorage.ocCmsTablessCollapsed = $panel.hasClass('collapsed') ? 1 : 0 + + window.setTimeout(function() { + $(window).trigger('oc.updateUi') + }, 500) + + return false + }) + + var $primaryCollapseIcon = $(''), + $primaryPanel = $('.control-tabs.primary-tabs', data.pane), + $secondaryPanel = $('.control-tabs.secondary-tabs', data.pane) + + if ($primaryPanel.length > 0) { + $secondaryPanel.append($primaryCollapseIcon); + + $primaryCollapseIcon.click(function() { + $primaryPanel.toggleClass('collapsed') + $secondaryPanel.toggleClass('primary-collapsed') + $(window).trigger('oc.updateUi') + if (typeof(localStorage) !== 'undefined') + localStorage.ocCmsPrimaryCollapsed = $primaryPanel.hasClass('collapsed') ? 1 : 0 + return false + }) + } + + if (typeof(localStorage) !== 'undefined') { + if (!$('a', data.tab).hasClass('new-template') && localStorage.ocCmsTablessCollapsed == 1) + $panel.addClass('collapsed') + + if (localStorage.ocCmsPrimaryCollapsed == 1) { + $primaryPanel.addClass('collapsed') + $secondaryPanel.addClass('primary-collapsed') + } + } + + var $componentListFormGroup = $('.control-componentlist', data.pane).closest('.form-group') + if ($primaryPanel.length > 0) + $primaryPanel.before($componentListFormGroup) + else + $secondaryPanel.parent().before($componentListFormGroup) + + $componentListFormGroup.removeClass() + $componentListFormGroup.addClass('layout-row min-size') + this.updateComponentListClass(data.pane) + this.updateFormEditorMode(data.pane, true) + + var $form = $('form', data.pane), + self = this + + $form.on('changed.oc.changeMonitor', function() { + $panel.trigger('modified.oc.tab') + self.updateModifiedCounter() + }) + + $form.on('unchanged.oc.changeMonitor', function() { + $panel.trigger('unmodified.oc.tab') + self.updateModifiedCounter() + }) + + this.addTokenExpanderToEditor(data.pane, $form) + } + + CmsPage.prototype.onAfterAllTabsClosed = function(ev) { + var $sidePanel = $('#cms-side-panel') + + $sidePanel.find('[data-control=filelist]').fileList('markActive', null) + $sidePanel.find('form').trigger('oc.list.setActiveItem', [null]) + } + + CmsPage.prototype.onAjaxUpdate = function(ev) { + var dataId = $('#cms-master-tabs .nav-tabs li.active').attr('data-tab-id'), + $sidePanel = $('#cms-side-panel') + + $sidePanel.find('[data-control=filelist]').fileList('markActive', dataId) + $sidePanel.find('form').trigger('oc.list.setActiveItem', [dataId]) + } + + CmsPage.prototype.onAjaxSuccess = function(ev, context, data) { + var element = ev.target + + if (data.templatePath !== undefined) { + $('input[name=templatePath]', element).val(data.templatePath) + $('input[name=templateMtime]', element).val(data.templateMtime) + $('[data-control=delete-button]', element).removeClass('hide') + $('[data-control=preview-button]', element).removeClass('hide') + + if (data.pageUrl !== undefined) + $('[data-control=preview-button]', element).attr('href', data.pageUrl) + } + + if (data.tabTitle !== undefined) { + $('#cms-master-tabs').ocTab('updateTitle', $(element).closest('.tab-pane'), data.tabTitle) + this.setPageTitle(data.tabTitle) + } + + var tabId = $('input[name=templateType]', element).val() + '-' + + $('input[name=theme]', element).val() + '-' + + $('input[name=templatePath]', element).val(); + + $('#cms-master-tabs').ocTab('updateIdentifier', $(element).closest('.tab-pane'), tabId) + + var templateType = $('input[name=templateType]', element).val() + if (templateType.length > 0) { + $.oc.cmsPage.updateTemplateList(templateType) + + if (templateType == 'layout') + this.updateLayouts(element) + } + + this.updateFormEditorMode($(element).closest('.tab-pane'), false) + + if (context.handler == 'onSave' && (!data['X_OCTOBER_ERROR_FIELDS'] && !data['X_OCTOBER_ERROR_MESSAGE'])) { + $(element).trigger('unchange.oc.changeMonitor') + } + } + + CmsPage.prototype.onAjaxError = function(ev, context, message, data, jqXHR) { + if (context.handler == 'onSave') { + if (jqXHR.responseText == 'mtime-mismatch') { + ev.preventDefault() + this.handleMtimeMismatch(ev.target) + } + } + } + + CmsPage.prototype.onCreateTemplateClick = function(ev) { + var $form = $(ev.target).closest('[data-template-type]'), + type = $form.data('template-type'), + tabId = type + Math.random(), + self = this + + $.oc.stripeLoadIndicator.show() + + $form.request('onCreateTemplate', { + data: {type: type} + }).done(function(data) { + $('#cms-master-tabs').ocTab('addTab', data.tabTitle, data.tab, tabId, $form.data('type-icon') + ' new-template') + $('#layout-side-panel').trigger('close.oc.sidePanel') + self.setPageTitle(data.tabTitle) + }).always(function() { + $.oc.stripeLoadIndicator.hide() + }) + } + + CmsPage.prototype.onDeleteTemplateClick = function(ev) { + var $el = $(ev.currentTarget), + $form = $el.closest('form'), + templateType = $form.data('template-type'), + self = this + + if (!confirm($el.data('confirmation'))) + return + + $.oc.stripeLoadIndicator.show() + + $form.request('onDeleteTemplates', { + data: {type: templateType} + }).done(function(data) { + var tabs = $('#cms-master-tabs').data('oc.tab'); + $.each(data.deleted, function(index, path){ + var + tabId = templateType + '-' + data.theme + '-' + path, + tab = tabs.findByIdentifier(tabId) + + $('#cms-master-tabs').ocTab('closeTab', tab, true) + }) + + if (data.error !== undefined && $.type(data.error) === 'string' && data.error.length) + $.oc.flashMsg({text: data.error, 'class': 'error'}) + }).always(function() { + self.updateTemplateList(templateType) + $.oc.stripeLoadIndicator.hide() + }) + } + + CmsPage.prototype.onInspectorShowing = function(ev, data) { + $(ev.currentTarget).closest('[data-control="toolbar"]').data('oc.dragScroll').goToElement(ev.currentTarget, data.callback) + + ev.stopPropagation() + } + + CmsPage.prototype.onInspectorHidden = function(ev) { + var element = ev.target, + values = $.parseJSON($('[data-inspector-values]', element).val()) + + $('[name="component_aliases[]"]', element).val(values['oc.alias']) + $('span.alias', element).text(values['oc.alias']) + } + + CmsPage.prototype.onInspectorHiding = function(ev, values) { + var element = ev.target, + values = $.parseJSON($('[data-inspector-values]', element).val()), + alias = values['oc.alias'], + $componentList = $('#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist .layout'), + $cell = $(ev.target).parent() + + $('div.layout-cell', $componentList).each(function() { + if ($cell.get(0) == this) + return true + + var $input = $('input[name="component_aliases[]"]', this) + + if ($input.val() == alias) { + ev.preventDefault() + alert('The component alias "'+alias+'" is already used.') + return false + } + }) + } + + CmsPage.prototype.onComponentRemove = function(ev) { + var element = ev.currentTarget + + $(element).trigger('change') + var pane = $(element).closest('.tab-pane'), + component = $(element).closest('div.layout-cell') + + /* + * Remove any {% component %} tags in the editor for this component + */ + var editor = $('[data-control=codeeditor]', pane) + if (editor.length) { + var alias = $('input[name="component_aliases[]"]', component).val(), + codeEditor = editor.codeEditor('getEditorObject') + + codeEditor.replace('', { + needle: "{% component '" + alias + "' %}" + }) + } + + component.remove() + $(window).trigger('oc.updateUi') + + this.updateComponentListClass(pane) + return false + } + + CmsPage.prototype.onComponentClick = function(ev) { + /* + * Determine if a page or layout is open in the master tabs + */ + + var $componentList = $('#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist .layout') + if ($componentList.length == 0) { + alert('Components can be added only to pages, partials and layouts.') + return; + } + + var $component = $(ev.currentTarget).clone(), + $iconInput = $component.find('[data-component-icon]'), + $componentContainer = $('.layout-relative', $component), + $configInput = $component.find('[data-inspector-config]'), + $aliasInput = $component.find('[data-component-default-alias]'), + $valuesInput = $component.find('[data-inspector-values]'), + $nameInput = $component.find('[data-component-name]'), + $classInput = $component.find('[data-inspector-class]'), + alias = $aliasInput.val(), + originalAlias = alias, + counter = 2, + existingAliases = [] + + $('div.layout-cell input[name="component_aliases[]"]', $componentList).each(function() { + existingAliases.push($(this).val()) + }) + + while($.inArray(alias, existingAliases) !== -1) { + alias = originalAlias + counter + counter++ + } + + // Set the last alias used so dragComponents can use it + $('input[name="component_aliases[]"]', $(ev.currentTarget)).val(alias) + + $component.attr('data-component-attached', true) + $componentContainer.addClass($iconInput.val()) + $iconInput.remove() + + $componentContainer.attr({ + 'data-inspectable': '', + 'data-inspector-title': $component.find('span.name').text(), + 'data-inspector-description': $component.find('span.description').text(), + 'data-inspector-config': $configInput.val(), + 'data-inspector-class': $classInput.val() + }) + + $configInput.remove() + $('input[name="component_names[]"]', $component).val($nameInput.val()) + $nameInput.remove() + $('input[name="component_aliases[]"]', $component).val(alias) + $component.find('span.alias').text(alias) + $valuesInput.val($valuesInput.val().replace('--alias--', alias)) + $aliasInput.remove() + + $component.addClass('adding') + $componentList.append($component) + $componentList.closest('[data-control="toolbar"]').data('oc.dragScroll').goToElement($component) + $component.removeClass('adding') + $component.trigger('change') + + this.updateComponentListClass($component.closest('.tab-pane')) + + $(window).trigger('oc.updateUi') + } + + // INTERNAL METHODS + // ============================ + + CmsPage.prototype.updateComponentListClass = function(pane) { + var $componentList = $('.control-componentlist', pane), + $primaryPanel = $('.control-tabs.primary-tabs', pane), + $primaryTabContainer = $('.nav-tabs', $primaryPanel), + hasComponents = $('.layout', $componentList).children(':not(.hidden)').length > 0 + + $primaryTabContainer.toggleClass('component-area', hasComponents) + $componentList.toggleClass('has-components', hasComponents) + } + + CmsPage.prototype.updateFormEditorMode = function(pane, initialization) { + var $contentTypeElement = $('[data-toolbar-type]', pane) + if ($contentTypeElement.length == 0) + return + + if ($contentTypeElement.data('toolbar-type') != 'content') + return + + var fileName = $('input[name=fileName]', pane).val(), + parts = fileName.split('.'), + extension = 'txt', + mode = 'plain_text', + modes = { css: "css", htm: "html", html: "html", js: "javascript", less: "less", md: "markdown", sass: "sass", scss: "scss", txt: "plain_text", yaml: "yaml", php: "php" }, + editor = $('[data-control=codeeditor]', pane) + + if (parts.length >= 2) + extension = parts.pop().toLowerCase() + + if (modes[extension] !== undefined) + mode = modes[extension]; + + var setEditorMode = function() { + window.setTimeout(function() { + editor.data('oc.codeEditor').editor.getSession().setMode({path: 'ace/mode/'+mode}) + }, 200) + } + + if (initialization) + editor.on('oc.codeEditorReady', setEditorMode) + else + setEditorMode() + } + + CmsPage.prototype.updateModifiedCounter = function() { + var counters = { + page: { menu: 'pages', count: 0 }, + partial: { menu: 'partials', count: 0 }, + layout: { menu: 'layouts', count: 0 }, + content: { menu: 'content', count: 0 }, + asset: { menu: 'assets', count: 0} + } + + $('> div.tab-content > div.tab-pane[data-modified]', '#cms-master-tabs').each(function() { + var inputType = $('> form > input[name=templateType]', this).val() + counters[inputType].count++ + }) + + $.each(counters, function(type, data){ + $.oc.sideNav.setCounter('cms/' + data.menu, data.count); + }) + } + + CmsPage.prototype.addTokenExpanderToEditor = function(pane, $form) { + var group = $('[data-field-name=markup]', pane), + editor = $('[data-control=codeeditor]', group), + canExpand = false, + self = this + + if (!editor.length || editor.data('oc.tokenexpander')) + return + + var toolbar = editor.codeEditor('getToolbar') + + editor.tokenExpander() + + var breakButton = $('
  • ').prop({ 'class': 'tokenexpander-button' }).append( + $('').prop({ 'href': 'javascript:; '}).append( + $('').prop({ 'class': 'icon-code-fork' }) + ) + ) + + breakButton.hide().on('click', function() { + self.handleExpandToken(editor, $form) + return false + }) + + $('ul:first', toolbar).prepend(breakButton) + + editor + .on('show.oc.tokenexpander', function() { + canExpand = true + breakButton.show() + }) + .on('hide.oc.tokenexpander', function() { + canExpand = false + breakButton.hide() + }) + .on('dblclick', function(ev){ + if ((ev.metaKey || ev.ctrlKey) && canExpand) { + self.handleExpandToken(editor, $form) + } + }) + } + + CmsPage.prototype.handleExpandToken = function(editor, $form) { + editor.tokenExpander('expandToken', function(token, value){ + return $form.request('onExpandMarkupToken', { + data: { tokenType: token, tokenName: value } + }) + }) + } + + CmsPage.prototype.handleMtimeMismatch = function(form) { + var $form = $(form) + $form.popup({ handler: 'onOpenConcurrencyResolveForm' }) + + var popup = $form.data('oc.popup'), + self = this + + $(popup.$content).on('click', 'button[data-action=reload]', function() { + popup.hide() + self.reloadForm(form) + }) + + $(popup.$content).on('click', 'button[data-action=save]', function() { + popup.hide() + + $('input[name=templateForceSave]', $form).val(1) + $('a[data-request=onSave]', $form).trigger('click') + $('input[name=templateForceSave]', $form).val(0) + }) + } + + CmsPage.prototype.reloadForm = function(form) { + var + $form = $(form), + data = { + type: $('[name=templateType]', $form).val(), + theme: $('[name=theme]', $form).val(), + path: $('[name=templatePath]', $form).val(), + }, + tabId = data.type + '-' + data.theme + '-' + data.path, + tabs = $('#cms-master-tabs').data('oc.tab'), + tab = tabs.findByIdentifier(tabId), + self = this + + /* + * Update tab + */ + + $.oc.stripeLoadIndicator.show() + + $form.request('onOpenTemplate', { + data: data + }).done(function(data) { + $('#cms-master-tabs').ocTab('updateTab', tab, data.tabTitle, data.tab) + $('#cms-master-tabs').ocTab('unmodifyTab', tab) + self.updateModifiedCounter() + }).always(function() { + $.oc.stripeLoadIndicator.hide() + }).fail(function(jqXHR, textStatus, errorThrown) { + alert(jqXHR.responseText.length ? jqXHR.responseText : jqXHR.statusText) + }) + } + + CmsPage.prototype.setPageTitle = function(title) { + if (title.length) + $.oc.layout.setPageTitle(title + ' | ') + else + $.oc.layout.setPageTitle(title) + } + + CmsPage.prototype.updateLayouts = function(form) { + $(form).request('onGetTemplateList', { + success: function(data) { + $('#cms-master-tabs > .tab-content select[name="settings[layout]"]').each(function() { + var + $select = $(this), + value = $select.val() + + $select.find('option').remove() + $.each(data.layouts, function(layoutFile, layoutName){ + $select.append($('
    + + + + + + + + + + +
    diff --git a/plugins/indikator/devtools/controllers/editor/_form_page.htm b/plugins/indikator/devtools/controllers/editor/_form_page.htm new file mode 100644 index 0000000..918a0ae --- /dev/null +++ b/plugins/indikator/devtools/controllers/editor/_form_page.htm @@ -0,0 +1,15 @@ + 'layout', + 'data-change-monitor' => 'true', + 'data-window-close-confirm' => e(trans('backend::lang.form.confirm_tab_close')), + 'data-inspector-external-parameters' => true + ]) ?> + render() ?> + + + + + + + + diff --git a/plugins/indikator/devtools/controllers/editor/_sidepanel.htm b/plugins/indikator/devtools/controllers/editor/_sidepanel.htm new file mode 100644 index 0000000..eb4f705 --- /dev/null +++ b/plugins/indikator/devtools/controllers/editor/_sidepanel.htm @@ -0,0 +1,14 @@ +
    +
    +
    +
    + widget->assetList->render() ?> +
    +
    +
    +
    diff --git a/plugins/indikator/devtools/controllers/editor/index.htm b/plugins/indikator/devtools/controllers/editor/index.htm new file mode 100644 index 0000000..dd35315 --- /dev/null +++ b/plugins/indikator/devtools/controllers/editor/index.htm @@ -0,0 +1,30 @@ + + fatalError): ?> + makePartial('sidepanel') ?> + + + + + fatalError): ?> +
    + +
    +
    + +
    +
    + +
    +
    + +

    fatalError)) ?>

    + + diff --git a/plugins/indikator/devtools/formwidgets/Help.php b/plugins/indikator/devtools/formwidgets/Help.php new file mode 100644 index 0000000..ddf69a6 --- /dev/null +++ b/plugins/indikator/devtools/formwidgets/Help.php @@ -0,0 +1,30 @@ +fillFromConfig([ + 'content' + ]); + } + + public function render() + { + $this->prepareVars(); + + return $this->makePartial('help'); + } + + protected function prepareVars() + { + $this->vars['content'] = $this->content; + } +} diff --git a/plugins/indikator/devtools/formwidgets/help/partials/_help.htm b/plugins/indikator/devtools/formwidgets/help/partials/_help.htm new file mode 100644 index 0000000..39b2c47 --- /dev/null +++ b/plugins/indikator/devtools/formwidgets/help/partials/_help.htm @@ -0,0 +1,88 @@ + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    + diff --git a/plugins/indikator/devtools/lang/en/lang.php b/plugins/indikator/devtools/lang/en/lang.php new file mode 100644 index 0000000..5c16666 --- /dev/null +++ b/plugins/indikator/devtools/lang/en/lang.php @@ -0,0 +1,72 @@ + [ + 'name' => 'Developer Tools', + 'description' => 'Useful features for developers.', + 'author' => 'Gergő Szabó' + ], + 'editor' => [ + 'menu_label' => 'Code editor', + 'plugins' => 'Plugins', + 'permission' => 'Use the code editor' + ], + 'help' => [ + 'menu_label' => 'Developer Tools', + 'menu_description' => 'Settings of developer features.', + 'tab' => 'Help', + 'cms' => 'CMS', + 'pages' => 'Pages', + 'partials' => 'Partials', + 'layouts' => 'Layouts', + 'content' => 'Content', + 'ajax' => 'AJAX', + 'functions' => 'Functions', + 'tags' => 'Tags', + 'filters' => 'Filters', + 'database' => 'Database', + 'basic' => 'Basic', + 'queries' => 'Queries', + 'plugins' => 'Plugins', + 'registration' => 'Registration', + 'version_history' => 'Version history', + 'building_components' => 'Building components', + 'settings_config' => 'Settings & Config', + 'localization' => 'Localization', + 'task_scheduling' => 'Task scheduling', + 'extending_plugins' => 'Extending plugins', + 'backend' => 'Backend', + 'controllers_ajax' => 'Controllers & AJAX', + 'views_partials' => 'Views & Partials', + 'widgets' => 'Widgets', + 'forms' => 'Forms', + 'lists' => 'Lists', + 'relations' => 'Relations', + 'sorting_records' => 'Sorting records', + 'importing_exporting' => 'Importing & Exporting', + 'users_permissions' => 'Users & Permissions', + 'user_interface_guide' => 'User interface guide', + 'services' => 'Services', + 'application' => 'Application', + 'behaviors' => 'Behaviors', + 'events' => 'Events', + 'forms_html' => 'Forms & Html', + 'mail' => 'Mail', + 'request_input' => 'Request & Input', + 'response_view' => 'Response & View', + 'router' => 'Router', + 'session' => 'Session', + 'validation' => 'Validation', + 'permission' => 'Manage settings' + ], + 'form' => [ + 'wysiwyg_label' => 'Wysiwyg Editor', + 'wysiwyg_enabled' => 'Enabled on the Content page', + 'help_label' => 'Help', + 'help_enabled' => 'Enable the Help tab on the CMS pages', + 'select_none' => '-- None --', + 'select_superuser' => 'Show it for the Superusers', + 'select_admingroup' => 'Show it for the following admin group', + 'select_adminid' => 'Show it for the following administrator', + ] +]; diff --git a/plugins/indikator/devtools/lang/hu/lang.php b/plugins/indikator/devtools/lang/hu/lang.php new file mode 100644 index 0000000..1619fc6 --- /dev/null +++ b/plugins/indikator/devtools/lang/hu/lang.php @@ -0,0 +1,72 @@ + [ + 'name' => 'Fejlesztői eszközök', + 'description' => 'Hasznos szolgáltatások fejlesztőknek.', + 'author' => 'Szabó Gergő' + ], + 'editor' => [ + 'menu_label' => 'Kódszerkesztő', + 'plugins' => 'Bővítmények', + 'permission' => 'Szerkesztő használata' + ], + 'help' => [ + 'menu_label' => 'Fejlesztőknek', + 'menu_description' => 'Szolgáltatások és lehetőségek beállítása.', + 'tab' => 'Súgó', + 'cms' => 'Testreszabás', + 'pages' => 'Lapok', + 'partials' => 'Részlapok', + 'layouts' => 'Elrendezések', + 'content' => 'Tartalom', + 'ajax' => 'AJAX', + 'functions' => 'Függvények', + 'tags' => 'Tag-ek', + 'filters' => 'Filterek', + 'database' => 'Adatbázis', + 'basic' => 'Alapok', + 'queries' => 'Lekérdezések', + 'plugins' => 'Bővítmények', + 'registration' => 'Regisztrálás', + 'version_history' => 'Verzió előzmények', + 'building_components' => 'Komponensek', + 'settings_config' => 'Beállítások', + 'localization' => 'Többnyelvűsítés', + 'task_scheduling' => 'Időzítés', + 'extending_plugins' => 'Kiegészítés', + 'backend' => 'Admin', + 'controllers_ajax' => 'Kontroller és AJAX', + 'views_partials' => 'Részlapok', + 'widgets' => 'Widgetek', + 'forms' => 'Űrlapok', + 'lists' => 'Listák', + 'relations' => 'Kapcsolatok', + 'sorting_records' => 'Elemek rendezése', + 'importing_exporting' => 'Import és export', + 'users_permissions' => 'Jogosultságok', + 'user_interface_guide' => 'Felhasználói felület', + 'services' => 'Szolgáltatások', + 'application' => 'Alkalmazás', + 'behaviors' => 'Viselkedések', + 'events' => 'Események', + 'forms_html' => 'Űrlapok és HTML', + 'mail' => 'Levelezés', + 'request_input' => 'Űrlap értékek', + 'response_view' => 'Válasz megjelenítés', + 'router' => 'Router kezelés', + 'session' => 'Munkamenet', + 'validation' => 'Ellenőrzés', + 'permission' => 'Beállítások kezelése' + ], + 'form' => [ + 'wysiwyg_label' => 'Szövegszerkesztő', + 'wysiwyg_enabled' => 'Engedélyezés a Tartalom oldalon', + 'help_label' => 'Súgó', + 'help_enabled' => 'Engedélyezés a Testreszabás aloldalakon', + 'select_none' => '-- nincs --', + 'select_superuser' => 'Szuperadminok láthatják', + 'select_admingroup' => 'A következő admin csoport tagjai láthatják', + 'select_adminid' => 'A következő admin felhasználó láthatja' + ] +]; diff --git a/plugins/indikator/devtools/models/Settings.php b/plugins/indikator/devtools/models/Settings.php new file mode 100644 index 0000000..879b2a8 --- /dev/null +++ b/plugins/indikator/devtools/models/Settings.php @@ -0,0 +1,63 @@ + 'indikator.devtools::lang.form.select_none']; + + public function getHelpAdmingroupOptions() + { + $result = $this->selectList; + $sql = Db::table('backend_user_groups')->orderBy('name', 'asc')->get()->all(); + + foreach ($sql as $item) { + $result[$item->id] = $item->name.' ('.Db::table('backend_users_groups')->where('user_group_id', $item->id)->count().')'; + } + + return $result; + } + + public function getHelpAdminidOptions() + { + $result = $this->selectList; + $sql = Db::table('backend_users')->orderBy('login', 'asc')->get()->all(); + + foreach ($sql as $item) { + $result[$item->id] = $item->login.' ('.$item->email.')'; + } + + return $result; + } + + public function getWysiwygAdmingroupOptions() + { + $result = $this->selectList; + $sql = Db::table('backend_user_groups')->orderBy('name', 'asc')->get()->all(); + + foreach ($sql as $item) { + $result[$item->id] = $item->name.' ('.Db::table('backend_users_groups')->where('user_group_id', $item->id)->count().')'; + } + + return $result; + } + + public function getWysiwygAdminidOptions() + { + $result = $this->selectList; + $sql = Db::table('backend_users')->orderBy('login', 'asc')->get()->all(); + + foreach ($sql as $item) { + $result[$item->id] = $item->login.' ('.$item->email.')'; + } + + return $result; + } +} diff --git a/plugins/indikator/devtools/models/settings/fields.yaml b/plugins/indikator/devtools/models/settings/fields.yaml new file mode 100644 index 0000000..001c764 --- /dev/null +++ b/plugins/indikator/devtools/models/settings/fields.yaml @@ -0,0 +1,85 @@ +# =================================== +# Field Definitions +# =================================== + +fields: + + help_title: + label: indikator.devtools::lang.form.help_label + type: section + comment: '' + span: left + + wysiwyg_title: + label: indikator.devtools::lang.form.wysiwyg_label + type: section + comment: '' + span: right + + help_enabled: + label: indikator.devtools::lang.form.help_enabled + type: switch + default: false + span: left + + wysiwyg_enabled: + label: indikator.devtools::lang.form.wysiwyg_enabled + type: switch + default: false + span: right + + help_superuser: + label: indikator.devtools::lang.form.select_superuser + type: switch + default: false + trigger: + action: show + field: help_enabled + condition: checked + span: left + + wysiwyg_superuser: + label: indikator.devtools::lang.form.select_superuser + type: switch + default: false + trigger: + action: show + field: wysiwyg_enabled + condition: checked + span: right + + help_admingroup: + label: indikator.devtools::lang.form.select_admingroup + type: dropdown + trigger: + action: show + field: help_enabled + condition: checked + span: left + + wysiwyg_admingroup: + label: indikator.devtools::lang.form.select_admingroup + type: dropdown + trigger: + action: show + field: wysiwyg_enabled + condition: checked + span: right + + help_adminid: + label: indikator.devtools::lang.form.select_adminid + type: dropdown + trigger: + action: show + field: help_enabled + condition: checked + span: left + + wysiwyg_adminid: + label: indikator.devtools::lang.form.select_adminid + type: dropdown + trigger: + action: show + field: wysiwyg_enabled + condition: checked + span: right diff --git a/plugins/indikator/devtools/routes.php b/plugins/indikator/devtools/routes.php new file mode 100644 index 0000000..5bb4d11 --- /dev/null +++ b/plugins/indikator/devtools/routes.php @@ -0,0 +1,10 @@ +is_superuser == 1) { + echo phpinfo(); + } +}); diff --git a/plugins/indikator/devtools/updates/version.yaml b/plugins/indikator/devtools/updates/version.yaml new file mode 100644 index 0000000..821eae6 --- /dev/null +++ b/plugins/indikator/devtools/updates/version.yaml @@ -0,0 +1,17 @@ +1.0.0: First version of Developer Tools. +1.1.0: Edit plugins with the code editor. +1.1.1: Translate some English texts. +1.1.2: Fixed the Create file issue. +1.1.3: Added new icon for main navigation. +1.1.4: Show the PHP's configuration. +1.1.5: Minor code improvements and bugfix. +1.1.6: The top menu icon shows again. +1.1.7: Fixed the Create folder issue. +1.1.8: "!!! Updated for October 420+." +1.1.9: + - Updated the main navigation icon. + - Added last modified date. +1.2.0: The syntax highlighting works again! +1.2.1: Help links open in a new window. +1.2.2: Fixed the dependency bug in asset list. +1.2.3: The file delete operation works again. diff --git a/plugins/indikator/devtools/widgets/AssetList.php b/plugins/indikator/devtools/widgets/AssetList.php new file mode 100644 index 0000000..f0380de --- /dev/null +++ b/plugins/indikator/devtools/widgets/AssetList.php @@ -0,0 +1,709 @@ +alias = $alias; + $this->theme = Theme::getEditTheme(); + $this->selectionInputName = 'file'; + $this->assetExtensions = FileDefinitions::get('assetExtensions'); + + parent::__construct($controller, []); + $this->bindToController(); + + $this->checkUploadPostback(); + } + + /** + * @inheritDoc + */ + protected function loadAssets() + { + $this->addCss('css/assetlist.css'); + $this->addJs('js/assetlist.js'); + } + + /** + * Renders the widget. + * @return string + */ + public function render() + { + return $this->makePartial('body', [ + 'data' => $this->getData() + ]); + } + + // + // Event handlers + // + + public function onOpenDirectory() + { + $path = Input::get('path'); + if (!$this->validatePath($path)) { + throw new ApplicationException(Lang::get('cms::lang.asset.invalid_path')); + } + + $delay = Input::get('delay'); + if ($delay) { + usleep(1000000 * $delay); + } + + $this->putSession('currentPath', $path); + return [ + '#'.$this->getId('asset-list') => $this->makePartial('items', ['items' => $this->getData()]) + ]; + } + + public function onRefresh() + { + return [ + '#'.$this->getId('asset-list') => $this->makePartial('items', ['items' => $this->getData()]) + ]; + } + + public function onUpdate() + { + $this->extendSelection(); + + return $this->onRefresh(); + } + + public function onDeleteFiles() + { + $fileList = Request::input('file'); + $error = null; + $deleted = []; + + try { + $assetsPath = $this->getAssetsPath(); + + foreach ($fileList as $path => $selected) { + if ($selected) { + if (!$this->validatePath($path)) { + throw new ApplicationException(Lang::get('cms::lang.asset.invalid_path')); + } + + $fullPath = $assetsPath.'/'.$path; + if (File::exists($fullPath)) { + if (!File::isDirectory($fullPath)) { + if (!@File::delete($fullPath)) { + throw new ApplicationException(Lang::get( + 'cms::lang.asset.error_deleting_file', + ['name' => $path] + )); + } + } + else { + $empty = File::isDirectoryEmpty($fullPath); + if ($empty === false) { + throw new ApplicationException(Lang::get( + 'cms::lang.asset.error_deleting_dir_not_empty', + ['name' => $path] + )); + } + + if (!@rmdir($fullPath)) { + throw new ApplicationException(Lang::get( + 'cms::lang.asset.error_deleting_dir', + ['name' => $path] + )); + } + } + + $deleted[] = $path; + $this->removeSelection($path); + } + } + } + } + catch (Exception $ex) { + $error = $ex->getMessage(); + } + + return [ + 'deleted' => $deleted, + 'error' => $error + ]; + } + + public function onLoadRenamePopup() + { + $path = Input::get('renamePath'); + if (!$this->validatePath($path)) { + throw new ApplicationException(Lang::get('cms::lang.asset.invalid_path')); + } + + $this->vars['originalPath'] = $path; + $this->vars['name'] = basename($path); + + return $this->makePartial('rename_form'); + } + + public function onApplyName() + { + $newName = trim(Input::get('name')); + if (!strlen($newName)) { + throw new ApplicationException(Lang::get('cms::lang.asset.name_cant_be_empty')); + } + + if (!$this->validatePath($newName)) { + throw new ApplicationException(Lang::get('cms::lang.asset.invalid_path')); + } + + if (!$this->validateName($newName)) { + throw new ApplicationException(Lang::get('cms::lang.asset.invalid_name')); + } + + $originalPath = Input::get('originalPath'); + if (!$this->validatePath($originalPath)) { + throw new ApplicationException(Lang::get('cms::lang.asset.invalid_path')); + } + + $originalFullPath = $this->getFullPath($originalPath); + if (!file_exists($originalFullPath)) { + throw new ApplicationException(Lang::get('cms::lang.asset.original_not_found')); + } + + if (!is_dir($originalFullPath) && !$this->validateFileType($newName)) { + throw new ApplicationException(Lang::get( + 'cms::lang.asset.type_not_allowed', + ['allowed_types' => implode(', ', $this->assetExtensions)] + )); + } + + $newFullPath = $this->getFullPath(dirname($originalPath).'/'.$newName); + if (file_exists($newFullPath) && $newFullPath !== $originalFullPath) { + throw new ApplicationException(Lang::get('cms::lang.asset.already_exists')); + } + + if (!@rename($originalFullPath, $newFullPath)) { + throw new ApplicationException(Lang::get('cms::lang.asset.error_renaming')); + } + + return [ + '#'.$this->getId('asset-list') => $this->makePartial('items', ['items' => $this->getData()]) + ]; + } + + public function onLoadNewDirPopup() + { + return $this->makePartial('new_dir_form'); + } + + public function onNewDirectory() + { + $newName = trim(Input::get('name')); + if (!strlen($newName)) { + throw new ApplicationException(Lang::get('cms::lang.asset.name_cant_be_empty')); + } + + if (!$this->validatePath($newName)) { + throw new ApplicationException(Lang::get('cms::lang.asset.invalid_path')); + } + + if (!$this->validateName($newName)) { + throw new ApplicationException(Lang::get('cms::lang.asset.invalid_name')); + } + + $newFullPath = $this->getCurrentPath().'/'.$newName; + if (file_exists($newFullPath)) { + throw new ApplicationException(Lang::get('cms::lang.asset.already_exists')); + } + + if (!File::makeDirectory($newFullPath)) { + throw new ApplicationException(Lang::get( + 'cms::lang.cms_object.error_creating_directory', + ['name' => $newName] + )); + } + + return [ + '#'.$this->getId('asset-list') => $this->makePartial('items', ['items' => $this->getData()]) + ]; + } + + public function onLoadMovePopup() + { + $fileList = Request::input('file'); + $directories = []; + + $selectedList = array_filter($fileList, function ($value) { + return $value == 1; + }); + + $this->listDestinationDirectories($directories, $selectedList); + + $this->vars['directories'] = $directories; + $this->vars['selectedList'] = base64_encode(json_encode(array_keys($selectedList))); + + return $this->makePartial('move_form'); + } + + public function onMove() + { + $selectedList = Input::get('selectedList'); + if (!strlen($selectedList)) { + throw new ApplicationException(Lang::get('cms::lang.asset.selected_files_not_found')); + } + + $destinationDir = Input::get('dest'); + if (!strlen($destinationDir)) { + throw new ApplicationException(Lang::get('cms::lang.asset.select_destination_dir')); + } + + $destinationFullPath = $this->getFullPath($destinationDir); + if (!file_exists($destinationFullPath) || !is_dir($destinationFullPath)) { + throw new ApplicationException(Lang::get('cms::lang.asset.destination_not_found')); + } + + $list = @json_decode(@base64_decode($selectedList)); + if ($list === false) { + throw new ApplicationException(Lang::get('cms::lang.asset.selected_files_not_found')); + } + + foreach ($list as $path) { + if (!$this->validatePath($path)) { + throw new ApplicationException(Lang::get('cms::lang.asset.invalid_path')); + } + + $basename = basename($path); + $originalFullPath = $this->getFullPath($path); + $newFullPath = rtrim($destinationFullPath, '/').'/'.$basename; + $safeDir = $this->getAssetsPath(); + + if ($originalFullPath == $newFullPath) { + continue; + } + + if (is_file($originalFullPath)) { + if (!@File::move($originalFullPath, $newFullPath)) { + throw new ApplicationException(Lang::get( + 'cms::lang.asset.error_moving_file', + ['file' => $basename] + )); + } + } + elseif (is_dir($originalFullPath)) { + if (!@File::copyDirectory($originalFullPath, $newFullPath)) { + throw new ApplicationException(Lang::get( + 'cms::lang.asset.error_moving_directory', + ['dir' => $basename] + )); + } + + if (strpos($originalFullPath, '../') !== false) { + throw new ApplicationException(Lang::get( + 'cms::lang.asset.error_deleting_directory', + ['dir' => $basename] + )); + } + + if (strpos($originalFullPath, $safeDir) !== 0) { + throw new ApplicationException(Lang::get( + 'cms::lang.asset.error_deleting_directory', + ['dir' => $basename] + )); + } + + if (!@File::deleteDirectory($originalFullPath)) { + throw new ApplicationException(Lang::get( + 'cms::lang.asset.error_deleting_directory', + ['dir' => $basename] + )); + } + } + } + + return [ + '#'.$this->getId('asset-list') => $this->makePartial('items', ['items' => $this->getData()]) + ]; + } + + public function onSearch() + { + $this->setSearchTerm(Input::get('search')); + $this->extendSelection(); + + return $this->onRefresh(); + } + + /* + * Methods for the internal use + */ + + protected function getData() + { + $assetsPath = $this->getAssetsPath(); + + if (!file_exists($assetsPath) || !is_dir($assetsPath)) { + if (!File::makeDirectory($assetsPath)) { + throw new ApplicationException(Lang::get( + 'cms::lang.cms_object.error_creating_directory', + ['name' => $assetsPath] + )); + } + } + + $searchTerm = Str::lower($this->getSearchTerm()); + + if (!strlen($searchTerm)) { + $currentPath = $this->getCurrentPath(); + return $this->getDirectoryContents( + new DirectoryIterator($currentPath) + ); + } + + return $this->findFiles(); + } + + protected function getAssetsPath() + { + return base_path().'/plugins'; + } + + protected function getThemeFileUrl($path) + { + return Url::to('plugins'.$path); + } + + public function getCurrentRelativePath() + { + $path = $this->getSession('currentPath', '/'); + + if (!$this->validatePath($path)) { + return null; + } + + if ($path == '.') { + return null; + } + + return ltrim($path, '/'); + } + + protected function getCurrentPath() + { + $assetsPath = $this->getAssetsPath(); + + $path = $assetsPath.'/'.$this->getCurrentRelativePath(); + if (!is_dir($path)) { + return $assetsPath; + } + + return $path; + } + + protected function getRelativePath($path) + { + $prefix = $this->getAssetsPath(); + + if (substr($path, 0, strlen($prefix)) == $prefix) { + $path = substr($path, strlen($prefix)); + } + + return $path; + } + + protected function getFullPath($path) + { + return $this->getAssetsPath().'/'.ltrim($path, '/'); + } + + protected function validatePath($path) + { + if (!preg_match('/^[0-9a-z\.\s_\-\/]+$/i', $path)) { + return false; + } + + if (strpos($path, '..') !== false || strpos($path, './') !== false) { + return false; + } + + return true; + } + + protected function validateName($name) + { + if (!preg_match('/^[0-9a-z\.\s_\-]+$/i', $name)) { + return false; + } + + if (strpos($name, '..') !== false) { + return false; + } + + return true; + } + + protected function getDirectoryContents($dir) + { + $editableAssetTypes = Asset::getEditableExtensions(); + + $result = $files = []; + + foreach ($dir as $node) { + if (substr($node->getFileName(), 0, 1) == '.') { + continue; + } + + if ($node->isDir() && !$node->isDot()) { + $result[$node->getFilename()] = (object)[ + 'type' => 'directory', + 'path' => File::normalizePath($this->getRelativePath($node->getPathname())), + 'name' => $node->getFilename(), + 'editable' => false + ]; + } + elseif ($node->isFile()) { + $files[] = (object)[ + 'type' => 'file', + 'path' => File::normalizePath($this->getRelativePath($node->getPathname())), + 'name' => $node->getFilename(), + 'editable' => in_array(strtolower($node->getExtension()), $editableAssetTypes) + ]; + } + } + + foreach ($files as $file) { + $result[] = $file; + } + + return $result; + } + + protected function listDestinationDirectories(&$result, $excludeList, $startDir = null, $level = 0) + { + if ($startDir === null) { + $startDir = $this->getAssetsPath(); + + $result['/'] = 'assets'; + $level = 1; + } + + $dirs = new DirectoryIterator($startDir); + foreach ($dirs as $node) { + if (substr($node->getFileName(), 0, 1) == '.') { + continue; + } + + if ($node->isDir() && !$node->isDot()) { + $fullPath = $node->getPathname(); + $relativePath = $this->getRelativePath($fullPath); + if (array_key_exists($relativePath, $excludeList)) { + continue; + } + + $result[$relativePath] = str_repeat(' ', $level * 4).$node->getFilename(); + + $this->listDestinationDirectories($result, $excludeList, $fullPath, $level+1); + } + } + } + + protected function getSearchTerm() + { + return $this->searchTerm !== false ? $this->searchTerm : $this->getSession('search'); + } + + protected function isSearchMode() + { + return strlen($this->getSearchTerm()); + } + + protected function getUpPath() + { + $path = $this->getCurrentRelativePath(); + if (!strlen(rtrim(ltrim($path, '/'), '/'))) { + return null; + } + + return dirname($path); + } + + /** + * Check for valid asset file extension + * @param string + * @return bool + */ + protected function validateFileType($name) + { + $extension = strtolower(File::extension($name)); + + if (!in_array($extension, $this->assetExtensions)) { + return false; + } + + return true; + } + + /** + * Checks the current request to see if it is a postback containing a file upload + * for this particular widget. + */ + protected function checkUploadPostback() + { + $fileName = null; + + try { + $uploadedFile = Input::file('file_data'); + + if (!is_object($uploadedFile)) { + return; + } + + $fileName = $uploadedFile->getClientOriginalName(); + + /* + * Check valid upload + */ + if (!$uploadedFile->isValid()) { + throw new ApplicationException(Lang::get('cms::lang.asset.file_not_valid')); + } + + /* + * Check file size + */ + $maxSize = UploadedFile::getMaxFilesize(); + if ($uploadedFile->getSize() > $maxSize) { + throw new ApplicationException(Lang::get( + 'cms::lang.asset.too_large', + ['max_size' => File::sizeToString($maxSize)] + )); + } + + /* + * Check for valid file extensions + */ + if (!$this->validateFileType($fileName)) { + throw new ApplicationException(Lang::get( + 'cms::lang.asset.type_not_allowed', + ['allowed_types' => implode(', ', $this->assetExtensions)] + )); + } + + /* + * Accept the uploaded file + */ + $uploadedFile->move($this->getCurrentPath(), $uploadedFile->getClientOriginalName()); + + die('success'); + } + catch (Exception $ex) { + $message = $fileName !== null + ? Lang::get('cms::lang.asset.error_uploading_file', ['name' => $fileName, 'error' => $ex->getMessage()]) + : $ex->getMessage(); + + die($message); + } + } + + protected function setSearchTerm($term) + { + $this->searchTerm = trim($term); + $this->putSession('search', $this->searchTerm); + } + + protected function findFiles() + { + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($this->getAssetsPath(), RecursiveDirectoryIterator::SKIP_DOTS), + RecursiveIteratorIterator::SELF_FIRST, + RecursiveIteratorIterator::CATCH_GET_CHILD + ); + + $editableAssetTypes = Asset::getEditableExtensions(); + $searchTerm = Str::lower($this->getSearchTerm()); + $words = explode(' ', $searchTerm); + + $result = []; + foreach ($iterator as $item) { + if (!$item->isDir()) { + if (substr($item->getFileName(), 0, 1) == '.') { + continue; + } + + $path = $this->getRelativePath($item->getPathname()); + + if ($this->pathMatchesSearch($words, $path)) { + $result[] = (object)[ + 'type' => 'file', + 'path' => File::normalizePath($path), + 'name' => $item->getFilename(), + 'editable' => in_array(strtolower($item->getExtension()), $editableAssetTypes) + ]; + } + } + } + + return $result; + } + + protected function pathMatchesSearch(&$words, $path) + { + foreach ($words as $word) { + $word = trim($word); + if (!strlen($word)) { + continue; + } + + if (!Str::contains(Str::lower($path), $word)) { + return false; + } + } + + return true; + } +} diff --git a/plugins/indikator/devtools/widgets/assetlist/assets/css/assetlist.css b/plugins/indikator/devtools/widgets/assetlist/assets/css/assetlist.css new file mode 100644 index 0000000..c748bb3 --- /dev/null +++ b/plugins/indikator/devtools/widgets/assetlist/assets/css/assetlist.css @@ -0,0 +1,244 @@ +.control-assetlist p.no-data { + padding: 22px; + margin: 0; + color: #666666; + font-size: 14px; + text-align: center; + font-weight: 400; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} +.control-assetlist p.parent, +.control-assetlist ul li { + font-weight: 300; + line-height: 150%; + margin-bottom: 0; +} +.control-assetlist p.parent.active a, +.control-assetlist ul li.active a { + background: #dddddd; + position: relative; +} +.control-assetlist p.parent.active a:after, +.control-assetlist ul li.active a:after { + position: absolute; + height: 100%; + width: 4px; + left: 0; + top: 0; + background: #e67e22; + display: block; + content: ' '; +} +.control-assetlist p.parent a.link, +.control-assetlist ul li a.link { + display: block; + position: relative; + word-wrap: break-word; + padding: 10px 50px 10px 20px; + outline: none; + font-weight: 400; + color: #405261; + font-size: 14px; +} +.control-assetlist p.parent a.link:hover, +.control-assetlist ul li a.link:hover, +.control-assetlist p.parent a.link:focus, +.control-assetlist ul li a.link:focus, +.control-assetlist p.parent a.link:active, +.control-assetlist ul li a.link:active { + text-decoration: none; +} +.control-assetlist p.parent a.link span, +.control-assetlist ul li a.link span { + display: block; +} +.control-assetlist p.parent a.link span.description, +.control-assetlist ul li a.link span.description { + color: #8f8f8f; + font-size: 12px; + font-weight: 400; + word-wrap: break-word; +} +.control-assetlist p.parent a.link span.description strong, +.control-assetlist ul li a.link span.description strong { + color: #405261; + font-weight: 400; +} +.control-assetlist p.parent.directory a.link, +.control-assetlist ul li.directory a.link, +.control-assetlist p.parent.parent a.link, +.control-assetlist ul li.parent a.link { + padding-left: 40px; +} +.control-assetlist p.parent.directory a.link:after, +.control-assetlist ul li.directory a.link:after, +.control-assetlist p.parent.parent a.link:after, +.control-assetlist ul li.parent a.link:after { + display: block; + position: absolute; + width: 10px; + height: 10px; + top: 10px; + left: 20px; + font-family: FontAwesome; + font-weight: normal; + font-style: normal; + text-decoration: inherit; + -webkit-font-smoothing: antialiased; + content: "\f07b"; + color: #a1aab1; + font-size: 14px; +} +.control-assetlist p.parent.parent a.link, +.control-assetlist ul li.parent a.link { + padding-left: 41px; + background-color: #ffffff; + word-wrap: break-word; +} +.control-assetlist p.parent.parent a.link:before, +.control-assetlist ul li.parent a.link:before { + content: ''; + display: block; + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 1px; + background: #ecf0f1; +} +.control-assetlist p.parent.parent a.link:after, +.control-assetlist ul li.parent a.link:after { + font-size: 13px; + color: #34495e; + width: 18px; + height: 18px; + top: 11px; + left: 22px; + opacity: 0.5; + filter: alpha(opacity=50); + font-family: FontAwesome; + font-weight: normal; + font-style: normal; + text-decoration: inherit; + -webkit-font-smoothing: antialiased; + content: "\f053"; +} +.control-assetlist p.parent a.link:hover { + background: #dddddd !important; +} +.control-assetlist p.parent a.link:hover:after { + opacity: 1; + filter: alpha(opacity=100); +} +.control-assetlist p.parent a.link:hover:before { + display: none; +} +.control-assetlist ul { + padding: 0; + margin: 0; +} +.control-assetlist ul li { + font-weight: 300; + line-height: 150%; + position: relative; + list-style: none; +} +.control-assetlist ul li.active a.link, +.control-assetlist ul li a.link:hover { + background: #dddddd; +} +.control-assetlist ul li.active a.link { + position: relative; +} +.control-assetlist ul li.active a.link:after { + position: absolute; + height: 100%; + width: 4px; + left: 0; + top: 0; + background: #e67e22; + display: block; + content: ' '; +} +.control-assetlist ul li div.controls { + position: absolute; + right: 45px; + top: 10px; +} +.control-assetlist ul li div.controls .dropdown { + width: 14px; + height: 21px; +} +.control-assetlist ul li div.controls .dropdown.open a.control { + display: block!important; +} +.control-assetlist ul li div.controls .dropdown.open a.control:before { + visibility: visible; + display: block; +} +.control-assetlist ul li div.controls a.control { + color: #405261; + font-size: 14px; + visibility: hidden; + overflow: hidden; + width: 14px; + height: 21px; + display: none; + text-decoration: none; + cursor: pointer; + opacity: 0.5; + filter: alpha(opacity=50); +} +.control-assetlist ul li div.controls a.control:before { + visibility: visible; + display: block; + margin-right: 0; +} +.control-assetlist ul li div.controls a.control:hover { + opacity: 1; + filter: alpha(opacity=100); +} +.control-assetlist ul li:hover { + background: #dddddd; +} +.control-assetlist ul li:hover div.controls, +.control-assetlist ul li:hover a.control { + display: block!important; +} +.control-assetlist ul li:hover div.controls > a.control, +.control-assetlist ul li:hover a.control > a.control { + display: block!important; +} +.control-assetlist ul li .checkbox { + position: absolute; + top: -5px; + right: -5px; +} +.control-assetlist ul li .checkbox label { + margin-right: 0; +} +.control-assetlist ul li .checkbox label:before { + border-color: #cccccc; +} +.control-assetlist div.list-container { + position: relative; + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); +} +.control-assetlist div.list-container.animate ul { + -webkit-transition: all 0.2s ease; + transition: all 0.2s ease; +} +.control-assetlist div.list-container.goForward ul { + -webkit-transform: translate(-350px, 0); + -ms-transform: translate(-350px, 0); + transform: translate(-350px, 0); +} +.control-assetlist div.list-container.goBackward ul { + -webkit-transform: translate(350px, 0); + -ms-transform: translate(350px, 0); + transform: translate(350px, 0); +} diff --git a/plugins/indikator/devtools/widgets/assetlist/assets/js/assetlist.js b/plugins/indikator/devtools/widgets/assetlist/assets/js/assetlist.js new file mode 100644 index 0000000..4fb3d41 --- /dev/null +++ b/plugins/indikator/devtools/widgets/assetlist/assets/js/assetlist.js @@ -0,0 +1,184 @@ +/* + * Asset list + */ ++function ($) { "use strict"; + + var AssetList = function (form, alias) { + this.$form = $(form) + this.alias = alias + + + this.$form.on('ajaxSuccess', $.proxy(this.onAjaxSuccess, this)) + this.$form.on('click', 'ul.list > li.directory > a', $.proxy(this.onDirectoryClick, this)) + this.$form.on('click', 'ul.list > li.file > a', $.proxy(this.onFileClick, this)) + this.$form.on('click', 'p.parent > a', $.proxy(this.onDirectoryClick, this)) + this.$form.on('click', 'a[data-control=delete-asset]', $.proxy(this.onDeleteClick, this)) + this.$form.on('oc.list.setActiveItem', $.proxy(this.onSetActiveItem, this)) + + this.setupUploader() + } + + // Event handlers + // ================= + + AssetList.prototype.onDirectoryClick = function(e) { + this.gotoDirectory( + $(e.currentTarget).data('path'), + $(e.currentTarget).parent().hasClass('parent') + ) + + return false; + } + + AssetList.prototype.gotoDirectory = function(path, gotoParent) { + var $container = $('div.list-container', this.$form), + self = this + + if (gotoParent !== undefined && gotoParent) + $container.addClass('goBackward') + else + $container.addClass('goForward') + + $.oc.stripeLoadIndicator.show() + this.$form.request(this.alias+'::onOpenDirectory', { + data: { + path: path, + d: 0.2 + }, + complete: function() { + self.updateUi() + $container.trigger('oc.scrollbar.gotoStart') + }, + error: function(jqXHR, textStatus, errorThrown) { + $container.removeClass('goForward goBackward') + alert(jqXHR.responseText.length ? jqXHR.responseText : jqXHR.statusText) + } + }).always(function(){ + $.oc.stripeLoadIndicator.hide() + }) + } + + AssetList.prototype.onDeleteClick = function(e) { + var $el = $(e.currentTarget), + self = this + + if (!confirm($el.data('confirmation'))) + return false + + this.$form.request(this.alias+'::onDeleteFiles', { + success: function(data) { + if (data.error !== undefined && $.type(data.error) === 'string' && data.error.length) + $.oc.flashMsg({text: data.error, 'class': 'error'}) + }, + complete: function() { + self.refresh() + } + }) + + return false + } + + AssetList.prototype.onAjaxSuccess = function() { + this.updateUi() + } + + AssetList.prototype.onUploadFail = function(file, message) { + if (file.xhr.status === 413) { + message = 'Server rejected the file because it was too large, try increasing post_max_size'; + } + if (!message) { + message = 'Error uploading file' + } + + $.oc.alert(message) + + this.refresh() + } + + AssetList.prototype.onUploadSuccess = function(file, data) { + if (data !== 'success') { + $.oc.alert(data) + } + } + + AssetList.prototype.onUploadComplete = function(file, data) { + $.oc.stripeLoadIndicator.hide() + this.refresh() + } + + AssetList.prototype.onUploadStart = function() { + $.oc.stripeLoadIndicator.show() + } + + AssetList.prototype.onFileClick = function(event) { + var $link = $(event.currentTarget), + $li = $link.parent() + + var e = $.Event('open.oc.list', {relatedTarget: $li.get(0), clickEvent: event}) + this.$form.trigger(e, this) + + if (e.isDefaultPrevented()) + return false; + } + + AssetList.prototype.onSetActiveItem = function(event, dataId) { + $('ul li.file', this.$form).removeClass('active') + if (dataId) + $('ul li.file[data-id="'+dataId+'"]', this.$form).addClass('active') + } + + // Service functions + // ================= + + AssetList.prototype.updateUi = function() { + $('button[data-control=asset-tools]', self.$form).trigger('oc.triggerOn.update') + } + + AssetList.prototype.refresh = function() { + var self = this; + + this.$form.request(this.alias+'::onRefresh', { + complete: function() { + self.updateUi() + } + }) + } + + AssetList.prototype.setupUploader = function() { + var self = this, + $link = $('[data-control="upload-assets"]', this.$form), + uploaderOptions = { + method: 'POST', + url: window.location, + paramName: 'file_data', + previewsContainer: $('
    ').get(0), + clickable: $link.get(0), + timeout: 0, + headers: {} + } + + /* + * Add CSRF token to headers + */ + var token = $('meta[name="csrf-token"]').attr('content') + if (token) { + uploaderOptions.headers['X-CSRF-TOKEN'] = token + } + + var dropzone = new Dropzone($('
    ').get(0), uploaderOptions) + dropzone.on('error', $.proxy(self.onUploadFail, self)) + dropzone.on('success', $.proxy(self.onUploadSuccess, self)) + dropzone.on('complete', $.proxy(self.onUploadComplete, self)) + dropzone.on('sending', function(file, xhr, formData) { + $.each(self.$form.serializeArray(), function (index, field) { + formData.append(field.name, field.value) + }) + xhr.setRequestHeader('X-OCTOBER-REQUEST-HANDLER', self.alias + '::onUpload') + self.onUploadStart() + }) + } + + $(document).ready(function(){ + new AssetList($('#asset-list-container').closest('form'), $('#asset-list-container').data('alias')) + }) +}(window.jQuery); diff --git a/plugins/indikator/devtools/widgets/assetlist/assets/less/assetlist.less b/plugins/indikator/devtools/widgets/assetlist/assets/less/assetlist.less new file mode 100644 index 0000000..a8070d3 --- /dev/null +++ b/plugins/indikator/devtools/widgets/assetlist/assets/less/assetlist.less @@ -0,0 +1,236 @@ +@import "../../../../../backend/assets/less/core/boot.less"; + +.control-assetlist { + p.no-data { + padding: 22px; + margin: 0; + color: @color-filelist-norecords-text; + font-size: 14px; + text-align: center; + font-weight: 400; + .border-radius(@border-radius-base); + } + + p.parent, ul li { + font-weight: 300; + line-height: 150%; + margin-bottom: 0; + + &.active a { + background: @color-list-active; + position: relative; + &:after { + position: absolute; + height: 100%; + width: 4px; + left: 0; + top: 0; + background: @color-list-active-border; + display: block; + content: ' '; + } + } + + a.link { + display: block; + position: relative; + word-wrap: break-word; + padding: 10px 50px 10px 20px; + outline: none; + font-weight: 400; + color: @color-text-title; + font-size: 14px; + + &:hover, &:focus, &:active {text-decoration: none;} + + span { + display: block; + + &.description { + color: @color-text-description; + font-size: 12px; + font-weight: 400; + word-wrap: break-word; + + strong { + color: @color-text-title; + font-weight: 400; + } + } + } + } + + &.directory, &.parent { + a.link { + padding-left: 40px; + + &:after { + display: block; + position: absolute; + width: 10px; + height: 10px; + top: 10px; + left: 20px; + .icon(@folder); + color: @color-list-icon; + font-size: 14px; + } + } + } + + &.parent { + a.link { + padding-left: 41px; + background-color: @color-list-parent-bg; + word-wrap: break-word; + + &:before { + content: ''; + height: 1px; + display: block; + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 1px; + background: #ecf0f1; + } + + + &:after { + font-size: 13px; + color: @color-list-nav-arrow; + width: 18px; + height: 18px; + top: 11px; + left: 22px; + .opacity(0.5); + .icon(@chevron-left); + } + } + } + } + + p.parent a.link:hover { + background: @color-list-active!important; + + &:after { + .opacity(1); + } + + &:before { + display: none; + } + } + + ul { + padding: 0; + margin: 0; + + li { + font-weight: 300; + line-height: 150%; + position: relative; + list-style: none; + + &.active a.link, a.link:hover {background: @color-list-active;} + &.active a.link { + position: relative; + &:after { + position: absolute; + height: 100%; + width: 4px; + left: 0; + top: 0; + background: @color-list-active-border; + display: block; + content: ' '; + } + } + + div.controls { + position: absolute; + right: 45px; + top: 10px; + + .dropdown { + width: 14px; + height: 21px; + + &.open a.control { + display: block!important; + &:before { + visibility: visible; + display: block; + } + } + } + + a.control { + color: @color-text-title; + font-size: 14px; + visibility: hidden; + overflow: hidden; + width: 14px; + height: 21px; + display: none; + text-decoration: none; + cursor: pointer; + .opacity(0.5); + &:before { + visibility: visible; + display: block; + margin-right: 0; + } + + &:hover { + .opacity(1); + } + } + } + + &:hover { + background: @color-list-active; + div.controls, a.control { + display: block!important; + + > a.control { + display: block!important; + } + } + } + + .checkbox { + position: absolute; + top: -5px; + right: -5px; + + label { + margin-right: 0; + + &:before { + border-color: @color-filelist-cb-border; + } + } + } + } + } + + div.list-container { + position: relative; + .translate(0, 0); + + &.animate ul { + .transition(all 0.2s ease); + } + + &.goForward ul { + .translate(-350px, 0); + } + + &.goBackward ul { + .translate(350px, 0); + } + + } +} diff --git a/plugins/indikator/devtools/widgets/assetlist/partials/_body.htm b/plugins/indikator/devtools/widgets/assetlist/partials/_body.htm new file mode 100644 index 0000000..75dfc38 --- /dev/null +++ b/plugins/indikator/devtools/widgets/assetlist/partials/_body.htm @@ -0,0 +1,8 @@ +makePartial('toolbar') ?> +
    +
    +
    + makePartial('files', ['data'=>$data]) ?> +
    +
    +
    diff --git a/plugins/indikator/devtools/widgets/assetlist/partials/_files.htm b/plugins/indikator/devtools/widgets/assetlist/partials/_files.htm new file mode 100644 index 0000000..8b57fbd --- /dev/null +++ b/plugins/indikator/devtools/widgets/assetlist/partials/_files.htm @@ -0,0 +1,7 @@ +
    +
    +
    + makePartial('items', ['items'=>$data]) ?> +
    +
    +
    diff --git a/plugins/indikator/devtools/widgets/assetlist/partials/_items.htm b/plugins/indikator/devtools/widgets/assetlist/partials/_items.htm new file mode 100644 index 0000000..1a378e1 --- /dev/null +++ b/plugins/indikator/devtools/widgets/assetlist/partials/_items.htm @@ -0,0 +1,57 @@ +isSearchMode(); + + if (($upPath = $this->getUpPath()) !== null && !$searchMode): +?> +

    + getCurrentRelativePath() ?> +

    + +
    + +
      + path, '/'); + ?> +
    • editable): ?>data-editable data-item-path="path, '/')) ?>" data-item-type="asset" data-id=""> + + name) ?> + + + + path)) ?> + + + + +
      + +
      + + +
      + path) ?> + isItemSelected($item->path) ? 'checked' : null ?> + data-request="getEventHandler('onSelect') ?>" + value="1"> + +
      + +
    • + +
    + +

    noRecordsMessage)) ?>

    + +
    diff --git a/plugins/indikator/devtools/widgets/assetlist/partials/_move_form.htm b/plugins/indikator/devtools/widgets/assetlist/partials/_move_form.htm new file mode 100644 index 0000000..1cec459 --- /dev/null +++ b/plugins/indikator/devtools/widgets/assetlist/partials/_move_form.htm @@ -0,0 +1,40 @@ +$this->getEventHandler('onMove'), + 'data-request-success'=>"\$(this).trigger('close.oc.popup')", + 'data-stripe-load-indicator'=>1, + 'id'=>'asset-move-popup-form' +]) ?> + + + + diff --git a/plugins/indikator/devtools/widgets/assetlist/partials/_new_dir_form.htm b/plugins/indikator/devtools/widgets/assetlist/partials/_new_dir_form.htm new file mode 100644 index 0000000..eb5b2ad --- /dev/null +++ b/plugins/indikator/devtools/widgets/assetlist/partials/_new_dir_form.htm @@ -0,0 +1,45 @@ +$this->getEventHandler('onNewDirectory'), + 'data-request-success'=>"\$(this).trigger('close.oc.popup')", + 'data-stripe-load-indicator'=>1, + 'id'=>'asset-new-dir-popup-form' +]) ?> + + + + + + + + diff --git a/plugins/indikator/devtools/widgets/assetlist/partials/_rename_form.htm b/plugins/indikator/devtools/widgets/assetlist/partials/_rename_form.htm new file mode 100644 index 0000000..29221a0 --- /dev/null +++ b/plugins/indikator/devtools/widgets/assetlist/partials/_rename_form.htm @@ -0,0 +1,46 @@ +getEventHandler('onApplyName'), [ + 'success' => "\$el.trigger('close.oc.popup');", + 'data-stripe-load-indicator' => 1, + 'id' => 'asset-rename-popup-form' +]) ?> + + + + + + + + diff --git a/plugins/indikator/devtools/widgets/assetlist/partials/_toolbar.htm b/plugins/indikator/devtools/widgets/assetlist/partials/_toolbar.htm new file mode 100644 index 0000000..7764cc0 --- /dev/null +++ b/plugins/indikator/devtools/widgets/assetlist/partials/_toolbar.htm @@ -0,0 +1,77 @@ +
    +
    + + +
    +
    + + + +
    +
    + + +
    + +
    + +
    +
    diff --git a/plugins/tps/birzha/controllers/SliderApp.php b/plugins/tps/birzha/controllers/SliderApp.php new file mode 100644 index 0000000..3e27b0a --- /dev/null +++ b/plugins/tps/birzha/controllers/SliderApp.php @@ -0,0 +1,18 @@ + + + +
    diff --git a/plugins/tps/birzha/controllers/sliderapp/config_form.yaml b/plugins/tps/birzha/controllers/sliderapp/config_form.yaml new file mode 100644 index 0000000..b2fb182 --- /dev/null +++ b/plugins/tps/birzha/controllers/sliderapp/config_form.yaml @@ -0,0 +1,10 @@ +name: SliderApp +form: $/tps/birzha/models/sliderapp/fields.yaml +modelClass: TPS\Birzha\Models\SliderApp +defaultRedirect: tps/birzha/sliderapp +create: + redirect: 'tps/birzha/sliderapp/update/:id' + redirectClose: tps/birzha/sliderapp +update: + redirect: tps/birzha/sliderapp + redirectClose: tps/birzha/sliderapp diff --git a/plugins/tps/birzha/controllers/sliderapp/config_list.yaml b/plugins/tps/birzha/controllers/sliderapp/config_list.yaml new file mode 100644 index 0000000..ed5d9b5 --- /dev/null +++ b/plugins/tps/birzha/controllers/sliderapp/config_list.yaml @@ -0,0 +1,12 @@ +list: $/tps/birzha/models/sliderapp/columns.yaml +modelClass: TPS\Birzha\Models\SliderApp +title: SliderApp +noRecordsMessage: 'backend::lang.list.no_records' +showSetup: true +showCheckboxes: true +recordsPerPage: 20 +toolbar: + buttons: list_toolbar + search: + prompt: 'backend::lang.list.search_prompt' +recordUrl: 'tps/birzha/sliderapp/update/:id' diff --git a/plugins/tps/birzha/controllers/sliderapp/create.htm b/plugins/tps/birzha/controllers/sliderapp/create.htm new file mode 100644 index 0000000..793bd71 --- /dev/null +++ b/plugins/tps/birzha/controllers/sliderapp/create.htm @@ -0,0 +1,46 @@ + + + + +fatalError): ?> + + 'layout']) ?> + +
    + formRender() ?> +
    + +
    +
    + + + + + +
    +
    + + + + +

    fatalError)) ?>

    +

    + \ No newline at end of file diff --git a/plugins/tps/birzha/controllers/sliderapp/index.htm b/plugins/tps/birzha/controllers/sliderapp/index.htm new file mode 100644 index 0000000..ea43a36 --- /dev/null +++ b/plugins/tps/birzha/controllers/sliderapp/index.htm @@ -0,0 +1 @@ +listRender() ?> diff --git a/plugins/tps/birzha/controllers/sliderapp/preview.htm b/plugins/tps/birzha/controllers/sliderapp/preview.htm new file mode 100644 index 0000000..98bbb81 --- /dev/null +++ b/plugins/tps/birzha/controllers/sliderapp/preview.htm @@ -0,0 +1,22 @@ + + + + +fatalError): ?> + +
    + formRenderPreview() ?> +
    + + +

    fatalError) ?>

    + + +

    + + + +

    \ No newline at end of file diff --git a/plugins/tps/birzha/controllers/sliderapp/update.htm b/plugins/tps/birzha/controllers/sliderapp/update.htm new file mode 100644 index 0000000..00fd88d --- /dev/null +++ b/plugins/tps/birzha/controllers/sliderapp/update.htm @@ -0,0 +1,54 @@ + + + + +fatalError): ?> + + 'layout']) ?> + +
    + formRender() ?> +
    + +
    +
    + + + + + + + +
    +
    + + + +

    fatalError)) ?>

    +

    + \ No newline at end of file diff --git a/plugins/tps/birzha/models/SliderApp.php b/plugins/tps/birzha/models/SliderApp.php new file mode 100644 index 0000000..fd54b73 --- /dev/null +++ b/plugins/tps/birzha/models/SliderApp.php @@ -0,0 +1,27 @@ +engine = 'InnoDB'; + $table->increments('id')->unsigned(); + $table->timestamp('created_at')->nullable(); + $table->timestamp('updated_at')->nullable(); + $table->timestamp('deleted_at')->nullable(); + $table->string('img')->nullable(); + $table->string('type')->nullable(); + $table->text('note')->nullable(); + }); + } + + public function down() + { + Schema::dropIfExists('tps_birzha_slider_app'); + } +} diff --git a/plugins/tps/birzha/updates/builder_table_update_tps_birzha_categories_2.php b/plugins/tps/birzha/updates/builder_table_update_tps_birzha_categories_2.php new file mode 100644 index 0000000..b76bdff --- /dev/null +++ b/plugins/tps/birzha/updates/builder_table_update_tps_birzha_categories_2.php @@ -0,0 +1,23 @@ +boolean('is_featured')->default(false); + }); + } + + public function down() + { + Schema::table('tps_birzha_categories', function($table) + { + $table->dropColumn('is_featured'); + }); + } +} diff --git a/plugins/tps/birzha/updates/builder_table_update_tps_birzha_slider_app.php b/plugins/tps/birzha/updates/builder_table_update_tps_birzha_slider_app.php new file mode 100644 index 0000000..f433594 --- /dev/null +++ b/plugins/tps/birzha/updates/builder_table_update_tps_birzha_slider_app.php @@ -0,0 +1,23 @@ +string('type', 191)->default('slider1')->change(); + }); + } + + public function down() + { + Schema::table('tps_birzha_slider_app', function($table) + { + $table->string('type', 191)->default(null)->change(); + }); + } +} diff --git a/plugins/tps/birzha/updates/builder_table_update_tps_birzha_slider_app_2.php b/plugins/tps/birzha/updates/builder_table_update_tps_birzha_slider_app_2.php new file mode 100644 index 0000000..247c6bb --- /dev/null +++ b/plugins/tps/birzha/updates/builder_table_update_tps_birzha_slider_app_2.php @@ -0,0 +1,23 @@ +integer('order')->nullable(); + }); + } + + public function down() + { + Schema::table('tps_birzha_slider_app', function($table) + { + $table->dropColumn('order'); + }); + } +} diff --git a/plugins/tps/birzha/updates/version.yaml b/plugins/tps/birzha/updates/version.yaml index 7062172..56e4c42 100644 --- a/plugins/tps/birzha/updates/version.yaml +++ b/plugins/tps/birzha/updates/version.yaml @@ -299,3 +299,15 @@ 1.0.106: - 'Updated table tps_birzha_products' - builder_table_update_tps_birzha_products_26.php +1.0.107: + - 'Created table tps_birzha_slider_app' + - builder_table_create_tps_birzha_slider_app.php +1.0.108: + - 'Updated table tps_birzha_slider_app' + - builder_table_update_tps_birzha_slider_app.php +1.0.109: + - 'Updated table tps_birzha_slider_app' + - builder_table_update_tps_birzha_slider_app_2.php +1.0.110: + - 'Updated table tps_birzha_categories' + - builder_table_update_tps_birzha_categories_2.php