Updating the Settings pages

This commit is contained in:
alekseybobkov 2014-07-27 11:11:16 +11:00
parent 1085d20f8c
commit bfc59c9007
14 changed files with 417 additions and 33 deletions

View File

@ -101,7 +101,8 @@ class ServiceProvider extends ModuleServiceProvider
'icon' => 'icon-user',
'url' => Backend::URL('backend/users/myaccount'),
'order' => 400,
'context' => 'mysettings'
'context' => 'mysettings',
'keywords' => 'backend::lang.myaccount.menu_keywords',
],
]);
});

View File

@ -12893,6 +12893,7 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover {
padding: 15px 15px 15px 33px;
margin: 0;
position: relative;
cursor: pointer;
}
.sidenav-tree ul.top-level > li > div.group h3:before {
width: 10px;

View File

@ -0,0 +1,248 @@
/*
* Side navigation tree
*
* Data attributes:
* - data-control="sidenav-tree" - enables the plugin
* - data-tree-name - unique name of the tree control. The name is used for storing user configuration in the browser cookies.
*
* JavaScript API:
* $('#tree').sidenavTree()
*
* Dependences:
* - Null
*/
+function ($) { "use strict";
// SIDENAVTREE CLASS DEFINITION
// ============================
var SidenavTree = function(element, options) {
this.options = options
this.$el = $(element)
this.init();
}
SidenavTree.DEFAULTS = {
treeName: 'sidenav_tree'
}
SidenavTree.prototype.init = function (){
var self = this
this.statusCookieName = this.options.treeName + 'groupStatus'
this.searchCookieName = this.options.treeName + 'search'
this.$searchInput = $(this.options.searchInput)
this.$el.on('click', 'li > div.group', function() {
self.toggleGroup($(this).closest('li'))
return false;
});
this.$searchInput.on('keyup', function(){
self.handleSearchChange()
})
var searchTerm = $.cookie(this.searchCookieName)
if (searchTerm !== undefined && searchTerm.length > 0) {
this.$searchInput.val(searchTerm)
this.applySearch()
}
}
SidenavTree.prototype.toggleGroup = function(group) {
var $group = $(group),
status = $group.attr('data-status')
status === undefined || status == 'expanded' ?
this.collapseGroup($group) :
this.expandGroup($group)
}
SidenavTree.prototype.collapseGroup = function(group) {
var
$list = $('> ul', group),
self = this;
$list.css('overflow', 'hidden')
$list.animate({'height': 0}, { duration: 100, queue: false, complete: function() {
$list.css({
'overflow': 'visible',
'display': 'none'
})
$(group).attr('data-status', 'collapsed')
$(window).trigger('oc.updateUi')
self.saveGroupStatus($(group).data('group-code'), true)
} })
}
SidenavTree.prototype.expandGroup = function(group, duration) {
var
$list = $('> ul', group),
self = this
duration = duration === undefined ? 100 : duration
$list.css({
'overflow': 'hidden',
'display': 'block',
'height': 0
})
$list.animate({'height': $list[0].scrollHeight}, { duration: duration, queue: false, complete: function() {
$list.css({
'overflow': 'visible',
'height': 'auto'
})
$(group).attr('data-status', 'expanded')
$(window).trigger('oc.updateUi')
self.saveGroupStatus($(group).data('group-code'), false)
} })
}
SidenavTree.prototype.saveGroupStatus = function(groupCode, collapsed) {
var collapsedGroups = $.cookie(this.statusCookieName),
updatedGroups = []
if (collapsedGroups === undefined)
collapsedGroups = ''
collapsedGroups = collapsedGroups.split('|')
$.each(collapsedGroups, function() {
if (groupCode != this)
updatedGroups.push(this)
})
if (collapsed)
updatedGroups.push(groupCode)
$.cookie(this.statusCookieName, updatedGroups.join('|'), { expires: 30, path: '/' })
}
SidenavTree.prototype.handleSearchChange = function() {
var lastValue = this.$searchInput.data('oc.lastvalue');
if (lastValue !== undefined && lastValue == this.$searchInput.val())
return
this.$searchInput.data('oc.lastvalue', this.$searchInput.val())
if (this.dataTrackInputTimer !== undefined)
window.clearTimeout(this.dataTrackInputTimer);
var self = this
this.dataTrackInputTimer = window.setTimeout(function(){
self.applySearch()
}, 300);
$.cookie(this.searchCookieName, $.trim(this.$searchInput.val()), { expires: 30, path: '/' })
}
SidenavTree.prototype.applySearch = function() {
var query = $.trim(this.$searchInput.val()),
words = query.toLowerCase().split(' '),
visibleGroups = [],
visibleItems = [],
self = this
if (query.length == 0) {
$('li', this.$el).removeClass('hidden')
return
}
// Find visible groups and items
//
$('ul.top-level > li', this.$el).each(function() {
var $li = $(this)
if (self.textContainsWords($('div.group h3', $li).text(), words)) {
visibleGroups.push($li.get(0))
$('ul li', $li).each(function(){
visibleItems.push(this)
})
} else {
$('ul li', $li).each(function(){
if (self.textContainsWords($(this).text(), words) || self.textContainsWords($(this).data('keywords'), words)) {
visibleGroups.push($li.get(0))
visibleItems.push(this)
}
})
}
})
// Hide invisible groups and items
//
$('ul.top-level > li', this.$el).each(function() {
var $li = $(this),
groupIsVisible = $.inArray(this, visibleGroups) !== -1
$li.toggleClass('hidden', !groupIsVisible)
if (groupIsVisible)
self.expandGroup($li, 0)
$('ul li', $li).each(function(){
var $itemLi = $(this)
$itemLi.toggleClass('hidden', $.inArray(this, visibleItems) == -1)
})
})
return false
}
SidenavTree.prototype.textContainsWords = function(text, words) {
text = text.toLowerCase()
for (var i = 0; i < words.length; i++) {
if (text.indexOf(words[i]) === -1)
return false
}
return true
}
// SIDENAVTREE PLUGIN DEFINITION
// ============================
var old = $.fn.sidenavTree
$.fn.sidenavTree = function (option) {
var args = arguments;
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.sidenavTree')
var options = $.extend({}, SidenavTree.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.sidenavTree', (data = new SidenavTree(this, options)))
if (typeof option == 'string') {
var methodArgs = [];
for (var i=1; i<args.length; i++)
methodArgs.push(args[i])
data[option].apply(data, methodArgs)
}
})
}
$.fn.sidenavTree.Constructor = SidenavTree
// SIDENAVREE NO CONFLICT
// =================
$.fn.sidenavTree.noConflict = function () {
$.fn.sidenavTree = old
return this
}
// SIDENAVTREE DATA-API
// ===============
$(document).ready(function () {
$('[data-control=sidenav-tree]').sidenavTree()
})
}(window.jQuery);

View File

@ -0,0 +1,117 @@
/*!
* jQuery Cookie Plugin v1.4.1
* https://github.com/carhartl/jquery-cookie
*
* Copyright 2006, 2014 Klaus Hartl
* Released under the MIT license
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// CommonJS
factory(require('jquery'));
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
var pluses = /\+/g;
function encode(s) {
return config.raw ? s : encodeURIComponent(s);
}
function decode(s) {
return config.raw ? s : decodeURIComponent(s);
}
function stringifyCookieValue(value) {
return encode(config.json ? JSON.stringify(value) : String(value));
}
function parseCookieValue(s) {
if (s.indexOf('"') === 0) {
// This is a quoted cookie as according to RFC2068, unescape...
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
}
try {
// Replace server-side written pluses with spaces.
// If we can't decode the cookie, ignore it, it's unusable.
// If we can't parse the cookie, ignore it, it's unusable.
s = decodeURIComponent(s.replace(pluses, ' '));
return config.json ? JSON.parse(s) : s;
} catch(e) {}
}
function read(s, converter) {
var value = config.raw ? s : parseCookieValue(s);
return $.isFunction(converter) ? converter(value) : value;
}
var config = $.cookie = function (key, value, options) {
// Write
if (arguments.length > 1 && !$.isFunction(value)) {
options = $.extend({}, config.defaults, options);
if (typeof options.expires === 'number') {
var days = options.expires, t = options.expires = new Date();
t.setTime(+t + days * 864e+5);
}
return (document.cookie = [
encode(key), '=', stringifyCookieValue(value),
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
options.path ? '; path=' + options.path : '',
options.domain ? '; domain=' + options.domain : '',
options.secure ? '; secure' : ''
].join(''));
}
// Read
var result = key ? undefined : {};
// To prevent the for loop in the first place assign an empty array
// in case there are no cookies at all. Also prevents odd result when
// calling $.cookie().
var cookies = document.cookie ? document.cookie.split('; ') : [];
for (var i = 0, l = cookies.length; i < l; i++) {
var parts = cookies[i].split('=');
var name = decode(parts.shift());
var cookie = parts.join('=');
if (key && key === name) {
// If second argument (value) is a function it's a converter...
result = read(cookie, value);
break;
}
// Prevent storing a cookie that we couldn't decode.
if (!key && (cookie = read(cookie)) !== undefined) {
result[name] = cookie;
}
}
return result;
};
config.defaults = {};
$.removeCookie = function (key, options) {
if ($.cookie(key) === undefined) {
return false;
}
// Must not alter options, thus extending a fresh object...
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
return !$.cookie(key);
};
}));

View File

@ -40,6 +40,7 @@
padding: 15px 15px 15px 33px;
margin: 0;
position: relative;
cursor: pointer;
&:before {
width: 10px;

View File

@ -49,6 +49,7 @@ return [
'user' => [
'name' => 'Administrator',
'menu_label' => 'Administrators',
'menu_description' => 'Manage back-end administrator users, groups and permissions.',
'list_title' => 'Manage Administrators',
'new' => 'New Administrator',
'login' => "Login",
@ -178,6 +179,7 @@ return [
'myaccount' => [
'menu_label' => 'My Account',
'menu_description' => 'Update your account details such as name, email address and password.',
'menu_keywords' => 'security login'
],
'backend_preferences' => [
'menu_label' => 'Backend Preferences',

View File

@ -18,6 +18,8 @@
<script src="<?= URL::asset('modules/backend/assets/js/vendor/raphael-min.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/vendor/jquery.autoellipsis.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/vendor/jquery.waterfall.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/vendor/jquery.cookie.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/vendor/select2/select2.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/vendor/mustache/mustache.min.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/vendor/fileupload/jquery.fileupload.js') ?>"></script>
@ -69,7 +71,7 @@
<script src="<?= URL::asset('modules/backend/assets/js/october.treelist.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.autocomplete.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.callout.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.sidenav-tree.js') ?>"></script>
<script>
<!--

View File

@ -132,29 +132,7 @@ class ServiceProvider extends ModuleServiceProvider
'icon' => 'icon-cog',
'url' => Backend::url('system/settings'),
'permissions' => ['backend.manage_users', 'system.*'],
'order' => 1000,
'sideMenu' => [
'settings' => [
'label' => 'system::lang.settings.menu_label',
'icon' => 'icon-cogs',
'url' => Backend::url('system/settings'),
'permissions' => ['system.manage_settings']
],
'users' => [
'label' => 'backend::lang.user.menu_label',
'icon' => 'icon-users',
'url' => Backend::url('backend/users'),
'permissions' => ['backend.manage_users']
],
'updates' => [
'label' => 'system::lang.updates.menu_label',
'icon' => 'icon-cloud-download',
'url' => Backend::url('system/updates'),
'permissions' => ['system.manage_updates']
]
]
'order' => 1000
]
]);
});
@ -237,8 +215,27 @@ class ServiceProvider extends ModuleServiceProvider
'category' => 'System',
'icon' => 'icon-envelope-square',
'url' => Backend::url('system/mailtemplates'),
'order' => 400,
'order' => 500,
],
'administrators' => [
'label' => 'backend::lang.user.menu_label',
'description' => 'backend::lang.user.menu_description',
'category' => 'System',
'icon' => 'icon-users',
'url' => Backend::url('backend/users'),
'permissions' => ['backend.manage_users'],
'order' => 600
],
'updates' => [
'label' => 'system::lang.updates.menu_label',
'description' => 'system::lang.updates.menu_description',
'category' => 'System',
'icon' => 'icon-cloud-download',
'url' => Backend::url('system/updates'),
'permissions' => ['system.manage_updates'],
'order' => 700
]
]);
});

View File

@ -41,6 +41,7 @@ class SettingsManager
'permissions' => [],
'order' => 500,
'context' => 'system',
'keywords' => null
];
/**

View File

@ -1,3 +1,7 @@
<div class="layout">
<div class="layout-cell oc-bg-logo"></div>
<div class="layout-cell oc-bg-logo">
<script>$(document).ready(function(){
$('#settings-search-input').focus()
})</script>
</div>
</div>

View File

@ -121,6 +121,7 @@ return [
'title' => 'Manage Updates',
'name' => 'Software update',
'menu_label' => 'Updates',
'menu_description' => 'Update the system, manage and install plugins and themes.',
'check_label' => 'Check for updates',
'retry_label' => 'Try again',
'plugin_name' => 'Name',

View File

@ -1,16 +1,23 @@
<?php
$context = System\Classes\SettingsManager::instance()->getContext();
$collapsedGroups = explode('|',
isset($_COOKIE['sidenav_treegroupStatus']) ? $_COOKIE['sidenav_treegroupStatus'] : null
);
?>
<ul class="top-level">
<?php foreach ($items as $category => $items): ?>
<li>
<?php foreach ($items as $category => $items):
$collapsed = in_array($category, $collapsedGroups);
?>
<li data-group-code="<?= e($category) ?>" <?= $collapsed ? 'data-status="collapsed"' : null ?>>
<div class="group">
<h3><?= $category ?></h3>
<h3><?= e($category) ?></h3>
</div>
<ul>
<?php foreach ($items as $item): ?>
<li class="<?= strtolower($item->owner) == $context->owner && strtolower($item->code) == $context->itemCode ? 'active' : false ?>">
<li class="<?= strtolower($item->owner) == $context->owner && strtolower($item->code) == $context->itemCode ? 'active' : false ?>" data-keywords="<?= e(trans($item->keywords)) ?>">
<a href="<?= $item->url ?>">
<i class="<?= $item->icon ?>"></i>
<span class="header"><?= e(trans($item->label)) ?></span>

View File

@ -2,7 +2,9 @@
<div class="layout-cell">
<div class="relative toolbar-item loading-indicator-container size-input-text">
<input placeholder="<?= e(trans('system::lang.settings.search')) ?>" type="text" name="search" value=""
class="form-control icon search" autocomplete="off"
class="form-control icon search"
id="settings-search-input"
autocomplete="off"
data-track-input
data-load-indicator
data-load-indicator-opaque

View File

@ -1,4 +1,4 @@
<div class="layout-cell sidenav-tree">
<div class="layout-cell sidenav-tree" data-control="sidenav-tree" data-search-input="#settings-search-input">
<div class="layout">
<div class="layout-row min-size">
<?= $this->makePartial('@/modules/system/partials/_settings_menu_toolbar.htm') ?>