mirror of
https://github.com/getgrav/grav-plugin-admin.git
synced 2025-11-15 17:56:07 +01:00
Feature/scheduler (#1512)
* Initial commit * basic cronstatus * updaetd cronstatus field * jquery-cron js * More compact custom list * Added placeholder for new cron field * ui tweaks * New custom cron field. Implemented lang for field. Fixed typos * minor alignment stuff
This commit is contained in:
35
admin.php
35
admin.php
@@ -753,6 +753,41 @@ class AdminPlugin extends Plugin
|
||||
|
||||
$translations .= '};';
|
||||
|
||||
$translations .= 'this.GravAdmin.translations.GRAV_CORE = {';
|
||||
$strings = [
|
||||
'NICETIME.SECOND',
|
||||
'NICETIME.MINUTE',
|
||||
'NICETIME.HOUR',
|
||||
'NICETIME.DAY',
|
||||
'NICETIME.WEEK',
|
||||
'NICETIME.MONTH',
|
||||
'NICETIME.YEAR',
|
||||
'CRON.EVERY',
|
||||
'CRON.EVERY_HOUR',
|
||||
'CRON.EVERY_MINUTE',
|
||||
'CRON.EVERY_DAY_OF_WEEK',
|
||||
'CRON.EVERY_DAY_OF_MONTH',
|
||||
'CRON.EVERY_MONTH',
|
||||
'CRON.TEXT_PERIOD',
|
||||
'CRON.TEXT_MINS',
|
||||
'CRON.TEXT_TIME',
|
||||
'CRON.TEXT_DOW',
|
||||
'CRON.TEXT_MONTH',
|
||||
'CRON.TEXT_DOM',
|
||||
'CRON.ERROR1',
|
||||
'CRON.ERROR2',
|
||||
'CRON.ERROR3',
|
||||
'CRON.ERROR4'
|
||||
];
|
||||
foreach ($strings as $string) {
|
||||
$separator = (end($strings) === $string) ? '' : ',';
|
||||
$translations .= '"' . $string . '": ' . json_encode($this->admin->translate($string)) . $separator;
|
||||
}
|
||||
|
||||
$translations .= ",'MONTHS_OF_THE_YEAR': ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],";
|
||||
$translations .= "'DAYS_OF_THE_WEEK': ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']";
|
||||
$translations .= '};';
|
||||
|
||||
// set the actual translations state back
|
||||
$this->config->set('system.languages.translations', $translations_actual_state);
|
||||
|
||||
|
||||
@@ -724,3 +724,9 @@ PLUGIN_ADMIN:
|
||||
STRICT_YAML_COMPAT_HELP: "Falls back to Symfony 2.4 YAML parser if Native or 3.4 parser fails"
|
||||
STRICT_TWIG_COMPAT: "Twig Compatibility"
|
||||
STRICT_TWIG_COMPAT_HELP: "Enables deprecated Twig autoescape setting. When disabled, |raw filter is required to output HTML as Twig will autoescape output"
|
||||
SCHEDULER_SETUP: Scheduler Setup
|
||||
SCHEDULER_INSTRUCTIONS: "The <b>Grav Scheduler</b> allow you to create and schedule custom jobs. It also provides a method for Grav plugins to integrate programatically and dynamically add jobs to be run periodically."
|
||||
SCHEDULER_POST_INSTRUCTIONS: "To enable the Scheduler's functionality, you must add the <b>Grav Scheduler</b> to your system's crontab file. Run the command above from the terminal to add it automatically. Once saved, refresh this page to see the status Ready."
|
||||
SCHEDULER_JOBS: "Custom Scheduler Jobs"
|
||||
SCHEDULER_STATUS: "Scheduler Status"
|
||||
|
||||
|
||||
145
themes/grav/app/forms/fields/cron.js
Normal file
145
themes/grav/app/forms/fields/cron.js
Normal file
@@ -0,0 +1,145 @@
|
||||
import $ from 'jquery';
|
||||
import '../../utils/cron-ui';
|
||||
import { translations } from 'grav-config';
|
||||
|
||||
export default class CronField {
|
||||
constructor() {
|
||||
this.items = $();
|
||||
|
||||
$('[data-grav-field="cron"]').each((index, cron) => this.addCron(cron));
|
||||
$('body').on('mutation._grav', this._onAddedNodes.bind(this));
|
||||
}
|
||||
|
||||
addCron(cron) {
|
||||
cron = $(cron);
|
||||
this.items = this.items.add(cron);
|
||||
|
||||
cron.find('.cron-selector').each((index, container) => {
|
||||
container = $(container);
|
||||
const input = container.closest('[data-grav-field]').find('input');
|
||||
|
||||
container.jqCron({
|
||||
numeric_zero_pad: true,
|
||||
enabled_minute: true,
|
||||
multiple_dom: true,
|
||||
multiple_month: true,
|
||||
multiple_mins: true,
|
||||
multiple_dow: true,
|
||||
multiple_time_hours: true,
|
||||
multiple_time_minutes: true,
|
||||
default_period: 'hour',
|
||||
default_value: input.val() || '* * * * *',
|
||||
no_reset_button: false,
|
||||
bind_to: input,
|
||||
bind_method: {
|
||||
set: function($element, value) {
|
||||
$element.val(value);
|
||||
}
|
||||
},
|
||||
texts: {
|
||||
en: {
|
||||
empty: translations.GRAV_CORE['CRON.EVERY'],
|
||||
empty_minutes: translations.GRAV_CORE['CRON.EVERY'],
|
||||
empty_time_hours: translations.GRAV_CORE['CRON.EVERY_HOUR'],
|
||||
empty_time_minutes: translations.GRAV_CORE['CRON.EVERY_MINUTE'],
|
||||
empty_day_of_week: translations.GRAV_CORE['CRON.EVERY_DAY_OF_WEEK'],
|
||||
empty_day_of_month: translations.GRAV_CORE['CRON.EVERY_DAY_OF_MONTH'],
|
||||
empty_month: translations.GRAV_CORE['CRON.EVERY_MONTH'],
|
||||
name_minute: translations.GRAV_CORE['NICETIME.MINUTE'],
|
||||
name_hour: translations.GRAV_CORE['NICETIME.HOUR'],
|
||||
name_day: translations.GRAV_CORE['NICETIME.DAY'],
|
||||
name_week: translations.GRAV_CORE['NICETIME.WEEK'],
|
||||
name_month: translations.GRAV_CORE['NICETIME.MONTH'],
|
||||
name_year: translations.GRAV_CORE['NICETIME.YEAR'],
|
||||
text_period: translations.GRAV_CORE['CRON.TEXT_PERIOD'],
|
||||
text_mins: translations.GRAV_CORE['CRON.TEXT_MINS'],
|
||||
text_time: translations.GRAV_CORE['CRON.TEXT_TIME'],
|
||||
text_dow: translations.GRAV_CORE['CRON.TEXT_DOW'],
|
||||
text_month: translations.GRAV_CORE['CRON.TEXT_MONTH'],
|
||||
text_dom: translations.GRAV_CORE['CRON.TEXT_DOM'],
|
||||
error1: translations.GRAV_CORE['CRON.ERROR1'],
|
||||
error2: translations.GRAV_CORE['CRON.ERROR2'],
|
||||
error3: translations.GRAV_CORE['CRON.ERROR3'],
|
||||
error4: translations.GRAV_CORE['CRON.ERROR4'],
|
||||
weekdays: translations.GRAV_CORE['DAYS_OF_THE_WEEK'],
|
||||
months: translations.GRAV_CORE['MONTHS_OF_THE_YEAR']
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_onAddedNodes(event, target/* , record, instance */) {
|
||||
let crons = $(target).find('[data-grav-field="cron"]');
|
||||
if (!crons.length) { return; }
|
||||
|
||||
crons.each((index, list) => {
|
||||
list = $(list);
|
||||
if (!~this.items.index(list)) {
|
||||
this.addCron(list);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export let Instance = new CronField();
|
||||
|
||||
/*
|
||||
|
||||
// cron-selector
|
||||
$(document).ready(function() {
|
||||
|
||||
$('.cron-selector').each(function(index, wrapper) {
|
||||
wrapper = $(wrapper);
|
||||
const input = wrapper.closest('[data-grav-field]').find('input');
|
||||
|
||||
wrapper.jqCron({
|
||||
enabled_minute: true,
|
||||
multiple_dom: true,
|
||||
multiple_month: true,
|
||||
multiple_mins: true,
|
||||
multiple_dow: true,
|
||||
multiple_time_hours: true,
|
||||
multiple_time_minutes: true,
|
||||
default_period: 'week',
|
||||
default_value: input.val() || '3 * * * *',
|
||||
no_reset_button: false,
|
||||
bind_to: input,
|
||||
bind_method: {
|
||||
set: function($element, value) {
|
||||
$element.val(value);
|
||||
}
|
||||
},
|
||||
texts: {
|
||||
en: {
|
||||
empty: 'every',
|
||||
empty_minutes: 'every',
|
||||
empty_time_hours: 'every hour',
|
||||
empty_time_minutes: 'every minute',
|
||||
empty_day_of_week: 'every day of the week',
|
||||
empty_day_of_month: 'every day of the month',
|
||||
empty_month: 'every month',
|
||||
name_minute: 'minute',
|
||||
name_hour: 'hour',
|
||||
name_day: 'day',
|
||||
name_week: 'week',
|
||||
name_month: 'month',
|
||||
name_year: 'year',
|
||||
text_period: 'Every <b />',
|
||||
text_mins: ' at <b /> minute(s) past the hour',
|
||||
text_time: ' at <b />:<b />',
|
||||
text_dow: ' on <b />',
|
||||
text_month: ' of <b />',
|
||||
text_dom: ' on <b />',
|
||||
error1: 'The tag %s is not supported !',
|
||||
error2: 'Bad number of elements',
|
||||
error3: 'The jquery_element should be set into jqCron settings',
|
||||
error4: 'Unrecognized expression',
|
||||
weekdays: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'],
|
||||
months: ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december']
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
*/
|
||||
@@ -10,6 +10,7 @@ import MediapickerField, { Instance as MediapickerInstance } from './mediapicker
|
||||
import MultilevelField, { Instance as MultilevelInstance } from './multilevel';
|
||||
import SelectUniqueField, { Instance as SelectUniqueInstance } from './selectunique';
|
||||
import IconpickerField, { Instance as IconpickerInstance } from './iconpicker';
|
||||
import CronField, { Instance as CronFieldInstance } from './cron';
|
||||
|
||||
export default {
|
||||
FilepickerField: {
|
||||
@@ -59,6 +60,9 @@ export default {
|
||||
IconpickerField: {
|
||||
IconpickerField,
|
||||
Instance: IconpickerInstance
|
||||
},
|
||||
CronField: {CronField,
|
||||
Insance: CronFieldInstance
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
864
themes/grav/app/utils/cron-ui.js
Normal file
864
themes/grav/app/utils/cron-ui.js
Normal file
@@ -0,0 +1,864 @@
|
||||
/* eslint-disable */
|
||||
import $ from 'jquery';
|
||||
/*
|
||||
* This file is part of the Arnapou jqCron package.
|
||||
*
|
||||
* (c) Arnaud Buathier <arnaud@arnapou.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default settings
|
||||
*/
|
||||
var jqCronDefaultSettings = {
|
||||
texts: {},
|
||||
monthdays: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31],
|
||||
hours: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
|
||||
hour_labels: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"],
|
||||
minutes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
|
||||
lang: 'en',
|
||||
enabled_minute: false,
|
||||
enabled_hour: true,
|
||||
enabled_day: true,
|
||||
enabled_week: true,
|
||||
enabled_month: true,
|
||||
enabled_year: true,
|
||||
multiple_dom: false,
|
||||
multiple_month: false,
|
||||
multiple_mins: false,
|
||||
multiple_dow: false,
|
||||
multiple_time_hours: false,
|
||||
multiple_time_minutes: false,
|
||||
numeric_zero_pad: false,
|
||||
default_period: 'day',
|
||||
default_value: '',
|
||||
no_reset_button: true,
|
||||
disabled: false,
|
||||
bind_to: null,
|
||||
bind_method: {
|
||||
set: function($element, value) {
|
||||
$element.is(':input') ? $element.val(value) : $element.data('jqCronValue', value);
|
||||
},
|
||||
get: function($element) {
|
||||
return $element.is(':input') ? $element.val() : $element.data('jqCronValue');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom extend of json for jqCron settings.
|
||||
* We don't use jQuery.extend because simple extend does not fit our needs, and deep extend has a bad
|
||||
* feature for us : it replaces keys of "Arrays" instead of replacing the full array.
|
||||
*/
|
||||
(function($){
|
||||
var extend = function(dst, src) {
|
||||
for(var i in src) {
|
||||
if($.isPlainObject(src[i])) {
|
||||
dst[i] = extend(dst[i] && $.isPlainObject(dst[i]) ? dst[i] : {}, src[i]);
|
||||
}
|
||||
else if($.isArray(src[i])) {
|
||||
dst[i] = src[i].slice(0);
|
||||
}
|
||||
else if(src[i] !== undefined) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
};
|
||||
this.jqCronMergeSettings = function(obj) {
|
||||
return extend(extend({}, jqCronDefaultSettings), obj || {});
|
||||
};
|
||||
}).call(window, $);
|
||||
|
||||
/**
|
||||
* Shortcut to get the instance of jqCron instance from one jquery object
|
||||
*/
|
||||
(function($){
|
||||
$.fn.jqCronGetInstance = function() {
|
||||
return this.data('jqCron');
|
||||
};
|
||||
}).call(window, $);
|
||||
|
||||
/**
|
||||
* Main plugin
|
||||
*/
|
||||
(function($){
|
||||
$.fn.jqCron = function(settings) {
|
||||
var saved_settings = settings;
|
||||
return this.each(function() {
|
||||
var cron, saved;
|
||||
var $this = $(this);
|
||||
var settings = jqCronMergeSettings(saved_settings); // clone settings
|
||||
var translations = settings.texts[settings.lang];
|
||||
|
||||
if (typeof(translations) !== 'object' || $.isEmptyObject(translations)) {
|
||||
console && console.error(
|
||||
'Missing translations for language "' + settings.lang + '". ' +
|
||||
'Please include jqCron.' + settings.lang + '.js or manually provide ' +
|
||||
'the necessary translations when calling $.fn.jqCron().'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!settings.jquery_container) {
|
||||
if($this.is(':container')) {
|
||||
settings.jquery_element = $this.uniqueId('jqCron');
|
||||
}
|
||||
else if($this.is(':autoclose')) {
|
||||
// delete already generated dom if exists
|
||||
if($this.next('.jqCron').length == 1) {
|
||||
$this.next('.jqCron').remove();
|
||||
}
|
||||
// generate new
|
||||
settings.jquery_element = $('<span class="jqCron"></span>').uniqueId('jqCron').insertAfter($this);
|
||||
}
|
||||
else {
|
||||
console && console.error(settings.texts[settings.lang].error1.replace('%s', this.tagName));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// autoset bind_to if it is an input
|
||||
if($this.is(':input')) {
|
||||
settings.bind_to = settings.bind_to || $this;
|
||||
}
|
||||
|
||||
// init cron object
|
||||
if(settings.bind_to){
|
||||
if(settings.bind_to.is(':input')) {
|
||||
// auto bind from input to object if an input, textarea ...
|
||||
settings.bind_to.blur(function(){
|
||||
var value = settings.bind_method.get(settings.bind_to);
|
||||
$this.jqCronGetInstance().setCron(value);
|
||||
});
|
||||
}
|
||||
saved = settings.bind_method.get(settings.bind_to);
|
||||
cron = new jqCron(settings);
|
||||
cron.setCron(saved);
|
||||
}
|
||||
else {
|
||||
cron = new jqCron(settings);
|
||||
}
|
||||
$(this).data('jqCron', cron);
|
||||
});
|
||||
};
|
||||
}).call(window, $);
|
||||
|
||||
/**
|
||||
* jqCron class
|
||||
*/
|
||||
(function($){
|
||||
var jqCronInstances = [];
|
||||
|
||||
function jqCron(settings) {
|
||||
var _initialized = false;
|
||||
var _self = this;
|
||||
var _$elt = this;
|
||||
var _$obj = $('<span class="jqCron-container"></span>');
|
||||
var _$blocks = $('<span class="jqCron-blocks"></span>');
|
||||
var _$blockPERIOD = $('<span class="jqCron-period"></span>');
|
||||
var _$blockDOM = $('<span class="jqCron-dom"></span>');
|
||||
var _$blockMONTH = $('<span class="jqCron-month"></span>');
|
||||
var _$blockMINS = $('<span class="jqCron-mins"></span>');
|
||||
var _$blockDOW = $('<span class="jqCron-dow"></span>');
|
||||
var _$blockTIME = $('<span class="jqCron-time"></span>');
|
||||
var _$cross = $('<span class="jqCron-cross">✘</span>');
|
||||
var _selectors = [];
|
||||
var _selectorPeriod, _selectorMins, _selectorTimeH, _selectorTimeM, _selectorDow, _selectorDom, _selectorMonth;
|
||||
|
||||
// instanciate a new selector
|
||||
function newSelector($block, multiple, type){
|
||||
var selector = new jqCronSelector(_self, $block, multiple, type);
|
||||
selector.$.bind('selector:open', function(){
|
||||
// we close all opened selectors of all other jqCron
|
||||
for(var n = jqCronInstances.length; n--; ){
|
||||
if(jqCronInstances[n] != _self) {
|
||||
jqCronInstances[n].closeSelectors();
|
||||
}
|
||||
else {
|
||||
// we close all other opened selectors of this jqCron
|
||||
for(var o = _selectors.length; o--; ){
|
||||
if(_selectors[o] != selector) {
|
||||
_selectors[o].close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
selector.$.bind('selector:change', function(){
|
||||
var boundChanged = false;
|
||||
// don't propagate if not initialized
|
||||
if(!_initialized) return;
|
||||
// bind data between two minute selectors (only if they have the same multiple settings)
|
||||
if(settings.multiple_mins == settings.multiple_time_minutes) {
|
||||
if(selector == _selectorMins) {
|
||||
boundChanged = _selectorTimeM.setValue(_selectorMins.getValue());
|
||||
}
|
||||
else if(selector == _selectorTimeM) {
|
||||
boundChanged = _selectorMins.setValue(_selectorTimeM.getValue());
|
||||
}
|
||||
}
|
||||
// we propagate the change event to the main object
|
||||
boundChanged || _$obj.trigger('cron:change', _self.getCron());
|
||||
});
|
||||
_selectors.push(selector);
|
||||
return selector;
|
||||
}
|
||||
|
||||
// disable the selector
|
||||
this.disable = function(){
|
||||
_$obj.addClass('disable');
|
||||
settings.disable = true;
|
||||
_self.closeSelectors();
|
||||
};
|
||||
|
||||
// return if the selector is disabled
|
||||
this.isDisabled = function() {
|
||||
return settings.disable == true;
|
||||
};
|
||||
|
||||
// enable the selector
|
||||
this.enable = function(){
|
||||
_$obj.removeClass('disable');
|
||||
settings.disable = false;
|
||||
};
|
||||
|
||||
// get cron value
|
||||
this.getCron = function(){
|
||||
var period = _selectorPeriod.getValue();
|
||||
var items = ['*', '*', '*', '*', '*'];
|
||||
if(period == 'hour') {
|
||||
items[0] = _selectorMins.getCronValue();
|
||||
}
|
||||
if(period == 'day' || period == 'week' || period == 'month' || period == 'year') {
|
||||
items[0] = _selectorTimeM.getCronValue();
|
||||
items[1] = _selectorTimeH.getCronValue();
|
||||
}
|
||||
if(period == 'month' || period == 'year') {
|
||||
items[2] = _selectorDom.getCronValue();
|
||||
}
|
||||
if(period == 'year') {
|
||||
items[3] = _selectorMonth.getCronValue();
|
||||
}
|
||||
if(period == 'week') {
|
||||
items[4] = _selectorDow.getCronValue();
|
||||
}
|
||||
return items.join(' ');
|
||||
};
|
||||
|
||||
// set cron (string like * * * * *)
|
||||
this.setCron = function(str) {
|
||||
if(!str) return;
|
||||
try {
|
||||
str = str.replace(/\s+/g, ' ').replace(/^ +/, '').replace(/ +$/, ''); // sanitize
|
||||
var mask = str.replace(/[^\* ]/g, '-').replace(/-+/g, '-').replace(/ +/g, '');
|
||||
var items = str.split(' ');
|
||||
if (items.length != 5) _self.error(_self.getText('error2'));
|
||||
if(mask == '*****') { // 1 possibility
|
||||
_selectorPeriod.setValue('minute');
|
||||
}
|
||||
else if(mask == '-****') { // 1 possibility
|
||||
_selectorPeriod.setValue('hour');
|
||||
_selectorMins.setCronValue(items[0]);
|
||||
_selectorTimeM.setCronValue(items[0]);
|
||||
}
|
||||
else if(mask.substring(2, mask.length) == '***') { // 4 possibilities
|
||||
_selectorPeriod.setValue('day');
|
||||
_selectorMins.setCronValue(items[0]);
|
||||
_selectorTimeM.setCronValue(items[0]);
|
||||
_selectorTimeH.setCronValue(items[1]);
|
||||
}
|
||||
else if(mask.substring(2, mask.length) == '-**') { // 4 possibilities
|
||||
_selectorPeriod.setValue('month');
|
||||
_selectorMins.setCronValue(items[0]);
|
||||
_selectorTimeM.setCronValue(items[0]);
|
||||
_selectorTimeH.setCronValue(items[1]);
|
||||
_selectorDom.setCronValue(items[2]);
|
||||
}
|
||||
else if(mask.substring(2, mask.length) == '**-') { // 4 possibilities
|
||||
_selectorPeriod.setValue('week');
|
||||
_selectorMins.setCronValue(items[0]);
|
||||
_selectorTimeM.setCronValue(items[0]);
|
||||
_selectorTimeH.setCronValue(items[1]);
|
||||
_selectorDow.setCronValue(items[4]);
|
||||
}
|
||||
else if (mask.substring(3, mask.length) == '-*') { // 8 possibilities
|
||||
_selectorPeriod.setValue('year');
|
||||
_selectorMins.setCronValue(items[0]);
|
||||
_selectorTimeM.setCronValue(items[0]);
|
||||
_selectorTimeH.setCronValue(items[1]);
|
||||
_selectorDom.setCronValue(items[2]);
|
||||
_selectorMonth.setCronValue(items[3]);
|
||||
}
|
||||
else {
|
||||
_self.error(_self.getText('error4'));
|
||||
}
|
||||
_self.clearError();
|
||||
} catch(e) {}
|
||||
};
|
||||
|
||||
// close all child selectors
|
||||
this.closeSelectors = function(){
|
||||
for(var n = _selectors.length; n--; ){
|
||||
_selectors[n].close();
|
||||
}
|
||||
};
|
||||
|
||||
// get the main element id
|
||||
this.getId = function(){
|
||||
return _$elt.attr('id');
|
||||
}
|
||||
|
||||
// get the translated text
|
||||
this.getText = function(key) {
|
||||
var text = settings.texts[settings.lang][key] || null;
|
||||
if(typeof(text) == "string" && text.match('<b')){
|
||||
text = text.replace(/(<b *\/>)/gi, '</span><b /><span class="jqCron-text">');
|
||||
text = '<span class="jqCron-text">' + text + '</span>';
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
// get the human readable text
|
||||
this.getHumanText = function() {
|
||||
var texts=[];
|
||||
_$obj
|
||||
.find('> span > span:visible')
|
||||
.find('.jqCron-text, .jqCron-selector > span')
|
||||
.each(function() {
|
||||
var text = $(this).text().replace(/\s+$/g, '').replace(/^\s+/g, '');
|
||||
text && texts.push(text);
|
||||
});
|
||||
return texts.join(' ').replace(/\s:\s/g, ':');
|
||||
}
|
||||
|
||||
// get settings
|
||||
this.getSettings = function(){
|
||||
return settings;
|
||||
};
|
||||
|
||||
// display an error
|
||||
this.error = function(msg) {
|
||||
console && console.error('[jqCron Error] ' + msg);
|
||||
_$obj.addClass('jqCron-error').attr('title', msg);
|
||||
throw msg;
|
||||
};
|
||||
|
||||
// clear error
|
||||
this.clearError = function(){
|
||||
_$obj.attr('title', '').removeClass('jqCron-error');
|
||||
};
|
||||
|
||||
// clear
|
||||
this.clear = function() {
|
||||
_selectorDom.setValue([]);
|
||||
_selectorDow.setValue([]);
|
||||
_selectorMins.setValue([]);
|
||||
_selectorMonth.setValue([]);
|
||||
_selectorTimeH.setValue([]);
|
||||
_selectorTimeM.setValue([]);
|
||||
_self.triggerChange();
|
||||
};
|
||||
|
||||
// init (called in constructor)
|
||||
this.init = function(){
|
||||
var n,i,labelsList,list;
|
||||
if(_initialized) return;
|
||||
|
||||
settings = jqCronMergeSettings(settings);
|
||||
settings.jquery_element || _self.error(_self.getText('error3'));
|
||||
_$elt = settings.jquery_element;
|
||||
_$elt.append(_$obj);
|
||||
_$obj.data('id', settings.id);
|
||||
_$obj.data('jqCron', _self);
|
||||
_$obj.append(_$blocks);
|
||||
settings.no_reset_button || _$obj.append(_$cross);
|
||||
(!settings.disable) || _$obj.addClass('disable');
|
||||
_$blocks.append(_$blockPERIOD);
|
||||
|
||||
if ( /^(ko)$/i.test(settings.lang) )
|
||||
{
|
||||
_$blocks.append(_$blockMONTH, _$blockDOM);
|
||||
}
|
||||
else
|
||||
{
|
||||
_$blocks.append(_$blockDOM, _$blockMONTH);
|
||||
}
|
||||
|
||||
_$blocks.append(_$blockMINS);
|
||||
_$blocks.append(_$blockDOW);
|
||||
_$blocks.append(_$blockTIME);
|
||||
|
||||
// various binding
|
||||
_$cross.click(function(){
|
||||
_self.isDisabled() || _self.clear();
|
||||
});
|
||||
|
||||
// binding from cron to target
|
||||
_$obj.bind('cron:change', function(evt, value){
|
||||
if(!settings.bind_to) return;
|
||||
settings.bind_method.set && settings.bind_method.set(settings.bind_to, value);
|
||||
_self.clearError();
|
||||
});
|
||||
|
||||
// PERIOD
|
||||
_$blockPERIOD.append(_self.getText('text_period'));
|
||||
_selectorPeriod = newSelector(_$blockPERIOD, false, 'period');
|
||||
settings.enabled_minute && _selectorPeriod.add('minute', _self.getText('name_minute'));
|
||||
settings.enabled_hour && _selectorPeriod.add('hour', _self.getText('name_hour'));
|
||||
settings.enabled_day && _selectorPeriod.add('day', _self.getText('name_day'));
|
||||
settings.enabled_week && _selectorPeriod.add('week', _self.getText('name_week'));
|
||||
settings.enabled_month && _selectorPeriod.add('month', _self.getText('name_month'));
|
||||
settings.enabled_year && _selectorPeriod.add('year', _self.getText('name_year'));
|
||||
_selectorPeriod.$.bind('selector:change', function(e, value){
|
||||
_$blockDOM.hide();
|
||||
_$blockMONTH.hide();
|
||||
_$blockMINS.hide();
|
||||
_$blockDOW.hide();
|
||||
_$blockTIME.hide();
|
||||
if(value == 'hour') {
|
||||
_$blockMINS.show();
|
||||
}
|
||||
else if(value == 'day') {
|
||||
_$blockTIME.show();
|
||||
}
|
||||
else if(value == 'week') {
|
||||
_$blockDOW.show();
|
||||
_$blockTIME.show();
|
||||
}
|
||||
else if(value == 'month') {
|
||||
_$blockDOM.show();
|
||||
_$blockTIME.show();
|
||||
}
|
||||
else if(value == 'year') {
|
||||
_$blockDOM.show();
|
||||
_$blockMONTH.show();
|
||||
_$blockTIME.show();
|
||||
}
|
||||
});
|
||||
_selectorPeriod.setValue(settings.default_period);
|
||||
|
||||
// MINS (minutes)
|
||||
_$blockMINS.append(_self.getText('text_mins'));
|
||||
_selectorMins = newSelector(_$blockMINS, settings.multiple_mins, 'minutes');
|
||||
for(i=0, list=settings.minutes; i<list.length; i++){
|
||||
_selectorMins.add(list[i], list[i]);
|
||||
}
|
||||
|
||||
// TIME (hour:min)
|
||||
_$blockTIME.append(_self.getText('text_time'));
|
||||
_selectorTimeH = newSelector(_$blockTIME, settings.multiple_time_hours, 'time_hours');
|
||||
for(i=0, list=settings.hours, labelsList=settings.hour_labels; i<list.length; i++){
|
||||
_selectorTimeH.add(list[i], labelsList[i]);
|
||||
}
|
||||
_selectorTimeM = newSelector(_$blockTIME, settings.multiple_time_minutes, 'time_minutes');
|
||||
for(i=0, list=settings.minutes; i<list.length; i++){
|
||||
_selectorTimeM.add(list[i], list[i]);
|
||||
}
|
||||
|
||||
// DOW (day of week)
|
||||
_$blockDOW.append(_self.getText('text_dow'));
|
||||
_selectorDow = newSelector(_$blockDOW, settings.multiple_dow, 'day_of_week');
|
||||
for(i=0, list=_self.getText('weekdays'); i<list.length; i++){
|
||||
_selectorDow.add(i+1, list[i]);
|
||||
}
|
||||
|
||||
// DOM (day of month)
|
||||
_$blockDOM.append(_self.getText('text_dom'));
|
||||
_selectorDom = newSelector(_$blockDOM, settings.multiple_dom, 'day_of_month');
|
||||
for(i=0, list=settings.monthdays; i<list.length; i++){
|
||||
_selectorDom.add(list[i], list[i]);
|
||||
}
|
||||
|
||||
// MONTH (day of week)
|
||||
_$blockMONTH.append(_self.getText('text_month'));
|
||||
_selectorMonth = newSelector(_$blockMONTH, settings.multiple_month, 'month');
|
||||
for(i=0, list=_self.getText('months'); i<list.length; i++){
|
||||
_selectorMonth.add(i+1, list[i]);
|
||||
}
|
||||
|
||||
// close all selectors when we click in body
|
||||
$('body').click(function(){
|
||||
var i, n = _selectors.length;
|
||||
for(i = 0; i < n; i++){
|
||||
_selectors[i].close();
|
||||
}
|
||||
});
|
||||
_initialized = true;
|
||||
|
||||
// default value
|
||||
if(settings.default_value) {
|
||||
_self.setCron(settings.default_value);
|
||||
}
|
||||
};
|
||||
|
||||
// trigger a change event
|
||||
this.triggerChange = function(){
|
||||
_$obj.trigger('cron:change', _self.getCron());
|
||||
};
|
||||
|
||||
// store instance in array
|
||||
jqCronInstances.push(this);
|
||||
|
||||
// expose main jquery object
|
||||
this.$ = _$obj;
|
||||
|
||||
// init
|
||||
try {
|
||||
this.init();
|
||||
_self.triggerChange();
|
||||
} catch(e){}
|
||||
}
|
||||
this.jqCron = jqCron;
|
||||
}).call(window, $);
|
||||
|
||||
|
||||
/**
|
||||
* jqCronSelector class
|
||||
*/
|
||||
(function($){
|
||||
function jqCronSelector(_cron, _$block, _multiple, _type){
|
||||
var _self = this;
|
||||
var _$list = $('<ul class="jqCron-selector-list"></ul>');
|
||||
var _$title = $('<span class="jqCron-selector-title"></span>');
|
||||
var _$selector = $('<span class="jqCron-selector"></span>');
|
||||
var _values = {};
|
||||
var _value = [];
|
||||
var _hasNumericTexts = true;
|
||||
var _numeric_zero_pad = _cron.getSettings().numeric_zero_pad;
|
||||
|
||||
// return an array without doublon
|
||||
function array_unique(l){
|
||||
var i=0,n=l.length,k={},a=[];
|
||||
while(i<n) {
|
||||
k[l[i]] || (k[l[i]] = 1 && a.push(l[i]));
|
||||
i++;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
// get the value (an array if multiple, else a single value)
|
||||
this.getValue = function(){
|
||||
return _multiple ? _value : _value[0];
|
||||
};
|
||||
|
||||
// get a correct string for cron
|
||||
this.getCronValue = function(){
|
||||
if(_value.length == 0) return '*';
|
||||
var cron = [_value[0]], i, s = _value[0], c = _value[0], n = _value.length;
|
||||
for(i=1; i<n; i++) {
|
||||
if(_value[i] == c+1) {
|
||||
c = _value[i];
|
||||
cron[cron.length-1] = s+'-'+c;
|
||||
}
|
||||
else {
|
||||
s = c = _value[i];
|
||||
cron.push(c);
|
||||
}
|
||||
}
|
||||
return cron.join(',');
|
||||
};
|
||||
|
||||
// set the cron value
|
||||
this.setCronValue = function(str) {
|
||||
var values = [], m ,i, n;
|
||||
if(str !== '*') {
|
||||
while(str != '') {
|
||||
// test "*/n" expression
|
||||
m = str.match(/^\*\/([0-9]+),?/);
|
||||
if(m && m.length == 2) {
|
||||
for(i=0; i<=59; i+=(m[1]|0)) {
|
||||
values.push(i);
|
||||
}
|
||||
str = str.replace(m[0], '');
|
||||
continue;
|
||||
}
|
||||
// test "a-b/n" expression
|
||||
m = str.match(/^([0-9]+)-([0-9]+)\/([0-9]+),?/);
|
||||
if(m && m.length == 4) {
|
||||
for(i=(m[1]|0); i<=(m[2]|0); i+=(m[3]|0)) {
|
||||
values.push(i);
|
||||
}
|
||||
str = str.replace(m[0], '');
|
||||
continue;
|
||||
}
|
||||
// test "a-b" expression
|
||||
m = str.match(/^([0-9]+)-([0-9]+),?/);
|
||||
if(m && m.length == 3) {
|
||||
for(i=(m[1]|0); i<=(m[2]|0); i++) {
|
||||
values.push(i);
|
||||
}
|
||||
str = str.replace(m[0], '');
|
||||
continue;
|
||||
}
|
||||
// test "c" expression
|
||||
m = str.match(/^([0-9]+),?/);
|
||||
if(m && m.length == 2) {
|
||||
values.push(m[1]|0);
|
||||
str = str.replace(m[0], '');
|
||||
continue;
|
||||
}
|
||||
// something goes wrong in the expression
|
||||
return ;
|
||||
}
|
||||
}
|
||||
_self.setValue(values);
|
||||
};
|
||||
|
||||
// close the selector
|
||||
this.close = function(){
|
||||
_$selector.trigger('selector:close');
|
||||
};
|
||||
|
||||
// open the selector
|
||||
this.open = function(){
|
||||
_$selector.trigger('selector:open');
|
||||
};
|
||||
|
||||
// whether the selector is open
|
||||
this.isOpened = function() {
|
||||
return _$list.is(':visible');
|
||||
};
|
||||
|
||||
// add a selected value to the list
|
||||
this.addValue = function(key) {
|
||||
var values = _multiple ? _value.slice(0) : []; // clone array
|
||||
values.push(key);
|
||||
_self.setValue(values);
|
||||
};
|
||||
|
||||
// remove a selected value from the list
|
||||
this.removeValue = function(key) {
|
||||
if(_multiple) {
|
||||
var i, newValue = [];
|
||||
for(i=0; i<_value.length; i++){
|
||||
if(key != [_value[i]]) {
|
||||
newValue.push(_value[i]);
|
||||
}
|
||||
}
|
||||
_self.setValue(newValue);
|
||||
}
|
||||
else {
|
||||
_self.clear();
|
||||
}
|
||||
};
|
||||
|
||||
// set the selected value(s) of the list
|
||||
this.setValue = function(keys){
|
||||
var i, newKeys = [], saved = _value.join(' ');
|
||||
if(!$.isArray(keys)) keys = [keys];
|
||||
_$list.find('li').removeClass('selected');
|
||||
keys = array_unique(keys);
|
||||
keys.sort(function(a, b){
|
||||
var ta = typeof(a);
|
||||
var tb = typeof(b);
|
||||
if(ta==tb && ta=="number") return a-b;
|
||||
else return String(a) == String(b) ? 0 : (String(a) < String(b) ? -1 : 1);
|
||||
});
|
||||
if(_multiple) {
|
||||
for(i=0; i<keys.length; i++){
|
||||
if(keys[i] in _values) {
|
||||
_values[keys[i]].addClass('selected');
|
||||
newKeys.push(keys[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(keys[0] in _values) {
|
||||
_values[keys[0]].addClass('selected');
|
||||
newKeys.push(keys[0]);
|
||||
}
|
||||
}
|
||||
// remove unallowed values
|
||||
_value = newKeys;
|
||||
if(saved != _value.join(' ')) {
|
||||
_$selector.trigger('selector:change', _multiple ? keys : keys[0]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// get the title text
|
||||
this.getTitleText = function(){
|
||||
var getValueText = function(key) {
|
||||
return (key in _values) ? _values[key].text() : key;
|
||||
};
|
||||
|
||||
if(_value.length == 0) {
|
||||
return _cron.getText('empty_' + _type) || _cron.getText('empty');
|
||||
}
|
||||
var cron = [getValueText(_value[0])], i, s = _value[0], c = _value[0], n = _value.length;
|
||||
for(i=1; i<n; i++) {
|
||||
if(_value[i] == c+1) {
|
||||
c = _value[i];
|
||||
cron[cron.length-1] = getValueText(s)+'-'+getValueText(c);
|
||||
}
|
||||
else {
|
||||
s = c = _value[i];
|
||||
cron.push(getValueText(c));
|
||||
}
|
||||
}
|
||||
return cron.join(',');
|
||||
};
|
||||
|
||||
// clear list
|
||||
this.clear = function() {
|
||||
_values = {};
|
||||
_self.setValue([]);
|
||||
_$list.empty();
|
||||
};
|
||||
|
||||
// add a (key, value) pair
|
||||
this.add = function(key, value) {
|
||||
if(!(value+'').match(/^[0-9]+$/)) _hasNumericTexts = false;
|
||||
if(_numeric_zero_pad && _hasNumericTexts && value < 10) {
|
||||
value = '0'+value;
|
||||
}
|
||||
var $item = $('<li>' + value + '</li>');
|
||||
_$list.append($item);
|
||||
_values[key] = $item;
|
||||
$item.click(function(){
|
||||
if(_multiple && $(this).hasClass('selected')) {
|
||||
_self.removeValue(key);
|
||||
}
|
||||
else {
|
||||
_self.addValue(key);
|
||||
if(!_multiple) _self.close();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// expose main jquery object
|
||||
this.$ = _$selector;
|
||||
|
||||
// constructor
|
||||
_$block.find('b:eq(0)').after(_$selector).remove();
|
||||
_$selector
|
||||
.addClass('jqCron-selector-' + _$block.find('.jqCron-selector').length)
|
||||
.append(_$title)
|
||||
.append(_$list)
|
||||
.bind('selector:open', function(){
|
||||
if(_hasNumericTexts) {
|
||||
var nbcols = 1, n = _$list.find('li').length;
|
||||
if(n > 5 && n <= 16) nbcols = 2;
|
||||
else if(n > 16 && n <= 23) nbcols = 3;
|
||||
else if(n > 23 && n <= 40) nbcols = 4;
|
||||
else if(n > 40) nbcols = 5;
|
||||
_$list.addClass('cols'+nbcols);
|
||||
}
|
||||
_$list.show();
|
||||
})
|
||||
.bind('selector:close', function(){
|
||||
_$list.hide();
|
||||
})
|
||||
.bind('selector:change', function(){
|
||||
_$title.html(_self.getTitleText());
|
||||
})
|
||||
.click(function(e){
|
||||
e.stopPropagation();
|
||||
})
|
||||
.trigger('selector:change')
|
||||
;
|
||||
$.fn.disableSelection && _$selector.disableSelection(); // only work with jQuery UI
|
||||
_$title.click(function(e){
|
||||
(_self.isOpened() || _cron.isDisabled()) ? _self.close() : _self.open();
|
||||
});
|
||||
_self.close();
|
||||
_self.clear();
|
||||
}
|
||||
this.jqCronSelector = jqCronSelector;
|
||||
}).call(window, $);
|
||||
|
||||
/**
|
||||
* Generate unique id for each element.
|
||||
* Skip elements which have already an id.
|
||||
*/
|
||||
(function($){
|
||||
var jqUID = 0;
|
||||
var jqGetUID = function(prefix){
|
||||
var id;
|
||||
while(1) {
|
||||
jqUID++;
|
||||
id = ((prefix || 'JQUID')+'') + jqUID;
|
||||
if(!document.getElementById(id)) return id;
|
||||
}
|
||||
};
|
||||
$.fn.uniqueId = function(prefix) {
|
||||
return this.each(function(){
|
||||
if($(this).attr('id')) return;
|
||||
var id = jqGetUID(prefix);
|
||||
$(this).attr('id', id);
|
||||
});
|
||||
};
|
||||
}).call(window, $);
|
||||
|
||||
|
||||
/**
|
||||
* Extends jQuery selectors with new block selector
|
||||
*/
|
||||
(function($){
|
||||
$.extend($.expr[':'], {
|
||||
container: function(a) {
|
||||
return (a.tagName+'').toLowerCase() in {
|
||||
a:1,
|
||||
abbr:1,
|
||||
acronym:1,
|
||||
address:1,
|
||||
b:1,
|
||||
big:1,
|
||||
blockquote:1,
|
||||
button:1,
|
||||
cite:1,
|
||||
code:1,
|
||||
dd: 1,
|
||||
del:1,
|
||||
dfn:1,
|
||||
div:1,
|
||||
dt:1,
|
||||
em:1,
|
||||
fieldset:1,
|
||||
form:1,
|
||||
h1:1,
|
||||
h2:1,
|
||||
h3:1,
|
||||
h4:1,
|
||||
h5:1,
|
||||
h6: 1,
|
||||
i:1,
|
||||
ins:1,
|
||||
kbd:1,
|
||||
label:1,
|
||||
li:1,
|
||||
p:1,
|
||||
pre:1,
|
||||
q:1,
|
||||
samp:1,
|
||||
small:1,
|
||||
span:1,
|
||||
strong:1,
|
||||
sub: 1,
|
||||
sup:1,
|
||||
td:1,
|
||||
tt:1
|
||||
};
|
||||
},
|
||||
autoclose: function(a) {
|
||||
return (a.tagName+'').toLowerCase() in {
|
||||
area:1,
|
||||
base:1,
|
||||
basefont:1,
|
||||
br:1,
|
||||
col:1,
|
||||
frame:1,
|
||||
hr:1,
|
||||
img:1,
|
||||
input:1,
|
||||
link:1,
|
||||
meta:1,
|
||||
param:1
|
||||
};
|
||||
}
|
||||
});
|
||||
}).call(window, $);
|
||||
2
themes/grav/css-compiled/fonts.css
vendored
2
themes/grav/css-compiled/fonts.css
vendored
@@ -1,3 +1,3 @@
|
||||
@import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500|Inconsolata:400,700|&subset=latin-ext");body,h5,h6,.badge,.note,.grav-mdeditor-preview,input,select,textarea,button,.selectize-input{font-family:"Roboto","Helvetica","Tahoma","Geneva","Arial",sans-serif}h1,h2,h3,h4,.form-tabs>label,.label{font-family:"Josefin Slab","Helvetica","Tahoma","Geneva","Arial",sans-serif}code,kbd,pre,samp,body .CodeMirror{font-family:"Inconsolata","Monaco","Consolas","Lucida Console",monospace !important}
|
||||
|
||||
/*# sourceMappingURL=fonts.css.map */
|
||||
/*# sourceMappingURL=../css-compiled/fonts.css.map */
|
||||
@@ -1 +1,11 @@
|
||||
{"version":3,"file":"fonts.css","sources":["fonts.scss","configuration/fonts/_support.scss"],"sourcesContent":["$fonts-header: 'Josefin Slab' !default;\n$fonts-default: 'Roboto' !default;\n$fonts-mono: 'Inconsolata' !default;\n\n$font-definitions: (\n Josefin+Slab: 400,\n Roboto: '300,400,500',\n Inconsolata: '400,700'\n);\n\n@import \"configuration/fonts/support\";\n\n\n\n\n","@function str-replace($string, $search, $replace: '') {\n $index: str-index($string, $search);\n\n @if $index {\n @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);\n }\n\n @return $string;\n}\n\n@function admin-font-faces($fonts) {\n $url: \"https://fonts.googleapis.com/css?family=\";\n $nb: 0;\n\n @each $fontname, $weights in $fonts {\n\n @if $fontname == $fonts-default or\n $fontname == $fonts-header or\n $fontname == $fonts-mono {\n\n $nb: $nb + 1;\n $nb-word: 0;\n\n $fontname: str-replace(\"#{$fontname}\", \" \", \"+\");\n\n $url: $url + $fontname;\n\n @if $weights != null {\n $url: $url + \":\" + $weights;\n }\n\n @if $nb < 3 {\n $url: $url + \"|\";\n }\n }\n }\n\n @return $url + \"&subset=latin-ext\";\n}\n\n@mixin body-fonts($font) {\n body, h5, h6,\n .badge, .note, .grav-mdeditor-preview,\n input, select, textarea, button, .selectize-input {\n font-family: \"#{$font}\", \"Helvetica\", \"Tahoma\", \"Geneva\", \"Arial\", sans-serif;\n }\n}\n\n@mixin header-fonts($font) {\n h1, h2, h3, h4,\n .form-tabs > label, .label {\n font-family: \"#{$font}\", \"Helvetica\", \"Tahoma\", \"Geneva\", \"Arial\", sans-serif;\n }\n}\n\n@mixin mono-fonts($font) {\n code, kbd, pre, samp,\n body .CodeMirror {\n font-family: \"#{$font}\", \"Monaco\", \"Consolas\", \"Lucida Console\", monospace !important;\n }\n}\n$font-url: admin-font-faces($font-definitions);\n\n@import url(\"#{$font-url}\");\n\n@include body-fonts($fonts-default);\n\n@include header-fonts($fonts-header);\n\n@include mono-fonts($fonts-mono);\n\n\n\n\n\n"],"names":[],"mappings":"AC+DA,OAAO,CAAC,uGAAI,CAtBR,AAAA,IAAI,CAAE,EAAE,CAAE,EAAE,CACZ,MAAM,CAAE,KAAK,CAAE,sBAAsB,CACrC,KAAK,CAAE,MAAM,CAAE,QAAQ,CAAE,MAAM,CAAE,gBAAgB,AAAC,CAC9C,WAAW,CAAE,QAAU,CAAE,WAAW,CAAE,QAAQ,CAAE,QAAQ,CAAE,OAAO,CAAE,UAAU,CAChF,AAID,AAAA,EAAE,CAAE,EAAE,CAAE,EAAE,CAAE,EAAE,CACd,UAAU,CAAG,KAAK,CAAE,MAAM,AAAC,CACvB,WAAW,CAAE,cAAU,CAAE,WAAW,CAAE,QAAQ,CAAE,QAAQ,CAAE,OAAO,CAAE,UAAU,CAChF,AAID,AAAA,IAAI,CAAE,GAAG,CAAE,GAAG,CAAE,IAAI,CACpB,IAAI,CAAC,WAAW,AAAC,CACb,WAAW,CAAE,aAAU,CAAE,QAAQ,CAAE,UAAU,CAAE,gBAAgB,CAAE,SAAS,CAAC,UAAU,CACxF"}
|
||||
{
|
||||
"version": 3,
|
||||
"file": "../scss/fonts.css",
|
||||
"sources": [
|
||||
"../scss/fonts.scss",
|
||||
"../hdr0",
|
||||
"../scss/configuration/fonts/_support.scss"
|
||||
],
|
||||
"mappings": "AE+DA,OAAO,CAAC,uGAAI,CAtBR,AAAA,IAAI,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CACZ,AAAA,MAAM,CAAE,AAAA,KAAK,CAAE,AAAA,sBAAsB,CACrC,AAAA,KAAK,CAAE,AAAA,MAAM,CAAE,AAAA,QAAQ,CAAE,AAAA,MAAM,CAAE,AAAA,gBAAgB,AAAC,CAC9C,WAAW,CAAE,QAAU,CAAE,WAAW,CAAE,QAAQ,CAAE,QAAQ,CAAE,OAAO,CAAE,UAAU,CAChF,AAID,AAAA,EAAE,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CACd,AAAa,UAAH,CAAG,KAAK,CAAE,AAAA,MAAM,AAAC,CACvB,WAAW,CAAE,cAAU,CAAE,WAAW,CAAE,QAAQ,CAAE,QAAQ,CAAE,OAAO,CAAE,UAAU,CAChF,AAID,AAAA,IAAI,CAAE,AAAA,GAAG,CAAE,AAAA,GAAG,CAAE,AAAA,IAAI,CACpB,AAAK,IAAD,CAAC,WAAW,AAAC,CACb,WAAW,CAAE,aAAU,CAAE,QAAQ,CAAE,UAAU,CAAE,gBAAgB,CAAE,SAAS,CAAC,UAAU,CACxF",
|
||||
"names": []
|
||||
}
|
||||
2
themes/grav/css-compiled/nucleus.css
vendored
2
themes/grav/css-compiled/nucleus.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
themes/grav/css-compiled/preset.css
vendored
4
themes/grav/css-compiled/preset.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
themes/grav/css-compiled/simple-fonts.css
vendored
2
themes/grav/css-compiled/simple-fonts.css
vendored
@@ -1,3 +1,3 @@
|
||||
body,h5,h6,.badge,.note,.grav-mdeditor-preview,input,select,textarea,button,.selectize-input,h1,h2,h3,h4,#admin-menu li,.form-tabs>label,.label,body .CodeMirror pre{font-family:"Helvetica Neue", "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif}code,kbd,pre,samp{font-family:"Monaco", "Consolas", "Lucida Console", monospace}
|
||||
|
||||
/*# sourceMappingURL=simple-fonts.css.map */
|
||||
/*# sourceMappingURL=../css-compiled/simple-fonts.css.map */
|
||||
@@ -1 +1,10 @@
|
||||
{"version":3,"file":"simple-fonts.css","sources":["simple-fonts.scss"],"sourcesContent":["body, h5, h6,\n.badge, .note, .grav-mdeditor-preview,\ninput, select, textarea, button, .selectize-input,\nh1, h2, h3, h4,\n#admin-menu li, .form-tabs > label, .label,\nbody .CodeMirror pre {\n font-family: \"Helvetica Neue\", \"Helvetica\", \"Tahoma\", \"Geneva\", \"Arial\", sans-serif;\n}\ncode, kbd, pre, samp {\n font-family: \"Monaco\", \"Consolas\", \"Lucida Console\", monospace;\n}\n\n"],"names":[],"mappings":"AAAA,AAAA,IAAI,CAAE,EAAE,CAAE,EAAE,CACZ,MAAM,CAAE,KAAK,CAAE,sBAAsB,CACrC,KAAK,CAAE,MAAM,CAAE,QAAQ,CAAE,MAAM,CAAE,gBAAgB,CACjD,EAAE,CAAE,EAAE,CAAE,EAAE,CAAE,EAAE,CACd,WAAW,CAAC,EAAE,CAAE,UAAU,CAAG,KAAK,CAAE,MAAM,CAC1C,IAAI,CAAC,WAAW,CAAC,GAAG,AAAC,CACjB,WAAW,CAAE,sEAAsE,CACtF,AACD,AAAA,IAAI,CAAE,GAAG,CAAE,GAAG,CAAE,IAAI,AAAE,CAClB,WAAW,CAAE,iDAAiD,CACjE"}
|
||||
{
|
||||
"version": 3,
|
||||
"file": "../scss/simple-fonts.css",
|
||||
"sources": [
|
||||
"../scss/simple-fonts.scss",
|
||||
"../hdr0"
|
||||
],
|
||||
"mappings": "AAAA,AAAA,IAAI,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CACZ,AAAA,MAAM,CAAE,AAAA,KAAK,CAAE,AAAA,sBAAsB,CACrC,AAAA,KAAK,CAAE,AAAA,MAAM,CAAE,AAAA,QAAQ,CAAE,AAAA,MAAM,CAAE,AAAA,gBAAgB,CACjD,AAAA,EAAE,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CACd,AAAY,WAAD,CAAC,EAAE,CAAE,AAAa,UAAH,CAAG,KAAK,CAAE,AAAA,MAAM,CAC1C,AAAiB,IAAb,CAAC,WAAW,CAAC,GAAG,AAAC,CACjB,WAAW,CAAE,sEAAuE,CACvF,AACD,AAAA,IAAI,CAAE,AAAA,GAAG,CAAE,AAAA,GAAG,CAAE,AAAA,IAAI,AAAE,CAClB,WAAW,CAAE,iDAAkD,CAClE",
|
||||
"names": []
|
||||
}
|
||||
4
themes/grav/css-compiled/template.css
vendored
4
themes/grav/css-compiled/template.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
36
themes/grav/js/admin.min.js
vendored
36
themes/grav/js/admin.min.js
vendored
File diff suppressed because one or more lines are too long
59
themes/grav/js/vendor.min.js
vendored
59
themes/grav/js/vendor.min.js
vendored
File diff suppressed because one or more lines are too long
5
themes/grav/package-lock.json
generated
5
themes/grav/package-lock.json
generated
@@ -4797,6 +4797,11 @@
|
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz",
|
||||
"integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg=="
|
||||
},
|
||||
"jquery-cron": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jquery-cron/-/jquery-cron-1.0.0.tgz",
|
||||
"integrity": "sha1-HXUAXWZ7Tx4RJKtMxRTwxL25mX8="
|
||||
},
|
||||
"js-base64": {
|
||||
"version": "2.4.8",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.8.tgz",
|
||||
|
||||
@@ -649,7 +649,11 @@ form {
|
||||
}
|
||||
|
||||
.overlay {
|
||||
background: darken($content-bg, 5%);
|
||||
background: darken($content-bg, 2%);
|
||||
|
||||
pre {
|
||||
background: lighten($pre-bg, 3%);
|
||||
}
|
||||
}
|
||||
|
||||
.form-border {
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
|
||||
// Media
|
||||
@import "template/media";
|
||||
@import "template/jqcron";
|
||||
|
||||
// Custom
|
||||
@import "template/custom";
|
||||
|
||||
@@ -119,11 +119,13 @@ form {
|
||||
}
|
||||
|
||||
.required {
|
||||
display: inline-block;
|
||||
font-family: helvetica, arial;
|
||||
vertical-align: middle;
|
||||
line-height: 1;
|
||||
line-height: 0;
|
||||
font-size: 30px;
|
||||
margin-left: 5px;
|
||||
margin-left: 0px;
|
||||
margin-bottom: -5px;
|
||||
}
|
||||
|
||||
label {
|
||||
@@ -672,3 +674,54 @@ textarea.frontmatter {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.cron-install {
|
||||
|
||||
margin: 1rem;
|
||||
padding: 0;
|
||||
border-radius: 4px;
|
||||
|
||||
form & pre {
|
||||
padding: 1rem;
|
||||
margin: 0 1.5rem;
|
||||
line-height: 1;
|
||||
}
|
||||
.setup-status {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.cron-status {
|
||||
.cron-at code {
|
||||
font-size: 120%;
|
||||
padding: 2px 10px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
tr {
|
||||
|
||||
|
||||
& > th {
|
||||
&:last-child {
|
||||
min-width: 220px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
form .cron-job-list {
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.form-field.grid {
|
||||
width: calc(50% - 5px);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
105
themes/grav/scss/template/_jqcron.scss
Normal file
105
themes/grav/scss/template/_jqcron.scss
Normal file
@@ -0,0 +1,105 @@
|
||||
|
||||
/*
|
||||
* This file is part of the Arnapou jqCron package.
|
||||
*
|
||||
* (c) Arnaud Buathier <arnaud@arnapou.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
.jqCron-selector {
|
||||
position: relative;
|
||||
}
|
||||
.jqCron-cross,
|
||||
.jqCron-selector-title {
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #ddd;
|
||||
margin: 0 0.2em;
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
.jqCron-container.disable .jqCron-cross:hover,
|
||||
.jqCron-container.disable .jqCron-selector-title:hover,
|
||||
.jqCron-cross,
|
||||
.jqCron-selector-title {
|
||||
background: #eee;
|
||||
border-color: #ddd;
|
||||
}
|
||||
.jqCron-cross:hover,
|
||||
.jqCron-selector-title:hover {
|
||||
background-color: #ddd;
|
||||
border-color: #aaa;
|
||||
}
|
||||
.jqCron-cross {
|
||||
border-radius: 1em;
|
||||
font-size: 80%;
|
||||
padding: 0 0.3em;
|
||||
}
|
||||
.jqCron-selector-list {
|
||||
background: #eee;
|
||||
border: 1px solid #aaa;
|
||||
-webkit-box-shadow: 2px 2px 3px #ccc;
|
||||
box-shadow: 2px 2px 3px #ccc;
|
||||
left: 0.2em;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
top: 1.5em;
|
||||
z-index: 5;
|
||||
}
|
||||
.jqCron-selector-list li {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
cursor: default;
|
||||
display: inline-block !important;
|
||||
margin: 0;
|
||||
padding: 0.1em 0.4em;
|
||||
width: 100%;
|
||||
}
|
||||
.jqCron-selector-list li.selected {
|
||||
background: #0088cc;
|
||||
color: white;
|
||||
}
|
||||
.jqCron-selector-list li:hover {
|
||||
background: #5fb9e7;
|
||||
color: white;
|
||||
}
|
||||
.jqCron-selector-list.cols2 {
|
||||
width: 4em;
|
||||
}
|
||||
.jqCron-selector-list.cols2 li {
|
||||
width: 50%;
|
||||
}
|
||||
.jqCron-selector-list.cols3 {
|
||||
width: 6em;
|
||||
}
|
||||
.jqCron-selector-list.cols3 li {
|
||||
width: 33%;
|
||||
}
|
||||
.jqCron-selector-list.cols4 {
|
||||
width: 8em;
|
||||
}
|
||||
.jqCron-selector-list.cols4 li {
|
||||
width: 25%;
|
||||
}
|
||||
.jqCron-selector-list.cols5 {
|
||||
width: 10em;
|
||||
}
|
||||
.jqCron-selector-list.cols5 li {
|
||||
width: 20%;
|
||||
}
|
||||
.jqCron-error .jqCron-selector-title {
|
||||
background: #fee;
|
||||
border: 1px solid #fdd;
|
||||
color: red;
|
||||
}
|
||||
.jqCron-container.disable * {
|
||||
color: #888;
|
||||
}
|
||||
.jqCron-container.disable .jqCron-selector-title {
|
||||
background: #eee !important;
|
||||
}
|
||||
@@ -19,6 +19,7 @@ tr {
|
||||
|
||||
@include display(flex);
|
||||
@include flex-wrap(wrap);
|
||||
@include align-items(center);
|
||||
|
||||
th, td {
|
||||
display: block;
|
||||
|
||||
6
themes/grav/templates/forms/fields/cron/cron.html.twig
Normal file
6
themes/grav/templates/forms/fields/cron/cron.html.twig
Normal file
@@ -0,0 +1,6 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block input %}
|
||||
<input type="hidden" class="input" name="{{ (scope ~ field.name)|fieldName }}" value="{{ value|join(', ') }}" />
|
||||
<div class="cron-selector"></div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,17 @@
|
||||
<div class="cron-install form-border overlay">
|
||||
|
||||
<p class="setup-status">Crontab Status:
|
||||
{% set cron_status = grav.scheduler.isCrontabSetup()%}
|
||||
{% if cron_status == 1 %}
|
||||
<span class="badge success"><i class="fa fa-check"></i> Ready</span>
|
||||
{% elseif cron_status == 2 %}
|
||||
<span class="badge error"><i class="fa fa-warning"></i> Cron Not Available</span>
|
||||
{% else %}
|
||||
<span class="badge error"><i class="fa fa-clock-o"></i> Not Enabled</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<pre><code>{{- grav.scheduler.getCronCommand()|trim -}}</code></pre>
|
||||
|
||||
<p>{{ "PLUGIN_ADMIN.SCHEDULER_POST_INSTRUCTIONS"|tu|raw }}</p>
|
||||
</div>
|
||||
@@ -0,0 +1,60 @@
|
||||
{% set jobs = grav.scheduler.getAllJobs() %}
|
||||
{% set job_states = grav.scheduler.getJobStates().content() %}
|
||||
|
||||
<table class="cron-status">
|
||||
<tr>
|
||||
<th style="flex:3;">Command</th>
|
||||
<th style="flex:3;">Run</th>
|
||||
<th>Status</th>
|
||||
<th>State</th>
|
||||
</tr>
|
||||
{% for job in jobs %}
|
||||
{% set job_status = attribute(data.status,job.id) %}
|
||||
{% set job_enabled = job_status is defined and job_status == 'disabled' ? 0 : 1 %}
|
||||
{% set job_id = job.id %}
|
||||
{% set job_id_md5 = job_id|md5 %}
|
||||
{% set job_state = attribute(job_states, job_id) %}
|
||||
{% set job_at = job.getAt %}
|
||||
<tr>
|
||||
<td style="flex:3;overflow:hidden;"><kbd>{{ job.getCommand }}</kbd></td>
|
||||
<td style="flex:3;" class="cron-at">
|
||||
{% if job_enabled %}
|
||||
<span class="hint--bottom" data-hint="next run: {{ cron(job_at).getNextRunDate()|date(config.date_format.default) }}">{{ job_at|nicecron }}</span>
|
||||
{% else %}
|
||||
{{ job_at|nicecron }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if job_state.state == 'failure' %}
|
||||
{% set run_type = 'error' %}
|
||||
{% set run_hint = job_state.error %}
|
||||
{% set run_text = "<i class=\"fa fa-warning\"></i> Failure" %}
|
||||
{% else %}
|
||||
{% set run_type = 'info' %}
|
||||
{% if job_state.state is not defined %}
|
||||
{% set run_hint = "not run yet" %}
|
||||
{% set run_text = "<i class=\"fa fa-check\"></i> Ready" %}
|
||||
{% else %}
|
||||
{% set run_hint = "last run: " ~ attribute(job_state,'last-run')|date(config.date_format.default) %}
|
||||
{% set run_text = "<i class=\"fa fa-check\"></i> Success" %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<span class="hint--bottom" data-hint="{{ run_hint }}">
|
||||
<span class="badge {{ run_type }}">{{ run_text|raw }}</span>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="form-data" data-grav-field="toggle" data-grav-disabled="" data-grav-default="null" data-grav-field-name="data[status][{{ job_id }}]">
|
||||
<div class="switch-toggle switch-grav switch-2 ">
|
||||
<input type="radio" value="enabled" id="toggle_status.{{ job_id_md5 }}1" name="data[status][{{ job_id }}]" class="highlight" {% if job_enabled %}checked="checked"{% endif %}>
|
||||
<label for="toggle_status.{{ job_id_md5 }}1">Enabled</label>
|
||||
|
||||
<input type="radio" value="disabled" id="toggle_status.{{ job_id_md5 }}0" name="data[status][{{ job_id }}]" class="" {% if not job_enabled %}checked="checked"{% endif %}>
|
||||
<label for="toggle_status.{{ job_id_md5 }}0">Disabled</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
Reference in New Issue
Block a user