Merge branch 'develop' into feature/integrate-with-admin-pro

This commit is contained in:
Flavio Copes
2015-11-21 16:40:18 +01:00
41 changed files with 1264 additions and 119 deletions

View File

@@ -1,3 +1,40 @@
# v1.0.0-rc.6
## XX/XX/2015
1. [](#improved)
* Implemented logic to detect when offline and suppress Ajax calls
# v1.0.0-rc.5
## 11/20/2015
1. [](#new)
* Use **Nonce** mechanism for form security
* Added Hungarian translation
* Add support for Markdown labels #271
* Added support for Markdown Editor in all the things
* Implemented save keyboard shortcut (Ctrl + S / CMD + S)
1. [](#improved)
* Better error for "Internal Server Error" when accessing GPM
* Updated French translation
* Updated Russian translation
* Load Gravatar image with protocol-less `//:` syntax
* Improved header UI in mobile browsers #265
* Dropped unused version of JQuery
* More visible Preview link icon
* Hide **Latest pages** if there are none
* Improved toggle to better support different length strings
1. [](#bugfix)
* Force rescanning fields when submitting a form #243
* Set default lang for pages on fresh session
* Escaped values in `array.html.twig`
* Fix saving in IE Edge
* Fixed various typos
* Fixed JS button issues #370
* Fixed JS error in private browsing #272
* Fixed date field border
* Fixed multiple instance of Markdown Editor #285
* Fixed Spacer CSS #267
# v1.0.0-rc.4
## 10/29/2015

View File

@@ -107,4 +107,3 @@ If you have been following the [blog](http://getgrav.org/blog), [Twitter](https:
The **standard free version**, is very powerful, and has more functionality than most commercial flat-file CMS systems.
We also intend to release in the near future a more feature-rich **pro version** that will include enhanced functionality, as well as some additional nice-to-have capabilities. This pro version will be a **paid** plugin the price of which is not yet 100% finalized.

View File

@@ -352,6 +352,14 @@ class AdminPlugin extends Plugin
}
}
// Initialize Admin Language if needed
/** @var Language $language */
$language = $this->grav['language'];
if ($language->enabled() && empty($this->grav['session']->admin_lang)) {
$this->grav['session']->admin_lang = $language->getLanguage();
}
// Decide admin template and route.
$path = trim(substr($this->uri->route(), strlen($this->base)), '/');
$this->template = 'dashboard';
@@ -362,13 +370,6 @@ class AdminPlugin extends Plugin
$this->route = array_shift($array);
}
/** @var Language $language */
// $require_language = ['pages', 'translations'];
// $language = $this->grav['language'];
// if ($language->isLanguageInUrl() && !in_array($this->template, $require_language)) {
// $this->grav->redirect($this->uri->route());
// }
// Initialize admin class.
require_once __DIR__ . '/classes/admin.php';
$this->admin = new Admin($this->grav, $this->base, $this->template, $this->route);

View File

@@ -1,5 +1,5 @@
name: Admin Panel
version: 1.0.0-rc.4
version: 1.0.0-rc.5
description: Adds an advanced administration panel to manage your site
icon: empire
author:

View File

@@ -12,6 +12,7 @@ use Grav\Common\Plugins;
use Grav\Common\Themes;
use Grav\Common\Uri;
use Grav\Common\User\User;
use Grav\Common\Utils;
use RocketTheme\Toolbox\File\File;
use RocketTheme\Toolbox\File\JsonFile;
use RocketTheme\Toolbox\File\LogFile;
@@ -312,6 +313,9 @@ class Admin
/** @var Plugins $plugins */
$plugins = $this->grav['plugins'];
$obj = $plugins->get(preg_replace('|plugins/|', '', $type));
if (!$obj) { return []; }
$obj->merge($post);
$obj->file($file);
@@ -320,6 +324,9 @@ class Admin
/** @var Themes $themes */
$themes = $this->grav['themes'];
$obj = $themes->get(preg_replace('|themes/|', '', $type));
if (!$obj) { return []; }
$obj->merge($post);
$obj->file($file);
@@ -707,6 +714,17 @@ class Admin
return $parent_route;
}
/**
* Static helper method to return the admin form nonce
*
* @return string
*/
public static function getNonce()
{
$action = 'admin-form';
return Utils::getNonce($action);
}
/**
* Static helper method to return the last used page name
*

View File

@@ -87,6 +87,30 @@ class AdminController
*/
public function execute()
{
if (method_exists('Grav\Common\Utils', 'getNonce')) {
if (strtolower($_SERVER['REQUEST_METHOD']) == 'post') {
if (!isset($this->post['admin-nonce']) || !Utils::verifyNonce($this->post['admin-nonce'], 'admin-form')) {
$this->admin->setMessage('Unauthorized', 'error');
return false;
}
unset($this->post['admin-nonce']);
} else {
if ($this->task == 'logout') {
$nonce = $this->grav['uri']->param('logout-nonce');
if (!isset($nonce) || !Utils::verifyNonce($nonce, 'logout-form')) {
$this->admin->setMessage('Unauthorized', 'error');
return false;
}
} else {
$nonce = $this->grav['uri']->param('admin-nonce');
if (!isset($nonce) || !Utils::verifyNonce($nonce, 'admin-form')) {
$this->admin->setMessage('Unauthorized', 'error');
return false;
}
}
}
}
$success = false;
$method = 'task' . ucfirst($this->task);
if (method_exists($this, $method)) {
@@ -391,7 +415,7 @@ class AdminController
}
$download = urlencode(base64_encode($backup));
$url = rtrim($this->grav['uri']->rootUrl(true), '/') . '/' . trim($this->admin->base, '/') . '/task' . $param_sep . 'backup/download' . $param_sep . $download;
$url = rtrim($this->grav['uri']->rootUrl(true), '/') . '/' . trim($this->admin->base, '/') . '/task' . $param_sep . 'backup/download' . $param_sep . $download . '/admin-nonce' . $param_sep . Utils::getNonce('admin-form');
$log->content([
'time' => time(),

File diff suppressed because it is too large Load Diff

View File

@@ -283,6 +283,10 @@ $(function () {
data: data,
toastErrors: true,
success: function (response) {
if (!response) {
return;
}
var grav = response.payload.grav,
installed = response.payload.installed,
resources = response.payload.resources,
@@ -403,6 +407,8 @@ $(function () {
if (options.callback && typeof options.callback == 'function') options.callback(response);
}
}).always(function() {
$('[data-gpm-checkupdates]').find('i').removeClass('fa-spin');
});
};
@@ -468,9 +474,17 @@ $(function () {
newItem.attr('data-collection-item', newItem.attr('data-collection-item').replace('*', key));
newItem.attr('data-collection-key', key);
newItem.find('[name]').each(function () {
$(this).attr('name', $(this).attr('name').replace('*', key));
});
var replaceAttribute = function replaceAttribute(attribute) {
newItem.find('[' + attribute + ']').each(function () {
$(this).attr(attribute, $(this).attr(attribute).replace('*', key));
});
};
replaceAttribute('name');
replaceAttribute('data-grav-field-name');
replaceAttribute('id');
replaceAttribute('for');
holder.append(newItem);
button.data('key-index', ++key);
@@ -524,4 +538,19 @@ $(function () {
keepAlive();
}, (GravAdmin.config.admin_timeout/2)*1000);
}
// CTRL + S / CMD + S - shortcut for [Save] when available
var saveTask = $('[name="task"][value="save"]').filter(function(index, element) {
return !($(element).parents('.remodal-overlay').length);
});
if (saveTask.length) {
$(window).on('keydown', function(event) {
var key = String.fromCharCode(event.which).toLowerCase();
if ((event.ctrlKey || event.metaKey) && key == 's') {
event.preventDefault();
saveTask.click();
}
});
}
});

View File

@@ -1,7 +1,20 @@
$(function(){
var root = window || {};
var root = window || {},
isOnline = typeof navigator.onLine !== 'undefined' && navigator.onLine;
window.addEventListener('online', function(){
isOnline = true;
});
window.addEventListener('offline', function(){
isOnline = false;
});
root.GravAjax = function (url, settings) {
if (!isOnline) {
toastr.error('You appear to be Offline.');
return false;
}
settings = typeof settings === 'undefined' ? typeof url === 'string' ? {} : url : settings;
settings.url = typeof settings.url === 'undefined' && typeof url === 'string' ? url : settings.url;

View File

@@ -282,6 +282,8 @@
};
Form.prototype.submit = function(ajax) {
// TODO: workaround, need to rework forms
this.scanned = false; // force to rescan items
var action = this.form.attr('action') || document.location,
method = this.form.attr('method') || 'POST',
values = {};

File diff suppressed because one or more lines are too long

View File

@@ -530,11 +530,12 @@
// init
$(function(){
var editor;
$('textarea[data-grav-mdeditor]').each(function() {
var editor = $(this), obj;
editor = $(this);
if (!editor.data('mdeditor')) {
obj = MDEditor(editor, JSON.parse(editor.attr('data-grav-mdeditor') || '{}'));
new MDEditor(editor, JSON.parse(editor.attr('data-grav-mdeditor') || '{}'));
}
});
})

View File

@@ -49,6 +49,14 @@ $(function(){
});
}
try {
sessionStorage.setItem('sessionStorage', 1);
sessionStorage.removeItem('sessionStorage');
} catch (e) {
Storage.prototype._setItem = Storage.prototype.setItem;
Storage.prototype.setItem = function() {};
}
var childrenToggles = $('[data-toggle="children"]'),
storage = sessionStorage.getItem('grav:admin:pages'),
collapseAll = function(store) {
@@ -122,7 +130,8 @@ $(function(){
url: GravAdmin.config.base_url_relative + '/pages-filter.json/' + task + 'filterPages',
data: {
flags: flags,
query: query
query: query,
'admin-nonce': GravAdmin.config.admin_nonce
},
toastErrors: true,
success: function (result, status) {

View File

@@ -215,16 +215,38 @@ $update-height: 3rem;
height: $topbar-height;
padding: 0 $padding-default;
@include breakpoint(mobile-only) {
height: 7.2rem;
margin-top: -5.5rem;
position: relative;
}
h1 {
@extend %vertical-align;
@include breakpoint(mobile-only){
@include breakpoint(mobile-only) {
> i:first-child:before {
content: "\f0c9";
}
@include transform(inherit);
transform: inherit;
top: 5px;
font-size: 1.2rem;
display: block;
text-align: center;
padding: 20px 0px;
margin-top: -90px;
z-index: 2;
}
@media only all and (max-width: 381px) {
margin-top: -138px;
}
.fa-th {
@include breakpoint(mobile-only) {
cursor: pointer;
float: left;
}
}
}
@@ -233,13 +255,13 @@ $update-height: 3rem;
padding: 0;
@include breakpoint(mobile-only) {
@include transform(inherit);
top: inherit;
bottom: 5px;
position: absolute;
right: 1rem;
text-align: center;
display: block;
float: none;
margin: 5rem auto 0px;
padding-top: 70px;
}
}
.preview {
@@ -331,8 +353,8 @@ $update-height: 3rem;
top: $topbar-height + $update-height;
@include transition (top 0.15s ease-out);
@include breakpoint(mobile-only){
top: 5.2rem;
@include breakpoint(mobile-only) {
top: 7.2rem;
padding-bottom: 8rem;
padding-top: 0rem;
}
@@ -348,6 +370,7 @@ $update-height: 3rem;
padding: 2.5rem;
@include breakpoint(mobile-only) {
top: 7.2rem;
left: 0;
}
}

View File

@@ -111,3 +111,8 @@ b, strong {
text-align: center;
}
.empty-state {
margin: 0 auto;
text-align: center;
padding-top: 100px;
}

View File

@@ -37,7 +37,7 @@ $form-select-pad: 5px 30px 5px 10px;
form {
h1 {
h1, h3 {
color: $page-bg;
padding: 0 $padding-default 0.5rem;
margin: 0 0 1rem;
@@ -227,7 +227,7 @@ form {
margin: 0;
}
input[type=text], input[type=password], input[type=email] {
input[type=text], input[type=password], input[type=email], input[type=date] {
width: 100%;
border: $form-border-width solid $form-border;
background: $white;

View File

@@ -222,7 +222,7 @@
right: 0.5rem;
height: 3.5rem;
@include breakpoint(mobile-only){
@include breakpoint(mobile-only) {
width: 100%;
right: 0;
top: .25rem;
@@ -235,8 +235,7 @@
display: inline-block;
vertical-align: inherit;
@include breakpoint(mobile-only){
width: 100%;
@include breakpoint(mobile-only) {
}
}

View File

@@ -4,15 +4,15 @@
<div class="button-bar">
{% if authorize(['admin.maintenance', 'admin.super']) %}
<div class="button-group">
<button data-clear-cache="{{ base_url_relative }}/cache.json/task{{ config.system.param_sep }}clearCache" class="button"><i class="fa fa-trash"></i> {{ "PLUGIN_ADMIN.CLEAR_CACHE"|tu }}</button>
<button data-clear-cache="{{ uri.addNonce(base_url_relative ~ '/cache.json/task' ~ config.system.param_sep ~ 'clearCache', 'admin-form', 'admin-nonce') }}" class="button"><i class="fa fa-trash"></i> {{ "PLUGIN_ADMIN.CLEAR_CACHE"|tu }}</button>
<button type="button" class="button dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-caret-down"></i>
</button>
<ul class="dropdown-menu">
<li><a data-clear-cache="{{ base_url_relative }}/cache.json/task{{ config.system.param_sep }}clearCache/cleartype{{ config.system.param_sep }}all" href="#">{{ "PLUGIN_ADMIN.CLEAR_CACHE_ALL_CACHE"|tu }}</a></li>
<li><a data-clear-cache="{{ base_url_relative }}/cache.json/task{{ config.system.param_sep }}clearCache/cleartype{{ config.system.param_sep }}assets-only" href="#">{{ "PLUGIN_ADMIN.CLEAR_CACHE_ASSETS_ONLY"|tu }}</a></li>
<li><a data-clear-cache="{{ base_url_relative }}/cache.json/task{{ config.system.param_sep }}clearCache/cleartype{{ config.system.param_sep }}images-only" href="#">{{ "PLUGIN_ADMIN.CLEAR_CACHE_IMAGES_ONLY"|tu }}</a></li>
<li><a data-clear-cache="{{ base_url_relative }}/cache.json/task{{ config.system.param_sep }}clearCache/cleartype{{ config.system.param_sep }}cache-only" href="#">{{ "PLUGIN_ADMIN.CLEAR_CACHE_CACHE_ONLY"|tu }}</a></li>
<li><a data-clear-cache="{{ uri.addNonce(base_url_relative ~ '/cache.json/task' ~ config.system.param_sep ~ 'clearCache/cleartype' ~ config.system.param_sep ~ 'all', 'admin-form', 'admin-nonce') }}" href="#">{{ "PLUGIN_ADMIN.CLEAR_CACHE_ALL_CACHE"|tu }}</a></li>
<li><a data-clear-cache="{{ uri.addNonce(base_url_relative ~ '/cache.json/task' ~ config.system.param_sep ~ 'clearCache/cleartype' ~ config.system.param_sep ~ 'assets-only', 'admin-form', 'admin-nonce') }}" href="#">{{ "PLUGIN_ADMIN.CLEAR_CACHE_ASSETS_ONLY"|tu }}</a></li>
<li><a data-clear-cache="{{ uri.addNonce(base_url_relative ~ '/cache.json/task' ~ config.system.param_sep ~ 'clearCache/cleartype' ~ config.system.param_sep ~ 'images-only', 'admin-form', 'admin-nonce') }}" href="#">{{ "PLUGIN_ADMIN.CLEAR_CACHE_IMAGES_ONLY"|tu }}</a></li>
<li><a data-clear-cache="{{ uri.addNonce(base_url_relative ~ '/cache.json/task' ~ config.system.param_sep ~ 'clearCache/cleartype' ~ config.system.param_sep ~ 'cache-only', 'admin-form', 'admin-nonce') }}" href="#">{{ "PLUGIN_ADMIN.CLEAR_CACHE_CACHE_ONLY"|tu }}</a></li>
</ul>
</div>
@@ -67,8 +67,8 @@
</div>
</div>
<div class="flush-bottom button-bar">
<button data-maintenance-update="{{ base_url_relative }}/update.json/task{{ config.system.param_sep }}update" class="button"><i class="fa fa-cloud-download"></i> {{ "PLUGIN_ADMIN.UPDATE"|tu }}</button>
<button data-ajax="{{ base_url_relative }}/backup.json/task{{ config.system.param_sep }}backup" class="button"><i class="fa fa-database"></i> {{ "PLUGIN_ADMIN.BACKUP"|tu }}</button>
<button data-maintenance-update="{{ uri.addNonce(base_url_relative ~ '/update.json/task' ~ config.system.param_sep ~ 'update', 'admin-form', 'admin-nonce') }}" class="button"><i class="fa fa-cloud-download"></i> {{ "PLUGIN_ADMIN.UPDATE"|tu }}</button>
<button data-ajax="{{ uri.addNonce(base_url_relative ~ '/backup.json/task' ~ config.system.param_sep ~ 'backup', 'admin-form', 'admin-nonce') }}" class="button"><i class="fa fa-database"></i> {{ "PLUGIN_ADMIN.BACKUP"|tu }}</button>
</div>
</div>
</div>
@@ -142,7 +142,7 @@
</div>
<h1>{{ "PLUGIN_ADMIN.LATEST_PAGE_UPDATES"|tu }}</h1>
<table>
{% for latest in admin.latestPages %}
{% for latest in admin.latestPages if admin.latestPages %}
<tr><td class="double page-title"><a href="{{ base_url }}/pages/{{ latest.route|trim('/') }}"><i class="fa fa-fw fa-file-o"></i> {{ latest.title }}</a></td><td class="double page-route">{{ latest.route }}</td><td><b class="last-modified">{{ latest.modified|nicetime }}</b></td></tr>
{% endfor %}
</table>

View File

@@ -8,7 +8,7 @@
{% include 'partials/messages.html.twig' %}
<p>{{ admin.user.fullname }}, {{ "PLUGIN_ADMIN.ACCOUNT_NOT_ADMIN"|tu }}. <a href="{{ base_url_relative }}/task{{ config.system.param_sep }}logout">{{ "PLUGIN_ADMIN.LOGOUT"|tu }}</a></p>
<p>{{ admin.user.fullname }}, {{ "PLUGIN_ADMIN.ACCOUNT_NOT_ADMIN"|tu }}. <a href="{{ uri.addNonce(base_url_relative ~ '/task' ~ config.system.param_sep ~ 'logout', 'logout-form', 'logout-nonce') }}">{{ "PLUGIN_ADMIN.LOGOUT"|tu }}</a></p>
</div>
{% endblock %}

View File

@@ -23,7 +23,7 @@
{% for subkey, subtext in text -%}
<div class="form-row" data-grav-array-type="subrow">
<input data-grav-array-type="keyArraySubelement" type="text" value="{{ subkey }}" />
<input data-grav-array-type="value" type="text" subkey="{{ subkey }}" name="{{ (field.name|fieldName) ~ '[' ~ key ~ '][' ~ subkey ~ ']' }}" value="{{ subtext|join(', ') }}" />
<input data-grav-array-type="value" type="text" subkey="{{ subkey }}" name="{{ (field.name|fieldName) ~ '[' ~ key ~ '][' ~ subkey ~ ']' }}" value="{{ subtext|join(', ')|e }}" />
<span data-grav-array-action="remArrayItem" class="fa fa-minus-square"></span>
<span data-grav-array-action="addArrayItem" class="fa fa-plus-square"></span>
@@ -41,7 +41,7 @@
<input data-grav-array-type="key" type="text" value="{{ key }}" placeholder="{{ field.placeholder_key|e|tu }}" />
{% endif %}
<input data-grav-array-type="value" type="text" name="{{ (field.name|fieldName) ~ '[' ~ key ~ ']' }}" value="{{ text|join(', ') }}" placeholder="{{ field.placeholder_value|e|tu }}" />
<input data-grav-array-type="value" type="text" name="{{ (field.name|fieldName) ~ '[' ~ key ~ ']' }}" value="{{ text|join(', ')|e }}" placeholder="{{ field.placeholder_value|e|tu }}" />
<span data-grav-array-action="rem" class="fa fa-minus"></span>
<span data-grav-array-action="add" class="fa fa-plus"></span>

View File

@@ -1,7 +1,15 @@
{% set value = (value is null ? field.default : value) %}
<div class="form-field">
<div class="form-data form-markdown-wrapper cm-s-paper">
<textarea data-grav-mdeditor="{{ { 'markdown': true }|json_encode|e('html_attr') }}" name="{{ (scope ~ field.name)|fieldName }}" data-grav-urlpreview="{{ base_url }}/media/{{ admin.route|trim('/') }}.json">{{ value|join("\n")|e('html') }}</textarea>
{% block label %}
{% if field.label %}
{% set hint = field.help ? 'data-hint="' ~ field.help ~ '"': '' %}
<div class="form-label form-field hint--bottom" {{ hint }}>{{ field.label|t }}</div>
{% endif %}
{% endblock %}
{% block field %}
<div class="form-field">
<div class="form-data form-markdown-wrapper cm-s-paper">
<textarea data-grav-mdeditor="{{ { 'markdown': true }|json_encode|e('html_attr') }}" name="{{ (scope ~ field.name)|fieldName }}" data-grav-urlpreview="{{ base_url }}/media/{{ admin.route|trim('/') }}.json">{{ value|join("\n")|e('html') }}</textarea>
</div>
</div>
</div>
{% endblock %}

View File

@@ -60,7 +60,7 @@
previewTemplate: "<div class=\"dz-preview dz-file-preview\">\n <div class=\"dz-details\">\n <div class=\"dz-filename\"><span data-dz-name></span></div>\n <div class=\"dz-size\" data-dz-size></div>\n <img data-dz-thumbnail />\n </div>\n <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress></span></div>\n <div class=\"dz-success-mark\"><span>✔</span></div>\n <div class=\"dz-error-mark\"><span>✘</span></div>\n <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n<a class=\"dz-remove\" href=\"javascript:undefined;\" data-dz-remove>Delete</a>\n<a class=\"dz-insert\" href=\"javascript:undefined;\" data-dz-insert>Insert</a>\n</div>",
init: function() {
thisDropzone = this;
$.get(URI + '/task{{ config.system.param_sep }}listmedia', function(data) {
$.get(URI + '/task{{ config.system.param_sep }}listmedia/admin-nonce:' + GravAdmin.config.admin_nonce, function(data) {
$.proxy(modalError, this, {
data: data,
@@ -109,7 +109,7 @@
this.on('removedfile', function(file) {
if (!file.accepted || file.rejected) return;
thisDropzone = this;
$.post(URI + '/task{{ config.system.param_sep }}delmedia', {filename: file.name}, function(data){
$.post(URI + '/task{{ config.system.param_sep }}delmedia', {filename: file.name, 'admin-nonce': GravAdmin.config.admin_nonce}, function(data){
$.proxy(modalError, thisDropzone, {
file: file,
data: data,
@@ -118,6 +118,10 @@
})();
});
});
this.on('sending', function(file, xhr, formData){
formData.append('admin-nonce', GravAdmin.config.admin_nonce);
});
}
};

View File

@@ -15,11 +15,22 @@
data-grav-field-name="{{ field.name|fieldName }}"
{% endblock %}
{% macro spanToggle(input, length) %}
{{ repeat('&nbsp;&nbsp;', (length - input|length) / 2) ~ input ~ repeat('&nbsp;&nbsp;', (length - input|length) / 2) }}
{% endmacro %}
{% block input %}
<div class="switch-toggle switch-grav {{ field.size }} switch-{{ field.options|length }}">
{% set maxLen = 0 %}
{% for text in field.options %}
{% set translation = grav.twig.twig.filters['tu'] is defined ? text|tu : text|t %}
{% set maxLen = max(translation|length, maxLen) %}
{% endfor %}
{% for key, text in field.options %}
{% set id = "toggle_" ~ field.name ~ key %}
{% set translation = (grav.twig.twig.filters['tu'] is defined ? text|tu : text|t)|trim %}
<input type="radio"
value="{{ key }}"
@@ -36,7 +47,7 @@
{% endif %}
{% if field.validate.required in ['on', 'true', 1] %}required="required"{% endif %}
/>
<label for="{{ id }}">{% if grav.twig.twig.filters['tu'] is defined %}{{ text|tu }}{% else %}{{ text|t }}{% endif %}</label>
<label for="{{ id }}">{{ _self.spanToggle(translation, maxLen)|trim }}</label>
{% endfor %}
<a></a>
</div>

View File

@@ -1,5 +1,9 @@
{% extends 'partials/base.html.twig' %}
{% macro spanToggle(input, length) %}
{{ repeat('&nbsp;&nbsp;', (length - input|length) / 2) ~ input ~ repeat('&nbsp;&nbsp;', (length - input|length) / 2) }}
{% endmacro %}
{% if admin.route %}
{% set context = admin.page(true) %}
{% endif %}
@@ -47,7 +51,7 @@
{% endblock %}
{% set preview_html = (base_url_relative_frontend|rtrim('/') ~ (context.home ? '' : context.route)) ?: '/' %}
{% set preview_link = context.routable ? '<a class="preview" target="_blank" href="' ~ preview_html ~ '"><i class="fa fa-fw fa-angle-double-right"></i></a>' : '' %}
{% set preview_link = context.routable ? '<a class="button" target="_blank" href="' ~ preview_html ~ '"> <i class="fa fa-fw fa-eye" style="font-size:18px;margin-right:0;"></i></a>' : '' %}
{% macro loop(page, depth, twig_vars) %}
{% set separator = twig_vars['config'].system.param_sep %}
@@ -57,9 +61,15 @@
{% set admin_lang = twig_vars['admin_lang'] %}
{% set warn = twig_vars['warn'] %}
{% if page.header.content.order.by %}
{% set pcol = page.children().order(page.header.content.order.by, page.header.content.order.dir) %}
{% elseif page.order_by %}
{% set pcol = page.children().order(page.order_by, page.order_dir) %}
{% else %}
{% set pcol = page.children() %}
{% endif %}
{% for p in page.children() %}
{% for p in pcol %}
{% set description = (not p.page ? 'Folder &bull; ' : 'Page &bull; ') ~
(p.modular ? 'Modular &bull; ' : '') ~
(p.routable ? 'Routable &bull; ' : 'Non-Routable &bull; ') ~
@@ -105,7 +115,6 @@
{% block titlebar %}
<div class="button-bar">
{% if mode == 'list' %}
<a class="button" href="{{ base_url }}"><i class="fa fa-reply"></i> {{ "PLUGIN_ADMIN.BACK"|tu }}</a>
@@ -128,7 +137,7 @@
{% for langCode in admin.languages_enabled %}
{% set langName = admin.siteLanguages[langCode] %}
{% if langCode != admin_lang %}
<li><a href="{{ base_url_relative }}{{ theme.slug }}/pages/task{{ config.system.param_sep }}switchlanguage/lang{{ config.system.param_sep }}{{langCode}}">{{ langName[:1]|upper ~ langName[1:] }}</a></li>
<li><a href="{{ uri.addNonce(base_url_relative ~ theme.slug ~ '/pages/task' ~ config.system.param_sep ~ 'switchlanguage/lang' ~ config.system.param_sep ~ langCode, 'admin-form', 'admin-nonce') }}">{{ langName[:1]|upper ~ langName[1:] }}</a></li>
{% endif %}
{% endfor %}
</ul>
@@ -138,15 +147,15 @@
{% elseif mode == 'edit' %}
{{ preview_link }}
<a class="button" href="{{ base_url }}/pages"><i class="fa fa-reply"></i> {{ "PLUGIN_ADMIN.BACK"|tu }}</a>
{% if exists %}
<a class="button" href="{{ page_url }}/task{{ config.system.param_sep }}copy" class="page-copy" ><i class="fa fa-copy"></i> {{ "PLUGIN_ADMIN.COPY"|tu }}</a>
<a class="button" href="{{ uri.addNonce(page_url ~ '/task' ~ config.system.param_sep ~ 'copy', 'admin-form', 'admin-nonce') }}" class="page-copy" ><i class="fa fa-copy"></i> {{ "PLUGIN_ADMIN.COPY"|tu }}</a>
<a class="button" href="#" data-remodal-target="move"><i class="fa fa-arrows"></i> {{ "PLUGIN_ADMIN.MOVE"|tu }}</a>
{% if warn %}
<a class="button" href="#delete" data-remodal-target="delete" data-delete-url="{{ page_url }}/task{{ config.system.param_sep }}delete"><i class="fa fa-close"></i> {{ "PLUGIN_ADMIN.DELETE"|tu }}</a>
<a class="button" href="#delete" data-remodal-target="delete" data-delete-url="{{ uri.addNonce(page_url ~ '/task' ~ config.system.param_sep ~ 'delete', 'admin-form', 'admin-nonce') }}"><i class="fa fa-close"></i> {{ "PLUGIN_ADMIN.DELETE"|tu }}</a>
{% else %}
<a class="button" href="{{ uri.route(true) }}/task{{ config.system.param_sep }}delete" class="page-delete" ><i class="fa fa-close"></i></a>
<a class="button" href="{{ uri.addNonce(uri.route(true) ~ '/task' ~ config.system.param_sep ~ 'delete', 'admin-form', 'admin-nonce') }}" class="page-delete" ><i class="fa fa-close"></i></a>
{% endif %}
{% endif %}
@@ -176,7 +185,7 @@
<h1><i class="fa fa-fw fa-file-text-o"></i> {{ "PLUGIN_ADMIN.ADD_PAGE"|tu }}</h1>
{% elseif mode == 'edit' %}
<h1><i class="fa fa-fw fa-file-text-o"></i>
{{ context.exists ? "PLUGIN_ADMIN.EDIT"|tu ~ " <i>#{context.menu}</i>" ~ preview_link : "PLUGIN_ADMIN.CREATE"|tu ~ " <i>#{context.menu}</i>" }}
{{ context.exists ? "PLUGIN_ADMIN.EDIT"|tu ~ " <i>#{context.menu}</i>" : "PLUGIN_ADMIN.CREATE"|tu ~ " <i>#{context.menu}</i>" }}
</h1>
{% else %}
<h1><i class="fa fa-fw fa-file-text-o"></i> {{ "PLUGIN_ADMIN.MANAGE_PAGES"|tu }}</h1>
@@ -219,12 +228,17 @@
{% endif %}
{% if context.blueprints.fields %}
{% set normalText = "PLUGIN_ADMIN.NORMAL"|tu %}
{% set expertText = "PLUGIN_ADMIN.EXPERT"|tu %}
{% set maxLen = max([normalText|length, expertText|length]) %}
{% set normalText = _self.spanToggle(normalText, maxLen) %}
{% set expertText = _self.spanToggle(expertText, maxLen) %}
<form id="admin-mode-toggle">
<div class="switch-toggle switch-grav">
<input type="radio" value="normal" data-leave-url="{{ base_url }}/pages/{{ admin.route|trim('/') }}/mode{{ config.system.param_sep }}normal" id="normal" name="mode-switch" class="highlight" {% if admin.session.expert == '0' %} checked="checked"{% endif %}>
<label for="normal">{{ "PLUGIN_ADMIN.NORMAL"|tu }}</label>
<label for="normal">{{ normalText|trim }}</label>
<input type="radio" value="expert" data-leave-url="{{ base_url }}/pages/{{ admin.route|trim('/') }}/mode{{ config.system.param_sep }}expert" id="expert" name="mode-switch" class="highlight" {% if admin.session.expert == '1' %} checked="checked"{% endif %}>
<label for="expert">{{ "PLUGIN_ADMIN.EXPERT"|tu }}</label>
<label for="expert">{{ expertText|trim }}</label>
<a></a>
</div>
</form>

View File

@@ -58,7 +58,7 @@
{% do assets.addJs(theme_url~'/js/forms/fields/toggle.js') %}
{% do assets.addJs(theme_url~'/js/forms.js') %}
{% if browser.getBrowser == 'msie' %}
{% if browser.getBrowser == 'msie' or browser.getBrowser == 'edge' %}
{% do assets.addJs(theme_url~'/js/form-attr.polyfill.js') %}
{% endif %}

View File

@@ -14,4 +14,7 @@
<div class="button-bar">
<button class="button primary">{{ "PLUGIN_ADMIN.CONTINUE"|tu }}</button>
</div>
{{ nonce_field('admin-form', 'admin-nonce') }}
</form>

View File

@@ -10,4 +10,7 @@
</div>
{% endif %}
{% endfor %}
{{ nonce_field('admin-form', 'admin-nonce') }}
</form>

View File

@@ -18,4 +18,6 @@
{% include 'forms/fields/hidden/hidden.html.twig' %}
{% endfor %}
{% endif %}
{{ nonce_field('admin-form', 'admin-nonce') }}
</form>

View File

@@ -5,6 +5,7 @@
param_sep: '{{ config.system.param_sep }}',
enable_auto_updates_check: '{{ config.plugins.admin.enable_auto_updates_check }}',
admin_timeout: '{{ config.plugins.admin.session.timeout ?: 1800 }}',
admin_nonce: '{{ admin.getNonce }}',
pro_enabled: '{{ config.plugins["admin-pro"].enabled }}'
};
window.GravAdmin.uri_params = {};

View File

@@ -10,6 +10,8 @@
<form method="post" action="{{ base_url_relative }}">
{% block form %}{% endblock %}
{{ nonce_field('admin-form', 'admin-nonce') }}
</form>
</section>
{% endblock %}

View File

@@ -1,12 +1,12 @@
<nav id="admin-sidebar">
<div id="admin-logo">
<h3><a href="{{ base_url_relative }}">{{ "PLUGIN_ADMIN.GRAV_ADMIN"|tu }}</a> <a target="_blank" href="{{ base_url_relative_frontend }}"><i class="fa fa-fw fa-angle-double-right"></i></a></h3>
<h3><a href="{{ base_url_relative }}">{{ "PLUGIN_ADMIN.GRAV_ADMIN"|tu }}</a> <a target="_blank" href="{{ base_url_relative_frontend }}"> <i class="fa fa-fw fa-arrow-circle-right"></i></a></h3>
</div>
{#{% if admin.authorize %}#}
<div id="admin-user-details">
<a href="{{ base_url_relative }}/users/{{ admin.user.username }}">
<img src="http://www.gravatar.com/avatar/{{ admin.user.email|md5 }}?s=32" />
<img src="//www.gravatar.com/avatar/{{ admin.user.email|md5 }}?s=32" />
<div class="admin-user-names">
<h4>{{ admin.user.fullname }}</h4>
@@ -81,7 +81,7 @@
</a>
</li> -->
<li>
<a href="{{ base_url_relative }}/task{{ config.system.param_sep }}logout"><i class="fa fa-fw fa-sign-out"></i> {{ "PLUGIN_ADMIN.LOGOUT"|tu }}</a>
<a href="{{ uri.addNonce(base_url_relative ~ '/task' ~ config.system.param_sep ~ 'logout', 'logout-form', 'logout-nonce') }}"><i class="fa fa-fw fa-sign-out"></i> {{ "PLUGIN_ADMIN.LOGOUT"|tu }}</a>
</li>
</ul>
</nav>

View File

@@ -35,10 +35,10 @@
<div class="button-bar danger">
<span class="danger-zone"></span>
<a class="button" href="{{ base_url_relative }}/plugins/{{ plugin.slug }}/task{{ config.system.param_sep }}uninstall"><i class="fa fa-fw fa-warning"></i>{{ "PLUGIN_ADMIN.REMOVE_PLUGIN"|tu }}</a>
<a class="button" href="{{ uri.addNonce(base_url_relative ~ '/plugins/' ~ plugin.slug ~ '/task' ~ config.system.param_sep ~ 'uninstall', 'admin-form', 'admin-nonce') }}"><i class="fa fa-fw fa-warning"></i>{{ "PLUGIN_ADMIN.REMOVE_PLUGIN"|tu }}</a>
</div>
{% else %}
<div class="button-bar success">
<a class="button" href="{{ base_url_relative }}/plugins/{{ plugin.slug }}/task{{ config.system.param_sep }}install"><i class="fa fa-fw fa-plus"></i>{{ "PLUGIN_ADMIN.INSTALL_PLUGIN"|tu }}</a>
<a class="button" href="{{ uri.addNonce(base_url_relative ~ '/plugins/' ~ plugin.slug ~ '/task' ~ config.system.param_sep ~ 'install', 'admin-form', 'admin-nonce') }}"><i class="fa fa-fw fa-plus"></i>{{ "PLUGIN_ADMIN.INSTALL_PLUGIN"|tu }}</a>
</div>
{% endif %}

View File

@@ -24,11 +24,11 @@
</td>
<td class="gpm-actions">
{% if (not installing and (plugin.form.fields.enabled.type != 'hidden')) %}
<a class="{{ data.get('enabled') ? 'enabled' : 'disabled' }}" href="{{ base_url_relative }}/plugins/{{ slug }}/task{{ config.system.param_sep }}{{ data.get('enabled') ? 'disable' : 'enable' }}">
<a class="{{ data.get('enabled') ? 'enabled' : 'disabled' }}" href="{{ uri.addNonce(base_url_relative ~ '/plugins/' ~ slug ~ '/task' ~ config.system.param_sep ~ data.get('enabled') ? 'disable' : 'enable', 'admin-form', 'admin-nonce') }}">
<i class="fa fa-fw fa-toggle-{{ data.get('enabled') ? 'on' : 'off' }}"></i>
</a>
{% elseif (installing) %}
<a class="button" href="{{ base_url_relative }}/plugins/{{ slug }}/task{{ config.system.param_sep }}install"><i class="fa fa-plus"></i> Install</a>
<a class="button" href="{{ uri.addNonce(base_url_relative ~ '/plugins/' ~ slug ~ '/task' ~ config.system.param_sep ~ 'install', 'admin-form', 'admin-nonce') }}"><i class="fa fa-plus"></i> Install</a>
{% endif %}
<span class="gpm-details-expand"><i class="fa fa-chevron-down"></i></span>
</td>

View File

@@ -86,7 +86,7 @@
{% endif %}
{% if theme.readme or theme.homepage %}
{% set readme_link = theme.readme ?: theme.homepage ~ '/blob/develop/README.md' %}
{% set readme_link = theme.readme ?: theme.homepage ~ '/blob/master/README.md' %}
<tr>
<td>{{ "PLUGIN_ADMIN.README"|tu }}:</td>
<td><a href="{{ readme_link }}" target="_blank">{{ readme_link }}</a></td>
@@ -102,12 +102,12 @@
{% if (config.get('system.pages.theme') != admin.route) %}
<div class="button-bar danger">
<span class="danger-zone"></span>
<a class="button" href="{{ base_url_relative }}/themes/{{ theme.slug }}/task{{ config.system.param_sep }}uninstall"><i class="fa fa-fw fa-warning"></i>{{ "PLUGIN_ADMIN.REMOVE_THEME"|tu }}</a>
<a class="button" href="{{ uri.addNonce(base_url_relative ~ '/themes/' ~ theme.slug ~ '/task' ~ config.system.param_sep ~ 'uninstall', 'admin-form', 'admin-nonce') }}"><i class="fa fa-fw fa-warning"></i>{{ "PLUGIN_ADMIN.REMOVE_THEME"|tu }}</a>
</div>
{% endif %}
{% else %}
<div class="button-bar success">
<a class="button" href="{{ base_url_relative }}/themes/{{ theme.slug }}/task{{ config.system.param_sep }}install"><i class="fa fa-fw fa-plus"></i>{{ "PLUGIN_ADMIN.INSTALL_THEME"|tu }}</a>
<a class="button" href="{{ uri.addNonce(base_url_relative ~ '/themes/' ~ theme.slug ~ '/task' ~ config.system.param_sep ~ 'install', 'admin-form', 'admin-nonce') }}"><i class="fa fa-fw fa-plus"></i>{{ "PLUGIN_ADMIN.INSTALL_THEME"|tu }}</a>
</div>
{% endif %}

View File

@@ -30,15 +30,14 @@
</div>
{% if (state == 'installing') %}
<div class="gpm-actions">
<a class="button" href="{{ base_url_relative }}/themes/{{ slug }}/task{{ config.system.param_sep }}install"><i class="fa fa-plus"></i> {{ "PLUGIN_ADMIN.INSTALL"|tu }}</a>
<a class="button" href="{{ uri.addNonce(base_url_relative ~ '/themes/' ~ slug ~ '/task' ~ config.system.param_sep ~ 'install', 'admin-form', 'admin-nonce') }}"><i class="fa fa-plus"></i> {{ "PLUGIN_ADMIN.INSTALL"|tu }}</a>
</div>
{% elseif state == 'active' %}
<div class="gpm-actions">
<i class="fa fa-star"></i> {{ "PLUGIN_ADMIN.ACTIVE_THEME"|tu }}
</div>
{% else %}
<a data-remodal-target="theme-switch-warn" href="{{ base_url_relative }}/themes/{{ slug }}/task{{ config.system.param_sep }}activate" class="gpm-actions">
{# <a class="disabled" href="{{ base_url_relative }}/themes/{{ slug }}/task{{ config.system.param_sep }}activate"><i class="fa fa-fw fa-toggle-off"></i></a> #}
<a data-remodal-target="theme-switch-warn" href="{{ uri.addNonce(base_url_relative ~ '/themes/' ~ slug ~ '/task' ~ config.system.param_sep ~ 'activate', 'admin-form', 'admin-nonce') }}" class="gpm-actions">
Activate
</a>
{% endif %}

View File

@@ -1,15 +0,0 @@
<a class="menu-toggle fa fa-fixed-width fa-bars" href="#menu"></a>
<h2>
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-bug fa-stack-1x"></i>
</span>
Admin
</h2>
{% if admin.authorize %}
<span class="user-details">
<img src="http://www.gravatar.com/avatar/{{ admin.user.email|md5 }}?s=50" /><span class="badge">6</span><span class="hide-small">Hi, {{ admin.user.fullname }}<span> <a href="{{ base_url_relative }}/task{{ config.system.param_sep }}logout">{{ "PLUGIN_ADMIN.LOGOUT"|tu }}</a>
</span>
{% endif %}

View File

@@ -1,5 +1,5 @@
<div class="user-details">
<img src="http://www.gravatar.com/avatar/{{ user.email|md5 }}?s=128" />
<img src="https://www.gravatar.com/avatar/{{ user.email|md5 }}?s=128" />
<p class="gravatar">{{ "PLUGIN_ADMIN.AVATAR_BY"|tu }} <a href="http://gravatar.com" target="_blank">gravatar.com</a></p>
<h2>{{ user.fullname }}</h2>
{% if user.title %}<h5>{{ user.title }}</h5>{% endif %}

View File

@@ -15,6 +15,19 @@
{% set title = "PLUGIN_ADMIN.PLUGIN"|tu ~ ": " ~ plugin.name|e %}
{% endif %}
{% if admin.route or installing %}
{% block stylesheets %}
{% do assets.addCss(theme_url~'/css/codemirror/codemirror.css') %}
{{ parent() }}
{% endblock %}
{% block javascripts %}
{% do assets.addJs(theme_url~'/js/codemirror-compressed.js') %}
{% do assets.addJs(theme_url~'/js/mdeditor.js') %}
{{ parent() }}
{% endblock %}
{% endif %}
{% block titlebar %}
{% if not admin.route or installing %}
<div class="button-bar">

View File

@@ -2,6 +2,17 @@
{% set data = admin.data('site') %}
{% block stylesheets %}
{% do assets.addCss(theme_url~'/css/codemirror/codemirror.css') %}
{{ parent() }}
{% endblock %}
{% block javascripts %}
{% do assets.addJs(theme_url~'/js/codemirror-compressed.js') %}
{% do assets.addJs(theme_url~'/js/mdeditor.js') %}
{{ parent() }}
{% endblock %}
{% block titlebar %}
<div class="button-bar">
<a class="button" href="{{ base_url }}"><i class="fa fa-reply"></i> {{ "PLUGIN_ADMIN.BACK"|tu }}</a>

View File

@@ -2,6 +2,17 @@
{% set data = admin.data('system') %}
{% block stylesheets %}
{% do assets.addCss(theme_url~'/css/codemirror/codemirror.css') %}
{{ parent() }}
{% endblock %}
{% block javascripts %}
{% do assets.addJs(theme_url~'/js/codemirror-compressed.js') %}
{% do assets.addJs(theme_url~'/js/mdeditor.js') %}
{{ parent() }}
{% endblock %}
{% block titlebar %}
<div class="button-bar">
<a class="button" href="{{ base_url }}"><i class="fa fa-reply"></i> {{ "PLUGIN_ADMIN.BACK"|tu }}</a>

View File

@@ -16,6 +16,19 @@
{% endif %}
{% if admin.route or installing %}
{% block stylesheets %}
{% do assets.addCss(theme_url~'/css/codemirror/codemirror.css') %}
{{ parent() }}
{% endblock %}
{% block javascripts %}
{% do assets.addJs(theme_url~'/js/codemirror-compressed.js') %}
{% do assets.addJs(theme_url~'/js/mdeditor.js') %}
{{ parent() }}
{% endblock %}
{% endif %}
{% block titlebar %}
{% if not admin.route or installing %}
<div class="button-bar">