api
This commit is contained in:
parent
402951cbe0
commit
0e85d1fc3a
|
|
@ -5,6 +5,8 @@ APP_DEBUG=true
|
|||
APP_URL=http://localhost
|
||||
APP_LOCALE=en
|
||||
|
||||
BAGISTO_URL=https://nurgul.com.tm/app/api/
|
||||
|
||||
ACTIVE_THEME=demo
|
||||
BACKEND_URI=/admin
|
||||
CMS_ROUTE_CACHE=false
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
<?php namespace Indikator\DevTools;
|
||||
|
||||
use System\Classes\PluginBase;
|
||||
use System\Classes\SettingsManager;
|
||||
use Event;
|
||||
use Backend;
|
||||
use BackendAuth;
|
||||
use BackendMenu;
|
||||
use Indikator\DevTools\Models\Settings as Tools;
|
||||
use Db;
|
||||
|
||||
class Plugin extends PluginBase
|
||||
{
|
||||
public $elevated = true;
|
||||
|
||||
public function pluginDetails()
|
||||
{
|
||||
return [
|
||||
'name' => '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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 512 512" height="512px" id="Слой_1" version="1.1" viewBox="0 0 512 512" width="512px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="wrench"><g><path d="M512,256c0,141.391-114.609,255.992-256,255.992C114.609,511.984,0,397.383,0,256.008 c0-141.391,114.609-255.992,256-256C397.391,0.008,512,114.617,512,256z" fill="#FA6E51"/><path d="M302.195,209.852L112.727,399.32l110.516,110.516c10.734,1.367,21.648,2.156,32.758,2.156 c139.391-0.008,252.656-111.398,255.852-250.016L356.742,106.875l-78.773,78.766L302.195,209.852z" fill="#E8573F"/></g><g><path d="M405.531,156.766l-41.086,41.094c-6.703,6.703-23.188,1.086-36.813-12.523 C314,171.703,308.391,155.227,315.109,148.5l0,0l41.633-41.625c-5.984-1.688-12.297-2.57-18.82-2.578 c-38.555,0.008-69.789,31.234-69.789,69.773c0,8.43,1.5,16.523,4.242,23.984L112.914,357.5c-5.32,5.313-8.594,12.672-8.617,20.797 c0,16.234,13.172,29.398,29.406,29.406c8.133,0,15.484-3.281,20.797-8.617l0,0l159.461-159.438 c7.461,2.727,15.539,4.227,23.961,4.219c38.539,0,69.781-31.258,69.781-69.797C407.703,168.109,406.945,162.297,405.531,156.766z" fill="#F4F6F9" id="wrench_3_"/><path d="M122.078,378.328c0,6.422,5.219,11.625,11.625,11.625c6.43,0,11.633-5.203,11.633-11.625 s-5.203-11.633-11.633-11.633C127.297,366.695,122.078,371.906,122.078,378.328z" fill="#CBD0D8"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -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 = $('<a href="javascript:;" class="tab-collapse-icon tabless"><i class="icon-chevron-up"></i></a>'),
|
||||
$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 = $('<a href="javascript:;" class="tab-collapse-icon primary"><i class="icon-chevron-down"></i></a>'),
|
||||
$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 = $('<li />').prop({ 'class': 'tokenexpander-button' }).append(
|
||||
$('<a />').prop({ 'href': 'javascript:; '}).append(
|
||||
$('<i />').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($('<option>').attr('value', layoutFile).text(layoutName))
|
||||
})
|
||||
$select.trigger('pause.oc.changeMonitor')
|
||||
$select.val(value)
|
||||
$select.trigger('change')
|
||||
$select.trigger('resume.oc.changeMonitor')
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$.oc.cmsPage = new CmsPage();
|
||||
}(window.jQuery);
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
<?php namespace Indikator\DevTools\Classes;
|
||||
|
||||
use File;
|
||||
use Lang;
|
||||
use Config;
|
||||
use Request;
|
||||
use Cms\Classes\Theme;
|
||||
use Cms\Helpers\File as FileHelper;
|
||||
use October\Rain\Extension\Extendable;
|
||||
use ApplicationException;
|
||||
use ValidationException;
|
||||
|
||||
/**
|
||||
* The CMS asset file class.
|
||||
*
|
||||
* @package october\cms
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class Asset extends Extendable
|
||||
{
|
||||
/**
|
||||
* @var \Cms\Classes\Theme A reference to the CMS theme containing the object.
|
||||
*/
|
||||
protected $theme;
|
||||
|
||||
/**
|
||||
* @var string The plugin folder name.
|
||||
*/
|
||||
protected $dirName = 'plugins';
|
||||
|
||||
/**
|
||||
* @var string Specifies the file name corresponding the CMS object.
|
||||
*/
|
||||
public $fileName;
|
||||
|
||||
/**
|
||||
* @var string Specifies the file name, the CMS object was loaded from.
|
||||
*/
|
||||
protected $originalFileName = null;
|
||||
|
||||
/**
|
||||
* @var string Last modified time.
|
||||
*/
|
||||
public $mtime;
|
||||
|
||||
/**
|
||||
* @var string The entire file content.
|
||||
*/
|
||||
public $content;
|
||||
|
||||
/**
|
||||
* @var array The attributes that are mass assignable.
|
||||
*/
|
||||
protected $fillable = [
|
||||
'fileName',
|
||||
'content'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array Allowable file extensions.
|
||||
*/
|
||||
protected $allowedExtensions = [];
|
||||
|
||||
/**
|
||||
* @var bool Indicates if the model exists.
|
||||
*/
|
||||
public $exists = false;
|
||||
|
||||
/**
|
||||
* Creates an instance of the object and associates it with a CMS theme.
|
||||
* @param \Cms\Classes\Theme $theme Specifies the theme the object belongs to.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->allowedExtensions = self::getEditableExtensions();
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the object from a file.
|
||||
* This method is used in the CMS back-end. It doesn't use any caching.
|
||||
* @param \Cms\Classes\Theme $theme Specifies the theme the object belongs to.
|
||||
* @param string $fileName Specifies the file name, with the extension.
|
||||
* The file name can contain only alphanumeric symbols, dashes and dots.
|
||||
* @return mixed Returns a CMS object instance or null if the object wasn't found.
|
||||
*/
|
||||
public static function load($theme, $fileName)
|
||||
{
|
||||
return (new static($theme))->find($fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the theme datasource for the model.
|
||||
* @param \Cms\Classes\Theme $theme Specifies a parent theme.
|
||||
* @return $this
|
||||
*/
|
||||
public static function inTheme($theme)
|
||||
{
|
||||
if (is_string($theme)) {
|
||||
$theme = Theme::load($theme);
|
||||
}
|
||||
return new static($theme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a single template by its file name.
|
||||
*
|
||||
* @param string $fileName
|
||||
* @return mixed|static
|
||||
*/
|
||||
public function find($fileName)
|
||||
{
|
||||
$filePath = $this->getFilePath($fileName);
|
||||
|
||||
if (!File::isFile($filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (($content = @File::get($filePath)) === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->fileName = $fileName;
|
||||
$this->originalFileName = $fileName;
|
||||
$this->mtime = File::lastModified($filePath);
|
||||
$this->content = $content;
|
||||
$this->exists = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the object attributes.
|
||||
* @param array $attributes A list of attributes to set.
|
||||
*/
|
||||
public function fill(array $attributes)
|
||||
{
|
||||
foreach ($attributes as $key => $value) {
|
||||
if (!in_array($key, $this->fillable)) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.cms_object.invalid_property',
|
||||
['name' => $key]
|
||||
));
|
||||
}
|
||||
|
||||
$this->$key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the object to the disk.
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$this->validateFileName();
|
||||
|
||||
$fullPath = $this->getFilePath();
|
||||
|
||||
if (File::isFile($fullPath) && $this->originalFileName !== $this->fileName) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.cms_object.file_already_exists',
|
||||
['name' => $this->fileName]
|
||||
));
|
||||
}
|
||||
|
||||
$dirPath = base_path().'/'.$this->dirName;
|
||||
if (!file_exists($dirPath) || !is_dir($dirPath)) {
|
||||
if (!File::makeDirectory($dirPath, 0777, true, true)) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.cms_object.error_creating_directory',
|
||||
['name' => $dirPath]
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if (($pos = strpos($this->fileName, '/')) !== false) {
|
||||
$dirPath = dirname($fullPath);
|
||||
|
||||
if (!is_dir($dirPath) && !File::makeDirectory($dirPath, 0777, true, true)) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.cms_object.error_creating_directory',
|
||||
['name' => $dirPath]
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
$newFullPath = $fullPath;
|
||||
if (@File::put($fullPath, $this->content) === false) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.cms_object.error_saving',
|
||||
['name' => $this->fileName]
|
||||
));
|
||||
}
|
||||
|
||||
if (strlen($this->originalFileName) && $this->originalFileName !== $this->fileName) {
|
||||
$fullPath = $this->getFilePath($this->originalFileName);
|
||||
|
||||
if (File::isFile($fullPath)) {
|
||||
@unlink($fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
clearstatcache();
|
||||
|
||||
$this->mtime = @File::lastModified($newFullPath);
|
||||
$this->originalFileName = $this->fileName;
|
||||
$this->exists = true;
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
$fileName = Request::input('fileName');
|
||||
$fullPath = $this->getFilePath($fileName);
|
||||
|
||||
$this->validateFileName($fileName);
|
||||
|
||||
if (File::exists($fullPath)) {
|
||||
if (!@File::delete($fullPath)) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.asset.error_deleting_file',
|
||||
['name' => $fileName]
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the supplied filename, extension and path.
|
||||
* @param string $fileName
|
||||
*/
|
||||
protected function validateFileName($fileName = null)
|
||||
{
|
||||
if ($fileName === null) {
|
||||
$fileName = $this->fileName;
|
||||
}
|
||||
|
||||
$fileName = trim($fileName);
|
||||
|
||||
if (!strlen($fileName)) {
|
||||
throw new ValidationException(['fileName' =>
|
||||
Lang::get('cms::lang.cms_object.file_name_required', [
|
||||
'allowed' => implode(', ', $this->allowedExtensions),
|
||||
'invalid' => pathinfo($fileName, PATHINFO_EXTENSION)
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
if (!FileHelper::validateExtension($fileName, $this->allowedExtensions, false)) {
|
||||
throw new ValidationException(['fileName' =>
|
||||
Lang::get('cms::lang.cms_object.invalid_file_extension', [
|
||||
'allowed' => implode(', ', $this->allowedExtensions),
|
||||
'invalid' => pathinfo($fileName, PATHINFO_EXTENSION)
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
if (!FileHelper::validatePath($fileName, null)) {
|
||||
throw new ValidationException(['fileName' =>
|
||||
Lang::get('cms::lang.cms_object.invalid_file', [
|
||||
'name' => $fileName
|
||||
])
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file name.
|
||||
* @return string
|
||||
*/
|
||||
public function getFileName()
|
||||
{
|
||||
return $this->fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute file path.
|
||||
* @param string $fileName Specifies the file name to return the path to.
|
||||
* @return string
|
||||
*/
|
||||
public function getFilePath($fileName = null)
|
||||
{
|
||||
if ($fileName === null) {
|
||||
$fileName = $this->fileName;
|
||||
}
|
||||
|
||||
return base_path().'/'.$this->dirName.'/'.$fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of editable asset extensions.
|
||||
* The list can be overridden with the cms.editableAssetTypes configuration option.
|
||||
* @return array
|
||||
*/
|
||||
public static function getEditableExtensions()
|
||||
{
|
||||
$defaultTypes = ['js', 'jsx', 'css', 'sass', 'scss', 'less', 'php', 'htm', 'html', 'yaml', 'md', 'txt'];
|
||||
|
||||
$configTypes = Config::get('cms.editableAssetTypes');
|
||||
if (!$configTypes) {
|
||||
return $defaultTypes;
|
||||
}
|
||||
|
||||
return $configTypes;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# ===================================
|
||||
# Form Field Definitions
|
||||
# ===================================
|
||||
|
||||
fields:
|
||||
|
||||
fileName:
|
||||
label: cms::lang.editor.filename
|
||||
attributes:
|
||||
default-focus: 1
|
||||
|
||||
toolbar:
|
||||
type: partial
|
||||
path: content_toolbar
|
||||
cssClass: collapse-visible
|
||||
|
||||
tabs:
|
||||
|
||||
cssClass: master-area
|
||||
|
||||
secondaryTabs:
|
||||
|
||||
stretch: true
|
||||
fields:
|
||||
|
||||
content:
|
||||
tab: cms::lang.editor.content
|
||||
stretch: true
|
||||
type: codeeditor
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "indikator/devtools-plugin",
|
||||
"type": "october-plugin",
|
||||
"description": "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.",
|
||||
"homepage": "https://octobercms.com/plugin/indikator-devtools",
|
||||
"keywords": ["october", "octobercms"],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"composer/installers": "~1.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,380 @@
|
|||
<?php namespace Indikator\DevTools\Controllers;
|
||||
|
||||
use Url;
|
||||
use Lang;
|
||||
use Flash;
|
||||
use Config;
|
||||
use Request;
|
||||
use Response;
|
||||
use Exception;
|
||||
use BackendMenu;
|
||||
use Indikator\DevTools\Classes\Asset;
|
||||
use Indikator\DevTools\Widgets\AssetList;
|
||||
use Cms\Classes\Router;
|
||||
use Backend\Classes\Controller;
|
||||
use Backend\Classes\WidgetManager;
|
||||
use System\Helpers\DateTime;
|
||||
use October\Rain\Router\Router as RainRouter;
|
||||
use ApplicationException;
|
||||
|
||||
/**
|
||||
* CMS index
|
||||
*
|
||||
* @package october\cms
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class Editor extends Controller
|
||||
{
|
||||
use \Backend\Traits\InspectableContainer;
|
||||
|
||||
public $requiredPermissions = ['indikator.devtools.editor'];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
BackendMenu::setContext('Indikator.DevTools', 'editor', 'assets');
|
||||
|
||||
try {
|
||||
new AssetList($this, 'assetList');
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$this->handleError($ex);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Pages
|
||||
//
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->addJs('/plugins/indikator/devtools/assets/october.cmspage.js');
|
||||
$this->addJs('/modules/cms/assets/js/october.dragcomponents.js', 'core');
|
||||
$this->addJs('/modules/cms/assets/js/october.tokenexpander.js', 'core');
|
||||
$this->addCss('/modules/cms/assets/css/october.components.css', 'core');
|
||||
|
||||
$this->bodyClass = 'compact-container';
|
||||
$this->pageTitle = 'cms::lang.cms.menu_label';
|
||||
$this->pageTitleTemplate = '%s '.trans($this->pageTitle);
|
||||
|
||||
if (Request::ajax() && Request::input('formWidgetAlias')) {
|
||||
$this->bindFormWidgetToController();
|
||||
}
|
||||
}
|
||||
|
||||
public function index_onOpenTemplate()
|
||||
{
|
||||
$type = Request::input('type');
|
||||
$template = $this->loadTemplate($type, Request::input('path'));
|
||||
$widget = $this->makeTemplateFormWidget($type, $template);
|
||||
|
||||
$this->vars['templatePath'] = Request::input('path');
|
||||
$this->vars['lastModified'] = DateTime::makeCarbon($template->mtime);
|
||||
|
||||
if ($type === 'page') {
|
||||
$router = new RainRouter;
|
||||
$this->vars['pageUrl'] = $router->urlFromPattern($template->url);
|
||||
}
|
||||
|
||||
return [
|
||||
'tabTitle' => $this->getTabTitle($type, $template),
|
||||
'tab' => $this->makePartial('form_page', [
|
||||
'form' => $widget,
|
||||
'templateType' => $type,
|
||||
'templateMtime' => $template->mtime
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
public function onSave()
|
||||
{
|
||||
$type = Request::input('templateType');
|
||||
$templatePath = trim(Request::input('templatePath'));
|
||||
$template = $templatePath ? $this->loadTemplate($type, $templatePath) : $this->createTemplate($type);
|
||||
$formWidget = $this->makeTemplateFormWidget($type, $template);
|
||||
|
||||
$saveData = $formWidget->getSaveData();
|
||||
$postData = post();
|
||||
$templateData = [];
|
||||
|
||||
$settings = array_get($saveData, 'settings', []) + Request::input('settings', []);
|
||||
$settings = $this->upgradeSettings($settings);
|
||||
|
||||
if ($settings) {
|
||||
$templateData['settings'] = $settings;
|
||||
}
|
||||
|
||||
$fields = ['markup', 'code', 'fileName', 'content'];
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if (array_key_exists($field, $saveData)) {
|
||||
$templateData[$field] = $saveData[$field];
|
||||
}
|
||||
elseif (array_key_exists($field, $postData)) {
|
||||
$templateData[$field] = $postData[$field];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($templateData['markup']) && Config::get('cms.convertLineEndings', false) === true) {
|
||||
$templateData['markup'] = $this->convertLineEndings($templateData['markup']);
|
||||
}
|
||||
|
||||
if (!empty($templateData['code']) && Config::get('cms.convertLineEndings', false) === true) {
|
||||
$templateData['code'] = $this->convertLineEndings($templateData['code']);
|
||||
}
|
||||
|
||||
if (!Request::input('templateForceSave') && $template->mtime) {
|
||||
if (Request::input('templateMtime') != $template->mtime) {
|
||||
throw new ApplicationException('mtime-mismatch');
|
||||
}
|
||||
}
|
||||
|
||||
$template->attributes = [];
|
||||
$template->fill($templateData);
|
||||
$template->save();
|
||||
|
||||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
$this->fireSystemEvent('cms.template.save', [$template, $type]);
|
||||
|
||||
Flash::success(Lang::get('cms::lang.template.saved'));
|
||||
|
||||
$result = [
|
||||
'templatePath' => $template->fileName,
|
||||
'templateMtime' => $template->mtime,
|
||||
'tabTitle' => $this->getTabTitle($type, $template)
|
||||
];
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function onOpenConcurrencyResolveForm()
|
||||
{
|
||||
return $this->makePartial('concurrency_resolve_form');
|
||||
}
|
||||
|
||||
public function onCreateTemplate()
|
||||
{
|
||||
$type = Request::input('type');
|
||||
$template = $this->createTemplate($type);
|
||||
|
||||
if ($type === 'asset') {
|
||||
$template->fileName = $this->widget->assetList->getCurrentRelativePath();
|
||||
}
|
||||
|
||||
$widget = $this->makeTemplateFormWidget($type, $template);
|
||||
|
||||
$this->vars['templatePath'] = '';
|
||||
|
||||
return [
|
||||
'tabTitle' => $this->getTabTitle($type, $template),
|
||||
'tab' => $this->makePartial('form_page', [
|
||||
'form' => $widget,
|
||||
'templateType' => $type,
|
||||
'templateMtime' => null
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
public function onDeleteTemplates()
|
||||
{
|
||||
$type = Request::input('type');
|
||||
$templates = Request::input('template');
|
||||
$error = null;
|
||||
$deleted = [];
|
||||
|
||||
try {
|
||||
foreach ($templates as $path => $selected) {
|
||||
if ($selected) {
|
||||
$this->loadTemplate($type, $path)->delete();
|
||||
$deleted[] = $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$error = $ex->getMessage();
|
||||
}
|
||||
|
||||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
$this->fireSystemEvent('cms.template.delete', [$type]);
|
||||
|
||||
return [
|
||||
'deleted' => $deleted,
|
||||
'error' => $error,
|
||||
'theme' => Request::input('theme')
|
||||
];
|
||||
}
|
||||
|
||||
public function onDelete()
|
||||
{
|
||||
$type = Request::input('templateType');
|
||||
|
||||
$this->loadTemplate($type, trim(Request::input('templatePath')))->delete();
|
||||
|
||||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
$this->fireSystemEvent('cms.template.delete', [$type]);
|
||||
}
|
||||
|
||||
//
|
||||
// Methods for the internal use
|
||||
//
|
||||
|
||||
protected function resolveTypeClassName($type)
|
||||
{
|
||||
$types = [
|
||||
'asset' => Asset::class
|
||||
];
|
||||
|
||||
if (!array_key_exists($type, $types)) {
|
||||
throw new ApplicationException(trans('cms::lang.template.invalid_type'));
|
||||
}
|
||||
|
||||
return $types[$type];
|
||||
}
|
||||
|
||||
protected function loadTemplate($type, $path)
|
||||
{
|
||||
$class = $this->resolveTypeClassName($type);
|
||||
|
||||
if (!($template = call_user_func([$class, 'load'], $this->theme, $path))) {
|
||||
throw new ApplicationException(trans('cms::lang.template.not_found'));
|
||||
}
|
||||
|
||||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
$this->fireSystemEvent('cms.template.processSettingsAfterLoad', [$template]);
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
protected function createTemplate($type)
|
||||
{
|
||||
$class = $this->resolveTypeClassName($type);
|
||||
|
||||
if (!($template = $class::inTheme($this->theme))) {
|
||||
throw new ApplicationException(trans('cms::lang.template.not_found'));
|
||||
}
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
protected function getTabTitle($type, $template)
|
||||
{
|
||||
if ($type === 'asset') {
|
||||
$result = in_array($type, ['asset', 'content']) ? $template->getFileName() : $template->getBaseFileName();
|
||||
if (!$result) {
|
||||
$result = trans('cms::lang.'.$type.'.new');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $template->getFileName();
|
||||
}
|
||||
|
||||
protected function makeTemplateFormWidget($type, $template, $alias = null)
|
||||
{
|
||||
$formConfigs = [
|
||||
'asset' => '~/plugins/indikator/devtools/classes/asset/fields.yaml'
|
||||
];
|
||||
|
||||
if (!array_key_exists($type, $formConfigs)) {
|
||||
throw new ApplicationException(trans('cms::lang.template.not_found'));
|
||||
}
|
||||
|
||||
$widgetConfig = $this->makeConfig($formConfigs[$type]);
|
||||
$widgetConfig->model = $template;
|
||||
$widgetConfig->alias = $alias ?: 'form'.studly_case($type).md5($template->getFileName()).uniqid();
|
||||
|
||||
$widget = $this->makeWidget('Backend\Widgets\Form', $widgetConfig);
|
||||
|
||||
return $widget;
|
||||
}
|
||||
|
||||
protected function upgradeSettings($settings)
|
||||
{
|
||||
/*
|
||||
* Handle component usage
|
||||
*/
|
||||
$componentProperties = post('component_properties');
|
||||
$componentNames = post('component_names');
|
||||
$componentAliases = post('component_aliases');
|
||||
|
||||
if ($componentProperties !== null) {
|
||||
if ($componentNames === null || $componentAliases === null) {
|
||||
throw new ApplicationException(trans('cms::lang.component.invalid_request'));
|
||||
}
|
||||
|
||||
$count = count($componentProperties);
|
||||
if (count($componentNames) != $count || count($componentAliases) != $count) {
|
||||
throw new ApplicationException(trans('cms::lang.component.invalid_request'));
|
||||
}
|
||||
|
||||
for ($index = 0; $index < $count; $index++) {
|
||||
$componentName = $componentNames[$index];
|
||||
$componentAlias = $componentAliases[$index];
|
||||
|
||||
$section = $componentName;
|
||||
if ($componentAlias != $componentName) {
|
||||
$section .= ' '.$componentAlias;
|
||||
}
|
||||
|
||||
$properties = json_decode($componentProperties[$index], true);
|
||||
unset($properties['oc.alias']);
|
||||
unset($properties['inspectorProperty']);
|
||||
unset($properties['inspectorClassName']);
|
||||
$settings[$section] = $properties;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle view bag
|
||||
*/
|
||||
$viewBag = post('viewBag');
|
||||
if ($viewBag !== null) {
|
||||
$settings['viewBag'] = $viewBag;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
$dataHolder = (object) ['settings' => $settings];
|
||||
|
||||
$this->fireSystemEvent('cms.template.processSettingsBeforeSave', [$dataHolder]);
|
||||
|
||||
return $dataHolder->settings;
|
||||
}
|
||||
|
||||
protected function bindFormWidgetToController()
|
||||
{
|
||||
$alias = Request::input('formWidgetAlias');
|
||||
$type = Request::input('templateType');
|
||||
$object = $this->loadTemplate($type, Request::input('templatePath'));
|
||||
|
||||
$widget = $this->makeTemplateFormWidget($type, $object, $alias);
|
||||
$widget->bindToController();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces Windows style (/r/n) line endings with unix style (/n)
|
||||
* line endings.
|
||||
* @param string $markup The markup to convert to unix style endings
|
||||
* @return string
|
||||
*/
|
||||
protected function convertLineEndings($markup)
|
||||
{
|
||||
$markup = str_replace(["\r\n", "\r"], "\n", $markup);
|
||||
|
||||
return $markup;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?= Form::open(['onsubmit'=>'return false']) ?>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('backend::lang.form.concurrency_file_changed_title')) ?></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p><?= e(trans('backend::lang.form.concurrency_file_changed_description')) ?></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
data-action="reload"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.reload')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
data-action="save"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.save')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
<?= Form::close() ?>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<div class="form-buttons loading-indicator-container" data-toolbar-type="content">
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="btn btn-primary oc-icon-check save"
|
||||
data-request="onSave"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
data-hotkey="ctrl+s, cmd+s">
|
||||
<?= e(trans('backend::lang.form.save')) ?>
|
||||
</a>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-danger empty oc-icon-trash-o <?php if (!$templatePath): ?>hide<?php endif ?>"
|
||||
data-request="onDelete"
|
||||
data-request-confirm="<?= e(trans('cms::lang.content.delete_confirm_single')) ?>"
|
||||
data-request-success="$(this).trigger('close.oc.tab', [{force: true}])"
|
||||
data-control="delete-button"></button>
|
||||
|
||||
<?php if (isset($lastModified)): ?>
|
||||
<span
|
||||
class="btn empty oc-icon-calendar"
|
||||
title="<?= e(trans('backend::lang.media.last_modified')) ?>: <?= $lastModified ?>"
|
||||
data-toggle="tooltip"
|
||||
data-placement="right">
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?= Form::open([
|
||||
'class' => 'layout',
|
||||
'data-change-monitor' => 'true',
|
||||
'data-window-close-confirm' => e(trans('backend::lang.form.confirm_tab_close')),
|
||||
'data-inspector-external-parameters' => true
|
||||
]) ?>
|
||||
<?= $form->render() ?>
|
||||
|
||||
<input type="hidden" value="<?= e($form->alias) ?>" name="formWidgetAlias">
|
||||
<input type="hidden" value="<?= ($templateType) ?>" name="templateType">
|
||||
<input type="hidden" value="<?= ($templatePath) ?>" name="templatePath">
|
||||
<input type="hidden" value="<?= ($templateMtime) ?>" name="templateMtime">
|
||||
<input type="hidden" value="0" name="templateForceSave">
|
||||
|
||||
<?= Form::close() ?>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<div class="layout control-scrollpanel" id="cms-side-panel">
|
||||
<div class="layout-cell">
|
||||
<div class="layout-relative fix-button-container">
|
||||
<form
|
||||
class="layout"
|
||||
data-content-id="assets"
|
||||
data-template-type="asset"
|
||||
data-type-icon="oc-icon-cubes"
|
||||
onsubmit="return false">
|
||||
<?= $this->widget->assetList->render() ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?= Block::put('sidepanel') ?>
|
||||
<?php if (!$this->fatalError): ?>
|
||||
<?= $this->makePartial('sidepanel') ?>
|
||||
<?php endif ?>
|
||||
<?= Block::endPut() ?>
|
||||
|
||||
<?= Block::put('body') ?>
|
||||
<?php if (!$this->fatalError): ?>
|
||||
<div
|
||||
data-control="tab"
|
||||
data-closable
|
||||
data-close-confirmation="<?= e(trans('backend::lang.form.confirm_tab_close')) ?>"
|
||||
data-pane-classes="layout-cell"
|
||||
data-max-title-symbols="15"
|
||||
data-title-as-file-names="true"
|
||||
class="layout control-tabs master-tabs fancy-layout oc-logo-transparent"
|
||||
id="cms-master-tabs">
|
||||
|
||||
<div class="layout-row min-size">
|
||||
<div class="tabs-container">
|
||||
<ul class="nav nav-tabs"></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-content layout-row"></div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
<?php endif ?>
|
||||
<?= Block::endPut() ?>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php namespace Indikator\DevTools\FormWidgets;
|
||||
|
||||
use Backend\Classes\FormField;
|
||||
use Backend\Classes\FormWidgetBase;
|
||||
|
||||
class Help extends FormWidgetBase
|
||||
{
|
||||
protected $defaultAlias = 'help';
|
||||
|
||||
public $content = 'cms';
|
||||
|
||||
public function init()
|
||||
{
|
||||
$this->fillFromConfig([
|
||||
'content'
|
||||
]);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$this->prepareVars();
|
||||
|
||||
return $this->makePartial('help');
|
||||
}
|
||||
|
||||
protected function prepareVars()
|
||||
{
|
||||
$this->vars['content'] = $this->content;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
<?php if ($content == 'cms'): ?>
|
||||
<div class="row" style="padding:24px;">
|
||||
<div class="col-md-4 col-sm-4 col-xs-6">
|
||||
<strong><?= e(trans('indikator.devtools::lang.help.cms')) ?></strong><br>
|
||||
<a href="https://octobercms.com/docs/cms/pages#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.pages')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/cms/partials#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.partials')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/cms/layouts#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.layouts')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/cms/content#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.content')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/cms/ajax#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.ajax')) ?></a><br>
|
||||
<br>
|
||||
<strong><?= e(trans('indikator.devtools::lang.help.functions')) ?></strong><br>
|
||||
<a href="https://octobercms.com/docs/markup/function-str#search-input" target="_blank">str()</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/function-form#search-input" target="_blank">form()</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/function-html#search-input" target="_blank">html()</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/function-dump#search-input" target="_blank">dump()</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-sm-4 col-xs-6">
|
||||
<strong><?= e(trans('indikator.devtools::lang.help.tags')) ?></strong><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-partial#search-input" target="_blank">{% partial %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-content#search-input" target="_blank">{% content %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-component#search-input" target="_blank">{% component %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-placeholder#search-input" target="_blank">{% placeholder %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-scripts#search-input" target="_blank">{% scripts %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-styles#search-input" target="_blank">{% styles %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-flash#search-input" target="_blank">{% flash %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-verbatim#search-input" target="_blank">{% verbatim %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-for#search-input" target="_blank">{% for %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-if#search-input" target="_blank">{% if %}</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-sm-4 col-xs-6">
|
||||
<strong><?= e(trans('indikator.devtools::lang.help.filters')) ?></strong><br>
|
||||
<a href="https://octobercms.com/docs/markup/filter-app#search-input" target="_blank">|app</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/filter-page#search-input" target="_blank">|page</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/filter-theme#search-input" target="_blank">|theme</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/filter-media#search-input" target="_blank">|media</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/filter-md#search-input" target="_blank">|md</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/filter-raw#search-input" target="_blank">|raw</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/filter-default#search-input" target="_blank">|default</a><br>
|
||||
<br>
|
||||
<strong><?= e(trans('indikator.devtools::lang.help.database')) ?></strong><br>
|
||||
<a href="https://octobercms.com/docs/database/basics#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.basic')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/database/query#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.queries')) ?></a>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="row" style="padding:24px;">
|
||||
<div class="col-md-4 col-sm-4 col-xs-6">
|
||||
<strong><?= e(trans('indikator.devtools::lang.help.plugins')) ?></strong><br>
|
||||
<a href="https://octobercms.com/docs/plugin/registration#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.registration')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/plugin/updates#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.version_history')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/plugin/components#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.building_components')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/plugin/settings#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.settings_config')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/plugin/localization#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.localization')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/plugin/scheduling#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.task_scheduling')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/plugin/extending#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.extending_plugins')) ?></a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-sm-4 col-xs-6">
|
||||
<strong><?= e(trans('indikator.devtools::lang.help.backend')) ?></strong><br>
|
||||
<a href="https://octobercms.com/docs/backend/controllers-ajax#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.controllers_ajax')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/backend/views-partials#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.views_partials')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/backend/widgets#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.widgets')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/backend/forms#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.forms')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/backend/lists#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.lists')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/backend/relations#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.relations')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/backend/reorder#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.sorting_records')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/backend/import-export#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.importing_exporting')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/backend/users#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.users_permissions')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/ui#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.user_interface_guide')) ?></a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-sm-4 col-xs-6">
|
||||
<strong><?= e(trans('indikator.devtools::lang.help.services')) ?></strong><br>
|
||||
<a href="https://octobercms.com/docs/services/application#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.application')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/behaviors#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.behaviors')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/events#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.events')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/html#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.forms_html')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/mail#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.mail')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/request-input#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.request_input')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/response-view#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.response_view')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/router#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.router')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/session#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.session')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/validation#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.validation')) ?></a>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'plugin' => [
|
||||
'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',
|
||||
]
|
||||
];
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'plugin' => [
|
||||
'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'
|
||||
]
|
||||
];
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<?php namespace Indikator\DevTools\Models;
|
||||
|
||||
use Model;
|
||||
use Db;
|
||||
|
||||
class Settings extends Model
|
||||
{
|
||||
public $implement = ['System.Behaviors.SettingsModel'];
|
||||
|
||||
public $settingsCode = 'indikator_devtools_settings';
|
||||
|
||||
public $settingsFields = 'fields.yaml';
|
||||
|
||||
public $selectList = [0 => '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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
Route::get('/phpinfo', function()
|
||||
{
|
||||
$admin = BackendAuth::getUser();
|
||||
|
||||
if (isset($admin) && $admin->is_superuser == 1) {
|
||||
echo phpinfo();
|
||||
}
|
||||
});
|
||||
|
|
@ -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.
|
||||
|
|
@ -0,0 +1,709 @@
|
|||
<?php namespace Indikator\DevTools\Widgets;
|
||||
|
||||
use Str;
|
||||
use Url;
|
||||
use File;
|
||||
use Lang;
|
||||
use Input;
|
||||
use Config;
|
||||
use Request;
|
||||
use Response;
|
||||
use Validator;
|
||||
use Cms\Classes\Theme;
|
||||
use Indikator\DevTools\Classes\Asset;
|
||||
use Backend\Classes\WidgetBase;
|
||||
use System\Classes\PluginManager;
|
||||
use ApplicationException;
|
||||
use ValidationException;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
|
||||
use October\Rain\Filesystem\Definitions as FileDefinitions;
|
||||
use RecursiveIteratorIterator;
|
||||
use RecursiveDirectoryIterator;
|
||||
use DirectoryIterator;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* CMS asset list widget.
|
||||
*
|
||||
* @package october\cms
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class AssetList extends WidgetBase
|
||||
{
|
||||
use \Backend\Traits\SelectableWidget;
|
||||
|
||||
protected $searchTerm = false;
|
||||
|
||||
protected $theme;
|
||||
|
||||
/**
|
||||
* @var string Message to display when there are no records in the list.
|
||||
*/
|
||||
public $noRecordsMessage = 'cms::lang.asset.no_list_records';
|
||||
|
||||
/**
|
||||
* @var string Message to display when the Delete button is clicked.
|
||||
*/
|
||||
public $deleteConfirmation = 'cms::lang.asset.delete_confirm';
|
||||
|
||||
/**
|
||||
* @var array Valid asset file extensions
|
||||
*/
|
||||
protected $assetExtensions;
|
||||
|
||||
public function __construct($controller, $alias)
|
||||
{
|
||||
$this->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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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: $('<div />').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($('<div />').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);
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?= $this->makePartial('toolbar') ?>
|
||||
<div class="layout-row" id="asset-list-container" data-alias="<?= $this->alias ?>">
|
||||
<div class="layout-cell">
|
||||
<div class="layout-relative">
|
||||
<?= $this->makePartial('files', ['data'=>$data]) ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<div class="layout-absolute">
|
||||
<div class="control-scrollbar" data-control="scrollbar">
|
||||
<div class="control-assetlist" data-control="assetlist" id="<?= $this->getId('asset-list') ?>">
|
||||
<?= $this->makePartial('items', ['items'=>$data]) ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
$searchMode = $this->isSearchMode();
|
||||
|
||||
if (($upPath = $this->getUpPath()) !== null && !$searchMode):
|
||||
?>
|
||||
<p class="parent">
|
||||
<a href="<?= $upPath ?>" data-path="<?= $upPath ?>" class="link"><?= $this->getCurrentRelativePath() ?></a>
|
||||
</p>
|
||||
<?php endif ?>
|
||||
<div class="list-container animate">
|
||||
<?php if ($items): ?>
|
||||
<ul class="list">
|
||||
<?php foreach ($items as $item):
|
||||
$dataId = 'asset-'.ltrim($item->path, '/');
|
||||
?>
|
||||
<li class="<?= $item->type ?>" <?php if ($item->editable): ?>data-editable<?php endif ?> data-item-path="<?= e(ltrim($item->path, '/')) ?>" data-item-type="asset" data-id="<?= e($dataId) ?>">
|
||||
<a class="link" target="_blank" data-path="<?= $item->path ?>" href="<?= $this->getThemeFileUrl($item->path) ?>">
|
||||
<?= e($item->name) ?>
|
||||
|
||||
<?php if ($searchMode): ?>
|
||||
<span class="description">
|
||||
<?= e(dirname($item->path)) ?>
|
||||
</span>
|
||||
<?php endif ?>
|
||||
</a>
|
||||
|
||||
<div class="controls">
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="control icon btn-primary oc-icon-terminal"
|
||||
title="<?= e(trans('cms::lang.asset.rename')) ?>"
|
||||
data-control="popup"
|
||||
data-request-data="renamePath: '<?= e($item->path) ?>'"
|
||||
data-handler="<?= $this->getEventHandler('onLoadRenamePopup') ?>"
|
||||
><?= e(trans('cms::lang.asset.rename')) ?></a>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="file[<?= e($item->path) ?>]" value="0">
|
||||
<div class="checkbox custom-checkbox nolabel">
|
||||
<?php $cbId = 'cb'.md5($item->path) ?>
|
||||
<input
|
||||
id="<?= $cbId ?>"
|
||||
type="checkbox"
|
||||
name="file[<?= e($item->path) ?>]"
|
||||
<?= $this->isItemSelected($item->path) ? 'checked' : null ?>
|
||||
data-request="<?= $this->getEventHandler('onSelect') ?>"
|
||||
value="1">
|
||||
<label for="<?= $cbId ?>"><?= e(trans('cms::lang.asset.select')) ?></label>
|
||||
</div>
|
||||
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
<?php else: ?>
|
||||
<p class="no-data"><?= e(trans($this->noRecordsMessage)) ?></p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?= Form::open([
|
||||
'data-request'=>$this->getEventHandler('onMove'),
|
||||
'data-request-success'=>"\$(this).trigger('close.oc.popup')",
|
||||
'data-stripe-load-indicator'=>1,
|
||||
'id'=>'asset-move-popup-form'
|
||||
]) ?>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('cms::lang.asset.move_popup_title')) ?></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label><?= e(trans('cms::lang.asset.move_destination')) ?></label>
|
||||
<select
|
||||
class="form-control custom-select"
|
||||
name="dest"
|
||||
data-placeholder="<?= e(trans('cms::lang.asset.move_please_select')) ?>">
|
||||
<option></option>
|
||||
<?php foreach ($directories as $path=>$directory):?>
|
||||
<option value="<?= e($path) ?>"><?= e($directory) ?></option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="selectedList" value="<?= e($selectedList) ?>">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('cms::lang.asset.move_button')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
<?= Form::close() ?>
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?= Form::open([
|
||||
'data-request'=>$this->getEventHandler('onNewDirectory'),
|
||||
'data-request-success'=>"\$(this).trigger('close.oc.popup')",
|
||||
'data-stripe-load-indicator'=>1,
|
||||
'id'=>'asset-new-dir-popup-form'
|
||||
]) ?>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('cms::lang.asset.directory_popup_title')) ?></h4>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label><?= e(trans('cms::lang.asset.directory_name')) ?></label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value=""
|
||||
class="form-control"
|
||||
autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.create')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
setTimeout(
|
||||
function() {
|
||||
$('#asset-new-dir-popup-form input.form-control').focus()
|
||||
}, 310
|
||||
)
|
||||
</script>
|
||||
<?= Form::close() ?>
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?= Form::ajax($this->getEventHandler('onApplyName'), [
|
||||
'success' => "\$el.trigger('close.oc.popup');",
|
||||
'data-stripe-load-indicator' => 1,
|
||||
'id' => 'asset-rename-popup-form'
|
||||
]) ?>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('cms::lang.asset.rename_popup_title')) ?></h4>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label><?= e(trans('cms::lang.asset.rename_new_name')) ?></label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value="<?= e($name) ?>"
|
||||
class="form-control"
|
||||
autocomplete="off">
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="originalPath" value="<?= e($originalPath) ?>">
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.apply')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
setTimeout(
|
||||
function() {
|
||||
$('#asset-rename-popup-form input.form-control').focus()
|
||||
}, 310
|
||||
)
|
||||
</script>
|
||||
<?= Form::close() ?>
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
<div class="layout-row min-size">
|
||||
<div class="control-toolbar toolbar-padded">
|
||||
|
||||
<!-- Control Panel -->
|
||||
<div class="toolbar-item" data-calculate-width>
|
||||
<div class="btn-group">
|
||||
<div class="dropdown last">
|
||||
<button type="button" class="btn btn-default oc-icon-plus"
|
||||
data-control="create-asset"
|
||||
data-toggle="dropdown"
|
||||
><?= e(trans('cms::lang.sidebar.add')) ?></button>
|
||||
|
||||
<ul class="dropdown-menu offset-left" role="menu" data-dropdown-title="<?= e(trans('cms::lang.asset.drop_down_add_title')) ?>">
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1" href="javascript:;" data-control="create-template" class="oc-icon-file-text-o"><?= e(trans('cms::lang.asset.create_file')) ?></a></li>
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1" href="javascript:;" data-control="upload-assets" class="oc-icon-upload"><?= e(trans('cms::lang.asset.upload_files')) ?></a></li>
|
||||
<li role="presentation" class="divider"></li>
|
||||
<li role="presentation"><a
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
href="javascript:;"
|
||||
class="oc-icon-folder-o"
|
||||
data-control="popup"
|
||||
data-handler="<?= $this->getEventHandler('onLoadNewDirPopup') ?>"
|
||||
><?= e(trans('cms::lang.asset.create_directory')) ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="dropdown hide"
|
||||
id="<?= $this->getId('tools-button') ?>"
|
||||
data-trigger-action="show"
|
||||
data-trigger="<?= '#'.$this->getId('asset-list') ?> input[type=checkbox]"
|
||||
data-trigger-condition="checked">
|
||||
<button type="button" class="btn btn-default empty oc-icon-wrench last"
|
||||
data-toggle="dropdown"
|
||||
data-control="asset-tools"
|
||||
></button>
|
||||
|
||||
<ul class="dropdown-menu" role="menu" data-dropdown-title="<?= e(trans('cms::lang.asset.drop_down_operation_title')) ?>">
|
||||
<li role="presentation"><a
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
href="javascript:;"
|
||||
data-control="delete-asset"
|
||||
data-confirmation="<?= e(trans($this->deleteConfirmation)) ?>"
|
||||
class="oc-icon-trash-o"
|
||||
><?= e(trans('cms::lang.asset.delete')) ?></a></li>
|
||||
<li role="presentation"><a
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
href="javascript:;"
|
||||
class="oc-icon-angle-double-right"
|
||||
data-control="popup"
|
||||
data-handler="<?= $this->getEventHandler('onLoadMovePopup') ?>"
|
||||
><?= e(trans('cms::lang.asset.move')) ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Asset Search -->
|
||||
<div class="relative toolbar-item loading-indicator-container size-input-text">
|
||||
<input
|
||||
type="text"
|
||||
name="search"
|
||||
value="<?= e($this->getSearchTerm()) ?>"
|
||||
class="form-control icon search" autocomplete="off"
|
||||
placeholder="<?= e(trans('cms::lang.sidebar.search')) ?>"
|
||||
data-track-input
|
||||
data-load-indicator
|
||||
data-load-indicator-opaque
|
||||
data-request-success="$('<?= '#'.$this->getId('tools-button') ?>').trigger('oc.triggerOn.update')"
|
||||
data-request="<?= $this->getEventHandler('onSearch') ?>"
|
||||
>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -26,6 +26,9 @@ class Plugin extends PluginBase
|
|||
*/
|
||||
public function registerComponents()
|
||||
{
|
||||
return [
|
||||
'Romanah\Bagisto\Components\Products' => 'products'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Romanah\Bagisto\Components;
|
||||
|
||||
use Cms\Classes\ComponentBase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Redirect;
|
||||
use Flash;
|
||||
|
||||
class Products extends ComponentBase
|
||||
{
|
||||
|
||||
public function componentDetails()
|
||||
{
|
||||
return [
|
||||
'name' => 'Products',
|
||||
'description' => 'Fetch Products settings'
|
||||
];
|
||||
}
|
||||
|
||||
public $bagisto_url = 'https://nurgul.com.tm/app/api/';
|
||||
|
||||
public function onFetchProduct()
|
||||
{
|
||||
$response = Http::get('https://nurgul.com.tm/app/api/products?locale=tm&limit=4');
|
||||
// $response = getenv('APP_URL');
|
||||
// dd($response);
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php namespace Romanah\Bagisto\Controllers;
|
||||
|
||||
use Backend\Classes\Controller;
|
||||
use BackendMenu;
|
||||
|
||||
class Brand extends Controller
|
||||
{
|
||||
public $implement = [ 'Backend\Behaviors\ListController', 'Backend\Behaviors\FormController' ];
|
||||
|
||||
public $listConfig = 'config_list.yaml';
|
||||
public $formConfig = 'config_form.yaml';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
BackendMenu::setContext('Romanah.Bagisto', 'main-menu-item', 'side-menu-item');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php namespace Romanah\Bagisto\Controllers;
|
||||
|
||||
use Backend\Classes\Controller;
|
||||
use BackendMenu;
|
||||
|
||||
class Slider extends Controller
|
||||
{
|
||||
public $implement = [ 'Backend\Behaviors\ListController', 'Backend\Behaviors\FormController' ];
|
||||
|
||||
public $listConfig = 'config_list.yaml';
|
||||
public $formConfig = 'config_form.yaml';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
BackendMenu::setContext('Romanah.Bagisto', 'main-menu-item');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<div data-control="toolbar">
|
||||
<a href="<?= Backend::url('romanah/bagisto/brand/create') ?>" class="btn btn-primary oc-icon-plus"><?= e(trans('backend::lang.form.create')) ?></a>
|
||||
<button
|
||||
class="btn btn-default oc-icon-trash-o"
|
||||
disabled="disabled"
|
||||
onclick="$(this).data('request-data', {
|
||||
checked: $('.control-list').listWidget('getChecked')
|
||||
})"
|
||||
data-request="onDelete"
|
||||
data-request-confirm="<?= e(trans('backend::lang.list.delete_selected_confirm')) ?>"
|
||||
data-trigger-action="enable"
|
||||
data-trigger=".control-list input[type=checkbox]"
|
||||
data-trigger-condition="checked"
|
||||
data-request-success="$(this).prop('disabled', true)"
|
||||
data-stripe-load-indicator>
|
||||
<?= e(trans('backend::lang.list.delete_selected')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
name: Brand
|
||||
form: $/romanah/bagisto/models/brand/fields.yaml
|
||||
modelClass: Romanah\Bagisto\Models\Brand
|
||||
defaultRedirect: romanah/bagisto/brand
|
||||
create:
|
||||
redirect: 'romanah/bagisto/brand/update/:id'
|
||||
redirectClose: romanah/bagisto/brand
|
||||
update:
|
||||
redirect: romanah/bagisto/brand
|
||||
redirectClose: romanah/bagisto/brand
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
list: $/romanah/bagisto/models/brand/columns.yaml
|
||||
modelClass: Romanah\Bagisto\Models\Brand
|
||||
title: Brand
|
||||
noRecordsMessage: 'backend::lang.list.no_records'
|
||||
showSetup: true
|
||||
showCheckboxes: true
|
||||
recordsPerPage: 20
|
||||
toolbar:
|
||||
buttons: list_toolbar
|
||||
search:
|
||||
prompt: 'backend::lang.list.search_prompt'
|
||||
recordUrl: 'romanah/bagisto/brand/update/:id'
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?php Block::put('breadcrumb') ?>
|
||||
<ul>
|
||||
<li><a href="<?= Backend::url('romanah/bagisto/brand') ?>">Brand</a></li>
|
||||
<li><?= e($this->pageTitle) ?></li>
|
||||
</ul>
|
||||
<?php Block::endPut() ?>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<?= Form::open(['class' => 'layout']) ?>
|
||||
|
||||
<div class="layout-row">
|
||||
<?= $this->formRender() ?>
|
||||
</div>
|
||||
|
||||
<div class="form-buttons">
|
||||
<div class="loading-indicator-container">
|
||||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.create')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
class="btn btn-default">
|
||||
<?= e(trans('backend::lang.form.create_and_close')) ?>
|
||||
</button>
|
||||
<span class="btn-text">
|
||||
<?= e(trans('backend::lang.form.or')) ?> <a href="<?= Backend::url('romanah/bagisto/brand') ?>"><?= e(trans('backend::lang.form.cancel')) ?></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?= Form::close() ?>
|
||||
|
||||
<?php else: ?>
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
<p><a href="<?= Backend::url('romanah/bagisto/brand') ?>" class="btn btn-default"><?= e(trans('backend::lang.form.return_to_list')) ?></a></p>
|
||||
<?php endif ?>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<?= $this->listRender() ?>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?php Block::put('breadcrumb') ?>
|
||||
<ul>
|
||||
<li><a href="<?= Backend::url('romanah/bagisto/brand') ?>">Brand</a></li>
|
||||
<li><?= e($this->pageTitle) ?></li>
|
||||
</ul>
|
||||
<?php Block::endPut() ?>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<div class="form-preview">
|
||||
<?= $this->formRenderPreview() ?>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
|
||||
<?php endif ?>
|
||||
|
||||
<p>
|
||||
<a href="<?= Backend::url('romanah/bagisto/brand') ?>" class="btn btn-default oc-icon-chevron-left">
|
||||
<?= e(trans('backend::lang.form.return_to_list')) ?>
|
||||
</a>
|
||||
</p>
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
<?php Block::put('breadcrumb') ?>
|
||||
<ul>
|
||||
<li><a href="<?= Backend::url('romanah/bagisto/brand') ?>">Brand</a></li>
|
||||
<li><?= e($this->pageTitle) ?></li>
|
||||
</ul>
|
||||
<?php Block::endPut() ?>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<?= Form::open(['class' => 'layout']) ?>
|
||||
|
||||
<div class="layout-row">
|
||||
<?= $this->formRender() ?>
|
||||
</div>
|
||||
|
||||
<div class="form-buttons">
|
||||
<div class="loading-indicator-container">
|
||||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-data="redirect:0"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.save')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
class="btn btn-default">
|
||||
<?= e(trans('backend::lang.form.save_and_close')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="oc-icon-trash-o btn-icon danger pull-right"
|
||||
data-request="onDelete"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.deleting')) ?>"
|
||||
data-request-confirm="<?= e(trans('backend::lang.form.confirm_delete')) ?>">
|
||||
</button>
|
||||
|
||||
<span class="btn-text">
|
||||
<?= e(trans('backend::lang.form.or')) ?> <a href="<?= Backend::url('romanah/bagisto/brand') ?>"><?= e(trans('backend::lang.form.cancel')) ?></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<?= Form::close() ?>
|
||||
|
||||
<?php else: ?>
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
<p><a href="<?= Backend::url('romanah/bagisto/brand') ?>" class="btn btn-default"><?= e(trans('backend::lang.form.return_to_list')) ?></a></p>
|
||||
<?php endif ?>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<div data-control="toolbar">
|
||||
<a href="<?= Backend::url('romanah/bagisto/slider/create') ?>" class="btn btn-primary oc-icon-plus"><?= e(trans('backend::lang.form.create')) ?></a>
|
||||
<button
|
||||
class="btn btn-default oc-icon-trash-o"
|
||||
disabled="disabled"
|
||||
onclick="$(this).data('request-data', {
|
||||
checked: $('.control-list').listWidget('getChecked')
|
||||
})"
|
||||
data-request="onDelete"
|
||||
data-request-confirm="<?= e(trans('backend::lang.list.delete_selected_confirm')) ?>"
|
||||
data-trigger-action="enable"
|
||||
data-trigger=".control-list input[type=checkbox]"
|
||||
data-trigger-condition="checked"
|
||||
data-request-success="$(this).prop('disabled', true)"
|
||||
data-stripe-load-indicator>
|
||||
<?= e(trans('backend::lang.list.delete_selected')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
name: Slider
|
||||
form: $/romanah/bagisto/models/slider/fields.yaml
|
||||
modelClass: Romanah\Bagisto\Models\Slider
|
||||
defaultRedirect: romanah/bagisto/slider
|
||||
create:
|
||||
redirect: 'romanah/bagisto/slider/update/:id'
|
||||
redirectClose: romanah/bagisto/slider
|
||||
update:
|
||||
redirect: romanah/bagisto/slider
|
||||
redirectClose: romanah/bagisto/slider
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
list: $/romanah/bagisto/models/slider/columns.yaml
|
||||
modelClass: Romanah\Bagisto\Models\Slider
|
||||
title: Slider
|
||||
noRecordsMessage: 'backend::lang.list.no_records'
|
||||
showSetup: true
|
||||
showCheckboxes: true
|
||||
recordsPerPage: 20
|
||||
toolbar:
|
||||
buttons: list_toolbar
|
||||
search:
|
||||
prompt: 'backend::lang.list.search_prompt'
|
||||
recordUrl: 'romanah/bagisto/slider/update/:id'
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?php Block::put('breadcrumb') ?>
|
||||
<ul>
|
||||
<li><a href="<?= Backend::url('romanah/bagisto/slider') ?>">Slider</a></li>
|
||||
<li><?= e($this->pageTitle) ?></li>
|
||||
</ul>
|
||||
<?php Block::endPut() ?>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<?= Form::open(['class' => 'layout']) ?>
|
||||
|
||||
<div class="layout-row">
|
||||
<?= $this->formRender() ?>
|
||||
</div>
|
||||
|
||||
<div class="form-buttons">
|
||||
<div class="loading-indicator-container">
|
||||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.create')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
class="btn btn-default">
|
||||
<?= e(trans('backend::lang.form.create_and_close')) ?>
|
||||
</button>
|
||||
<span class="btn-text">
|
||||
<?= e(trans('backend::lang.form.or')) ?> <a href="<?= Backend::url('romanah/bagisto/slider') ?>"><?= e(trans('backend::lang.form.cancel')) ?></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?= Form::close() ?>
|
||||
|
||||
<?php else: ?>
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
<p><a href="<?= Backend::url('romanah/bagisto/slider') ?>" class="btn btn-default"><?= e(trans('backend::lang.form.return_to_list')) ?></a></p>
|
||||
<?php endif ?>
|
||||
|
|
@ -0,0 +1 @@
|
|||
<?= $this->listRender() ?>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?php Block::put('breadcrumb') ?>
|
||||
<ul>
|
||||
<li><a href="<?= Backend::url('romanah/bagisto/slider') ?>">Slider</a></li>
|
||||
<li><?= e($this->pageTitle) ?></li>
|
||||
</ul>
|
||||
<?php Block::endPut() ?>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<div class="form-preview">
|
||||
<?= $this->formRenderPreview() ?>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
|
||||
<?php endif ?>
|
||||
|
||||
<p>
|
||||
<a href="<?= Backend::url('romanah/bagisto/slider') ?>" class="btn btn-default oc-icon-chevron-left">
|
||||
<?= e(trans('backend::lang.form.return_to_list')) ?>
|
||||
</a>
|
||||
</p>
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
<?php Block::put('breadcrumb') ?>
|
||||
<ul>
|
||||
<li><a href="<?= Backend::url('romanah/bagisto/slider') ?>">Slider</a></li>
|
||||
<li><?= e($this->pageTitle) ?></li>
|
||||
</ul>
|
||||
<?php Block::endPut() ?>
|
||||
|
||||
<?php if (!$this->fatalError): ?>
|
||||
|
||||
<?= Form::open(['class' => 'layout']) ?>
|
||||
|
||||
<div class="layout-row">
|
||||
<?= $this->formRender() ?>
|
||||
</div>
|
||||
|
||||
<div class="form-buttons">
|
||||
<div class="loading-indicator-container">
|
||||
<button
|
||||
type="submit"
|
||||
data-request="onSave"
|
||||
data-request-data="redirect:0"
|
||||
data-hotkey="ctrl+s, cmd+s"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.save')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-request="onSave"
|
||||
data-request-data="close:1"
|
||||
data-hotkey="ctrl+enter, cmd+enter"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
class="btn btn-default">
|
||||
<?= e(trans('backend::lang.form.save_and_close')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="oc-icon-trash-o btn-icon danger pull-right"
|
||||
data-request="onDelete"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.deleting')) ?>"
|
||||
data-request-confirm="<?= e(trans('backend::lang.form.confirm_delete')) ?>">
|
||||
</button>
|
||||
|
||||
<span class="btn-text">
|
||||
<?= e(trans('backend::lang.form.or')) ?> <a href="<?= Backend::url('romanah/bagisto/slider') ?>"><?= e(trans('backend::lang.form.cancel')) ?></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<?= Form::close() ?>
|
||||
|
||||
<?php else: ?>
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
<p><a href="<?= Backend::url('romanah/bagisto/slider') ?>" class="btn btn-default"><?= e(trans('backend::lang.form.return_to_list')) ?></a></p>
|
||||
<?php endif ?>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php namespace Romanah\Bagisto\Models;
|
||||
|
||||
use Model;
|
||||
|
||||
/**
|
||||
* Model
|
||||
*/
|
||||
class Brand extends Model
|
||||
{
|
||||
use \October\Rain\Database\Traits\Validation;
|
||||
|
||||
use \October\Rain\Database\Traits\SoftDelete;
|
||||
|
||||
protected $dates = ['deleted_at'];
|
||||
|
||||
|
||||
/**
|
||||
* @var string The database table used by the model.
|
||||
*/
|
||||
public $table = 'romanah_bagisto_brand';
|
||||
|
||||
/**
|
||||
* @var array Validation rules
|
||||
*/
|
||||
public $rules = [
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php namespace Romanah\Bagisto\Models;
|
||||
|
||||
use Model;
|
||||
|
||||
/**
|
||||
* Model
|
||||
*/
|
||||
class Slider extends Model
|
||||
{
|
||||
use \October\Rain\Database\Traits\Validation;
|
||||
|
||||
use \October\Rain\Database\Traits\SoftDelete;
|
||||
|
||||
protected $dates = ['deleted_at'];
|
||||
|
||||
|
||||
/**
|
||||
* @var string The database table used by the model.
|
||||
*/
|
||||
public $table = 'romanah_bagisto_slider';
|
||||
|
||||
/**
|
||||
* @var array Validation rules
|
||||
*/
|
||||
public $rules = [
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
columns:
|
||||
id:
|
||||
label: id
|
||||
type: number
|
||||
img:
|
||||
label: img
|
||||
type: text
|
||||
note:
|
||||
label: note
|
||||
type: text
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
fields:
|
||||
img:
|
||||
label: Img
|
||||
span: auto
|
||||
type: mediafinder
|
||||
note:
|
||||
label: Note
|
||||
span: auto
|
||||
type: text
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
columns:
|
||||
id:
|
||||
label: id
|
||||
type: number
|
||||
header:
|
||||
label: header
|
||||
type: text
|
||||
header2:
|
||||
label: header2
|
||||
type: text
|
||||
txt:
|
||||
label: txt
|
||||
type: text
|
||||
btn_txt:
|
||||
label: btn_txt
|
||||
type: text
|
||||
url:
|
||||
label: url
|
||||
type: text
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
fields:
|
||||
img:
|
||||
label: Img
|
||||
span: auto
|
||||
type: mediafinder
|
||||
header:
|
||||
label: Header
|
||||
span: auto
|
||||
type: text
|
||||
header2:
|
||||
label: Header2
|
||||
span: auto
|
||||
type: text
|
||||
txt:
|
||||
label: Txt
|
||||
span: auto
|
||||
type: text
|
||||
btn_txt:
|
||||
label: 'Btn txt'
|
||||
span: auto
|
||||
type: text
|
||||
url:
|
||||
label: Url
|
||||
span: auto
|
||||
type: text
|
||||
|
|
@ -4,3 +4,13 @@ plugin:
|
|||
author: Romanah
|
||||
icon: oc-icon-cart-plus
|
||||
homepage: ''
|
||||
navigation:
|
||||
main-menu-item:
|
||||
label: 'Slider (Nurgul)'
|
||||
url: romanah/bagisto/slider
|
||||
icon: icon-image
|
||||
sideMenu:
|
||||
side-menu-item:
|
||||
label: Brendlar
|
||||
url: romanah/bagisto/brand
|
||||
icon: icon-slack
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
<?php namespace Romanah\Bagisto\Updates;
|
||||
|
||||
use Schema;
|
||||
use October\Rain\Database\Updates\Migration;
|
||||
|
||||
class BuilderTableCreateRomanahBagistoBrand extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('romanah_bagisto_brand', function($table)
|
||||
{
|
||||
$table->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('note')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('romanah_bagisto_brand');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php namespace Romanah\Bagisto\Updates;
|
||||
|
||||
use Schema;
|
||||
use October\Rain\Database\Updates\Migration;
|
||||
|
||||
class BuilderTableCreateRomanahBagistoSlider extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('romanah_bagisto_slider', function($table)
|
||||
{
|
||||
$table->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('header')->nullable();
|
||||
$table->string('header2')->nullable();
|
||||
$table->text('txt')->nullable();
|
||||
$table->string('btn_txt')->nullable();
|
||||
$table->string('url')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('romanah_bagisto_slider');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +1,8 @@
|
|||
v1.0.1:
|
||||
1.0.1:
|
||||
- 'Initialize plugin'
|
||||
1.0.2:
|
||||
- 'Created table romanah_bagisto_slider'
|
||||
- builder_table_create_romanah_bagisto_slider.php
|
||||
1.0.3:
|
||||
- 'Created table romanah_bagisto_brand'
|
||||
- builder_table_create_romanah_bagisto_brand.php
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -25,6 +25,8 @@ code = "top-menu"
|
|||
<link rel="stylesheet" href="{{'assets/css/style.css'|theme}}">
|
||||
<!-- Responsive css -->
|
||||
<link rel="stylesheet" href="{{'assets/css/responsive.css'|theme}}">
|
||||
|
||||
{% styles %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
@ -60,10 +62,15 @@ code = "top-menu"
|
|||
<!-- preloader area end -->
|
||||
|
||||
<!-- All JS Plugins -->
|
||||
<script src="{{'assets/jquery.js'|theme}}"></script>
|
||||
<script src="{{'assets/js/plugins.js'|theme}}"></script>
|
||||
<!-- Main JS -->
|
||||
<script src="{{'assets/js/main.js'|theme}}"></script>
|
||||
|
||||
{% framework %}
|
||||
|
||||
{% scripts %} %}
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -1,14 +1,15 @@
|
|||
url = "/"
|
||||
layout = "main"
|
||||
title = "Home"
|
||||
==
|
||||
|
||||
[products]
|
||||
==
|
||||
{% partial 'home/slider' %}
|
||||
|
||||
{% partial 'home/banner' %}
|
||||
|
||||
{% partial 'home/banner2' %}
|
||||
|
||||
<button data-request="onFetchProduct">TESTQ</button>
|
||||
{% partial 'home/new-products' header='New Products' %}
|
||||
|
||||
{% partial 'home/banner-mix' %}
|
||||
|
|
@ -23,7 +24,7 @@ title = "Home"
|
|||
<div class="col-md-12">
|
||||
<div class="ltn__banner-item">
|
||||
<div class="ltn__banner-img">
|
||||
<a href="shop.html"><img src="{{'assets/img/banner/10.jpg'|theme}}" alt="Banner Image"></a>
|
||||
<a href="/"><img src="{{'banner-bottom/10.jpg'|media}}" alt="Nurgul"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -33,4 +34,4 @@ title = "Home"
|
|||
<!-- BANNER AREA END -->
|
||||
|
||||
|
||||
{% partial 'home/brand' %}
|
||||
{% partial 'home/brand' %}
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
<div class="col-md-6 col-12">
|
||||
<div class="footer-copyright-left">
|
||||
<div class="ltn__copyright-design clearfix">
|
||||
<p>© <span class="current-year"></span> - Just For You</p>
|
||||
<p>© <span class="current-year"></span> - {{'footer.privacy'|_}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -15,20 +15,20 @@
|
|||
<div class="footer-copyright-right text-right">
|
||||
<div class="ltn__copyright-menu d-none">
|
||||
<ul>
|
||||
<li><a href="index.html#">Terms & Conditions</a></li>
|
||||
<li><a href="index.html#">Claim</a></li>
|
||||
<li><a href="index.html#">Privacy & Policy</a></li>
|
||||
<li><a href="#">Terms & Conditions</a></li>
|
||||
<li><a href="#">Claim</a></li>
|
||||
<li><a href="#">Privacy & Policy</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="ltn__social-media ">
|
||||
<ul>
|
||||
<li><a href="index.html#" title="Facebook"><i
|
||||
<li><a href="" title="Facebook"><i
|
||||
class="icon-social-facebook"></i></a></li>
|
||||
<li><a href="index.html#" title="Twitter"><i
|
||||
<li><a href="#" title="Twitter"><i
|
||||
class="icon-social-twitter"></i></a></li>
|
||||
<li><a href="index.html#" title="Pinterest"><i
|
||||
<li><a href="#" title="Pinterest"><i
|
||||
class="icon-social-pinterest"></i></a></li>
|
||||
<li><a href="index.html#" title="Instagram"><i
|
||||
<li><a href="#" title="Instagram"><i
|
||||
class="icon-social-instagram"></i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -38,4 +38,4 @@
|
|||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<!-- FOOTER AREA END -->
|
||||
<!-- FOOTER AREA END -->
|
||||
|
|
@ -5,14 +5,14 @@
|
|||
<div class="col-md-6">
|
||||
<div class="ltn__banner-item">
|
||||
<div class="ltn__banner-img">
|
||||
<a href="shop.html"><img src="{{'assets/img/banner/6.jpg'|theme}}" alt="Banner Image"></a>
|
||||
<a href="/"><img src="{{'banner-center/8.jpg'|media}}" alt="Nurgul"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="ltn__banner-item">
|
||||
<div class="ltn__banner-img">
|
||||
<a href="shop.html"><img src="{{'assets/img/banner/7.jpg'|theme}}" alt="Banner Image"></a>
|
||||
<a href="/"><img src="{{'banner-center/9.jpg'|media}}" alt="Nurgul"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@
|
|||
<img src="{{'assets/img/icons/svg/8-trolley.svg'|theme}}" alt="#">
|
||||
</div>
|
||||
<div class="ltn__feature-info">
|
||||
<h4>Free shipping</h4>
|
||||
<p>On all orders over $49.00</p>
|
||||
<h4>{{'home.free.shipping.title'|_}}</h4>
|
||||
<p>{{'home.free.shipping.txt'|_}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ltn__feature-item ltn__feature-item-8">
|
||||
|
|
@ -19,8 +19,8 @@
|
|||
<img src="{{'assets/img/icons/svg/9-money.svg'|theme}}" alt="#">
|
||||
</div>
|
||||
<div class="ltn__feature-info">
|
||||
<h4>15 days returns</h4>
|
||||
<p>Moneyback guarantee</p>
|
||||
<h4>{{'home.title.second'|_}}</h4>
|
||||
<p>{{'home.desc.second'|_}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ltn__feature-item ltn__feature-item-8">
|
||||
|
|
@ -28,8 +28,8 @@
|
|||
<img src="{{'assets/img/icons/svg/10-credit-card.svg'|theme}}" alt="#">
|
||||
</div>
|
||||
<div class="ltn__feature-info">
|
||||
<h4>Secure checkout</h4>
|
||||
<p>Protected by Paypal</p>
|
||||
<h4>{{'home.title.third'|_}}</h4>
|
||||
<p>{{'home.desc.third'|_}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ltn__feature-item ltn__feature-item-8">
|
||||
|
|
@ -37,8 +37,8 @@
|
|||
<img src="{{'assets/img/icons/svg/11-gift-card.svg'|theme}}" alt="#">
|
||||
</div>
|
||||
<div class="ltn__feature-info">
|
||||
<h4>Offer & gift here</h4>
|
||||
<p>On all orders over</p>
|
||||
<h4>{{'home.title.fourth'|_}}</h4>
|
||||
<p>{{'home.desc.fourth'|_}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -46,4 +46,4 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- FEATURE AREA END -->
|
||||
<!-- FEATURE AREA END -->
|
||||
|
|
@ -5,25 +5,25 @@
|
|||
<div class="col-lg-4 col-md-6">
|
||||
<div class="ltn__banner-item">
|
||||
<div class="ltn__banner-img">
|
||||
<a href="shop.html"><img src="{{'assets/img/banner/1.jpg'|theme}}" alt="Banner Image"></a>
|
||||
<a href="/"><img src="{{'banner/1.jpg'|media}}" alt="Nurgul"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="ltn__banner-item">
|
||||
<div class="ltn__banner-img">
|
||||
<a href="shop.html"><img src="{{'assets/img/banner/2.jpg'|theme}}" alt="Banner Image"></a>
|
||||
<a href="/"><img src="{{'banner/2.jpg'|media}}" alt="Nurgul"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="ltn__banner-item">
|
||||
<div class="ltn__banner-img">
|
||||
<a href="shop.html"><img src="{{'assets/img/banner/3.jpg'|theme}}" alt="Banner Image"></a>
|
||||
<a href="/"><img src="{{'banner/3.jpg'|media}}" alt="Nurgul"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- BANNER AREA END -->
|
||||
<!-- BANNER AREA END -->
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<div class="col-lg-12">
|
||||
<div class="ltn__brand-logo-item">
|
||||
<img src="{{'assets/img/brand-logo/1.png'|theme}}" alt="Brand Logo">
|
||||
<img src="{{img|media}}" alt="Brand Logo">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,20 +1,31 @@
|
|||
[builderList]
|
||||
modelClass = "Romanah\Bagisto\Models\Brand"
|
||||
scope = "-"
|
||||
scopeValue = "{{ :scope }}"
|
||||
displayColumn = "id"
|
||||
noRecordsMessage = "No records found"
|
||||
detailsPage = "-"
|
||||
detailsUrlParameter = "id"
|
||||
pageNumber = "{{ :page }}"
|
||||
==
|
||||
{% set records = builderList.records %}
|
||||
{% set displayColumn = builderList.displayColumn %}
|
||||
{% set noRecordsMessage = builderList.noRecordsMessage %}
|
||||
{% set detailsPage = builderList.detailsPage %}
|
||||
{% set detailsKeyColumn = builderList.detailsKeyColumn %}
|
||||
{% set detailsUrlParameter = builderList.detailsUrlParameter %}
|
||||
|
||||
|
||||
<!-- BRAND LOGO AREA START -->
|
||||
<div class="ltn__brand-logo-area ltn__brand-logo-1 section-bg-1 pt-35 pb-35 plr--5">
|
||||
<div class="container-fluid">
|
||||
<div class="row ltn__brand-logo-active">
|
||||
|
||||
{% partial "home/brand-item" %}
|
||||
{% partial "home/brand-item" %}
|
||||
{% partial "home/brand-item" %}
|
||||
{% partial "home/brand-item" %}
|
||||
{% partial "home/brand-item" %}
|
||||
{% partial "home/brand-item" %}
|
||||
{% partial "home/brand-item" %}
|
||||
{% partial "home/brand-item" %}
|
||||
{% partial "home/brand-item" %}
|
||||
{% partial "home/brand-item" %}
|
||||
{% for record in records %}
|
||||
{% partial "home/brand-item" img=record.img %}
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- BRAND LOGO AREA END -->
|
||||
<!-- BRAND LOGO AREA END -->
|
||||
|
|
@ -1,77 +1,63 @@
|
|||
[builderList]
|
||||
modelClass = "Romanah\Bagisto\Models\Slider"
|
||||
scope = "-"
|
||||
scopeValue = "{{ :scope }}"
|
||||
displayColumn = "id"
|
||||
noRecordsMessage = "No records found"
|
||||
detailsPage = "-"
|
||||
detailsUrlParameter = "id"
|
||||
pageNumber = "{{ :page }}"
|
||||
==
|
||||
{% set records = builderList.records %}
|
||||
{% set displayColumn = builderList.displayColumn %}
|
||||
{% set noRecordsMessage = builderList.noRecordsMessage %}
|
||||
{% set detailsPage = builderList.detailsPage %}
|
||||
{% set detailsKeyColumn = builderList.detailsKeyColumn %}
|
||||
{% set detailsUrlParameter = builderList.detailsUrlParameter %}
|
||||
|
||||
<!-- SLIDER AREA START (slider-6) -->
|
||||
<div class="ltn__slider-area ltn__slider-3 ltn__slider-6 section-bg-1">
|
||||
<div class="ltn__slide-one-active slick-slide-arrow-1 slick-slide-dots-1 arrow-white---">
|
||||
<!-- ltn__slide-item -->
|
||||
<div class="ltn__slide-item ltn__slide-item-8 text-color-white---- bg-image bg-overlay-theme-black-80---"
|
||||
data-bs-bg="{{'assets/img/slider/1.jpg'|theme}}">
|
||||
<div class="ltn__slide-item-inner">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12 align-self-center">
|
||||
<div class="slide-item-info">
|
||||
<div class="slide-item-info-inner ltn__slide-animation">
|
||||
<div class="slide-item-info">
|
||||
<div class="slide-item-info-inner ltn__slide-animation">
|
||||
<h1 class="slide-title animated ">{{'Fresh Flower'|_}}</h1>
|
||||
<h6 class="slide-sub-title ltn__body-color slide-title-line animated">
|
||||
Natural & Beautiful Flower Here</h6>
|
||||
<div class="slide-brief animated">
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed
|
||||
do eiusmod tempor incididunt ut labore.</p>
|
||||
</div>
|
||||
<div class="btn-wrapper animated">
|
||||
<a href="https://tunatheme.com/tf/html/fiama-preview/fiama/service.html"
|
||||
class="theme-btn-1 btn btn-round">Shop Now</a>
|
||||
|
||||
{% for record in records %}
|
||||
|
||||
<div class="ltn__slide-item ltn__slide-item-8 text-color-white---- bg-image bg-overlay-theme-black-80---"
|
||||
data-bs-bg="{{record.img|media}}">
|
||||
<div class="ltn__slide-item-inner">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12 align-self-center">
|
||||
<div class="slide-item-info">
|
||||
<div class="slide-item-info-inner ltn__slide-animation">
|
||||
<div class="slide-item-info">
|
||||
<div class="slide-item-info-inner ltn__slide-animation">
|
||||
<h1 class="slide-title animated ">{{record.header}}</h1>
|
||||
<h6 class="slide-sub-title ltn__body-color slide-title-line animated">
|
||||
{{record.header2}}</h6>
|
||||
<div class="slide-brief animated">
|
||||
<p>{{record.txt}}</p>
|
||||
</div>
|
||||
<div class="btn-wrapper animated">
|
||||
<a href="{{record.url}}"
|
||||
class="theme-btn-1 btn btn-round">{{record.btn_txt}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="slide-item-img">
|
||||
<img src="img/slider/41-1.png" alt="#">
|
||||
<span class="call-to-circle-1"></span>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="slide-item-img">
|
||||
<img src="img/slider/41-1.png" alt="#">
|
||||
<span class="call-to-circle-1"></span>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ltn__slide-item -->
|
||||
<div class="ltn__slide-item ltn__slide-item-8 text-color-white---- bg-image bg-overlay-theme-black-80---"
|
||||
data-bs-bg="{{'assets/img/slider/3.jpg'|theme}}">
|
||||
<div class="ltn__slide-item-inner">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12 align-self-center">
|
||||
<div class="slide-item-info">
|
||||
<div class="slide-item-info-inner ltn__slide-animation">
|
||||
<div class="slide-item-info">
|
||||
<div class="slide-item-info-inner ltn__slide-animation">
|
||||
<h1 class="slide-title animated ">Fresh Flower</h1>
|
||||
<h6 class="slide-sub-title ltn__body-color slide-title-line animated">
|
||||
Natural & Beautiful Flower Here</h6>
|
||||
<div class="slide-brief animated">
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed
|
||||
do eiusmod tempor incididunt ut labore.</p>
|
||||
</div>
|
||||
<div class="btn-wrapper animated">
|
||||
<a href="https://tunatheme.com/tf/html/fiama-preview/fiama/service.html"
|
||||
class="theme-btn-1 btn btn-round">Shop Now</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="slide-item-img">
|
||||
<img src="img/slider/41-1.png" alt="#">
|
||||
<span class="call-to-circle-1"></span>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- -->
|
||||
|
||||
{% endfor %}
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- SLIDER AREA END -->
|
||||
Loading…
Reference in New Issue