2022-03-08 15:55:41 +01:00
|
|
|
|
<?php namespace ProcessWire;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* AdminThemeUikit
|
|
|
|
|
*
|
|
|
|
|
* @property bool $isSuperuser Is current user a superuser?
|
|
|
|
|
* @property bool $isEditor Does current user have page-edit permission?
|
|
|
|
|
* @property bool $isLoggedIn Is current user logged in?
|
|
|
|
|
* @property bool $useOffset Use offset/margin for all Inputfields?
|
|
|
|
|
* @property array $noBorderTypes Inputfield class names that should always use the noBorder option (when 100% width).
|
|
|
|
|
* @property array $cardTypes Inputfield class names that should always use the card option.
|
|
|
|
|
* @property array $offsetTypes Inputfield class names that should always use the offset option.
|
|
|
|
|
* @property string $logoURL URL to custom logo, relative to PW installation root.
|
|
|
|
|
* @property string $cssURL URL to custom CSS file, relative to PW installation root.
|
|
|
|
|
* @property string $layout Layout type (blank=default, sidenav=multi-pane, sidenav-tree=left-tree, sidenav-tree-alt=right-tree)
|
|
|
|
|
* @property int $logoAction Logo click action (0=admin root page list, 1=offcanvas nav)
|
|
|
|
|
* @property string $userLabel Text containing user {vars} to use for user label in masthead (default="{Name}")
|
|
|
|
|
* @property int $maxWidth Maximum layout width in pixels, or 0 for no max (default=1600).
|
|
|
|
|
* @property bool|int $groupNotices Whether or not notices should be grouped by type
|
|
|
|
|
* @property string $inputSize Size for input/select elements. One of "s" for small, "m" for medium (default), or "l" for large.
|
|
|
|
|
* @property bool|int $ukGrid When true, use uk-width classes for Inputfields (rather than CSS percentages).
|
|
|
|
|
* @property int $toggleBehavior (0=Standard, 1=Consistent)
|
|
|
|
|
* @property string $configPhpHash Hash used internally to detect changes to $config->AdminThemeUikit settings.
|
|
|
|
|
* @property int $cssVersion Current version number of core CSS/LESS files
|
|
|
|
|
*
|
|
|
|
|
* @method string renderBreadcrumbs()
|
|
|
|
|
* @method string getUikitCSS()
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
class AdminThemeUikit extends AdminThemeFramework implements Module, ConfigurableModule {
|
|
|
|
|
|
|
|
|
|
public static function getModuleInfo() {
|
|
|
|
|
return array(
|
|
|
|
|
'title' => 'Uikit',
|
|
|
|
|
'version' => 33,
|
|
|
|
|
'summary' => 'Uikit v3 admin theme',
|
|
|
|
|
'autoload' => 'template=admin',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Development mode, to be used when developing this module’s code
|
|
|
|
|
*
|
|
|
|
|
* Makes it use runtime/temporary compiled CSS files rather than the final ones.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
const dev = false;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set to true when upgrading Uikit version
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
const upgrade = false;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Required CSS/LESS files version
|
|
|
|
|
*
|
|
|
|
|
* Increment on core less file changes that will also require a recompile of /site/assets/admin.css
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
const requireCssVersion = 1;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Default logo image file (relative to this dir)
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
const logo = 'uikit-pw/images/pw-mark.png';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* sidenavType: primary navigation on left sidebar
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
const sidenavTypePrimary = 0;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* sidenavType: tree navigation on left sidebar
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
const sidenavTypeTree = 1;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Construct and establish default module config settings
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function __construct() {
|
|
|
|
|
|
|
|
|
|
parent::__construct();
|
|
|
|
|
|
|
|
|
|
$this->set('useOffset', false);
|
|
|
|
|
$this->set('cardTypes', array());
|
|
|
|
|
$this->set('offsetTypes', array());
|
|
|
|
|
$this->set('logoURL', '');
|
|
|
|
|
$this->set('cssURL', '');
|
|
|
|
|
$this->set('layout', '');
|
|
|
|
|
$this->set('noBorderTypes', array()); // 'InputfieldCKEditor' is a good one for this
|
|
|
|
|
$this->set('logoAction', 0);
|
|
|
|
|
$this->set('toggleBehavior', 0);
|
|
|
|
|
$this->set('userLabel', '{Name}');
|
|
|
|
|
$this->set('userAvatar', 'icon.user-circle');
|
|
|
|
|
$this->set('maxWidth', 1600);
|
|
|
|
|
$this->set('groupNotices', true);
|
|
|
|
|
$this->set('inputSize', 'm'); // m=medium (default), s=small, l=large
|
|
|
|
|
$this->set('ukGrid', false);
|
|
|
|
|
$this->set('configPhpHash', '');
|
|
|
|
|
$this->set('cssVersion', 0);
|
|
|
|
|
$this->setClasses(array(
|
|
|
|
|
'input' => 'uk-input',
|
|
|
|
|
'input-small' => 'uk-input uk-form-small',
|
|
|
|
|
'input-checkbox' => 'uk-checkbox',
|
|
|
|
|
'input-radio' => 'uk-radio',
|
|
|
|
|
'input-password' => 'uk-input uk-form-width-medium',
|
|
|
|
|
'select' => 'uk-select',
|
|
|
|
|
'select-asm' => 'uk-select uk-form-small',
|
|
|
|
|
'select-small' => 'uk-select uk-form-small',
|
|
|
|
|
'textarea' => 'uk-textarea',
|
|
|
|
|
'table' => 'uk-table uk-table-divider uk-table-justify uk-table-small',
|
|
|
|
|
'dl' => 'uk-description-list uk-description-list-divider',
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function wired() {
|
|
|
|
|
parent::wired();
|
|
|
|
|
$this->addHookAfter('InputfieldSelector::ajaxReady', $this, 'hookInputfieldSelectorAjax');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initialize and attach hooks
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function init() {
|
|
|
|
|
parent::init();
|
|
|
|
|
|
|
|
|
|
// if this is not the current admin theme, exit now so no hooks are attached
|
|
|
|
|
if(!$this->isCurrent()) return;
|
|
|
|
|
|
|
|
|
|
/** @var Page $page */
|
|
|
|
|
$page = $this->wire('page');
|
|
|
|
|
/** @var Modules $modules */
|
|
|
|
|
$modules = $this->wire('modules');
|
|
|
|
|
/** @var Modules $modules */
|
|
|
|
|
$session = $this->wire('session');
|
|
|
|
|
|
|
|
|
|
$sidenav = strpos($this->layout, 'sidenav') === 0;
|
|
|
|
|
|
|
|
|
|
// disable sidebar layout if SystemNotifications is active
|
|
|
|
|
if($sidenav && $modules->isInstalled('SystemNotifications')) {
|
|
|
|
|
/** @var SystemNotifications $systemNotifications */
|
|
|
|
|
$systemNotifications = $modules->get('SystemNotifications');
|
|
|
|
|
if(!$systemNotifications->disabled) {
|
|
|
|
|
$this->layout = '';
|
|
|
|
|
$sidenav = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!$page || $page->template != 'admin') {
|
|
|
|
|
// front-end
|
|
|
|
|
if($sidenav) {
|
|
|
|
|
// ensure that page edit links on front-end load the sidenav-init
|
|
|
|
|
$session->setFor('Page', 'appendEditUrl', "&layout=sidenav-init");
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$inputSize = $this->get('inputSize');
|
|
|
|
|
if($inputSize && $inputSize != 'm') {
|
|
|
|
|
$inputClass = $inputSize === 'l' ? 'uk-form-large' : 'uk-form-small';
|
|
|
|
|
foreach(array('input', 'select', 'textarea') as $name) {
|
|
|
|
|
$this->addClass($name, $inputClass);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!$this->ukGrid) {
|
|
|
|
|
$this->addClass('body', 'AdminThemeUikitNoGrid');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($this->className() !== 'AdminThemeUikit') {
|
|
|
|
|
$this->addBodyClass('AdminThemeUikit');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$session->removeFor('Page', 'appendEditUrl');
|
|
|
|
|
/** @var JqueryUI $jqueryUI */
|
|
|
|
|
$jqueryUI = $modules->get('JqueryUI');
|
|
|
|
|
$jqueryUI->use('panel');
|
|
|
|
|
|
|
|
|
|
// add rendering hooks
|
|
|
|
|
$this->addHookBefore('Inputfield::render', $this, 'hookBeforeRenderInputfield');
|
|
|
|
|
$this->addHookBefore('Inputfield::renderValue', $this, 'hookBeforeRenderInputfield');
|
|
|
|
|
$this->addHookAfter('Inputfield::getConfigInputfields', $this, 'hookAfterInputfieldGetConfigInputfields');
|
|
|
|
|
$this->addHookAfter('Inputfield::getConfigAllowContext', $this, 'hookAfterInputfieldGetConfigAllowContext');
|
|
|
|
|
$this->addHookAfter('MarkupAdminDataTable::render', $this, 'hookAfterTableRender');
|
|
|
|
|
|
|
|
|
|
// hooks and settings specific to sidebar layouts
|
|
|
|
|
if($sidenav) {
|
|
|
|
|
$this->addHookAfter('ProcessLogin::afterLoginURL', $this, 'hookAfterLoginURL');
|
|
|
|
|
if(strpos($this->layout, 'sidenav-tree') === 0) {
|
|
|
|
|
// page-edit breadcrumbs go to page editor when page tree is always in sidebar
|
|
|
|
|
$this->wire('config')->pageEdit('editCrumbs', true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// add cache clearing hooks
|
|
|
|
|
$this->wire('pages')->addHookAfter('saved', $this, 'hookClearCaches');
|
|
|
|
|
$modules->addHookAfter('refresh', $this, 'hookClearCaches');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Render an extra markup region
|
|
|
|
|
*
|
|
|
|
|
* @param string $for
|
|
|
|
|
* @return mixed|string
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function renderExtraMarkup($for) {
|
|
|
|
|
$out = parent::renderExtraMarkup($for);
|
|
|
|
|
if($for === 'notices') {
|
|
|
|
|
}
|
|
|
|
|
return $out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Test all notice types
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function testNotices() {
|
|
|
|
|
if(parent::testNotices()) {
|
|
|
|
|
$v = $this->wire('input')->get('test_notices');
|
|
|
|
|
if($v === 'group-off') $this->groupNotices = false;
|
|
|
|
|
if($v === 'group-on') $this->groupNotices = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get Uikit uk-width-* class for given column width
|
|
|
|
|
*
|
|
|
|
|
* @param int $columnWidth
|
|
|
|
|
* @param array $widths
|
|
|
|
|
* @return string
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
protected function getUkWidthClass($columnWidth, array $widths) {
|
|
|
|
|
|
|
|
|
|
static $minColumnWidth = null;
|
|
|
|
|
|
|
|
|
|
$ukWidthClass = '1-1';
|
|
|
|
|
|
|
|
|
|
if($minColumnWidth === null) {
|
|
|
|
|
$widthKeys = array_keys($widths);
|
|
|
|
|
sort($widthKeys, SORT_NATURAL);
|
|
|
|
|
$minColumnWidth = (int) reset($widthKeys);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($columnWidth < 10) {
|
|
|
|
|
// use uk-width-1-1
|
|
|
|
|
} else if($columnWidth && $columnWidth < 100) {
|
|
|
|
|
if($columnWidth < $minColumnWidth) $columnWidth = $minColumnWidth;
|
|
|
|
|
// determine column width class
|
|
|
|
|
foreach($widths as $pct => $uk) {
|
|
|
|
|
$pct = (int) $pct;
|
|
|
|
|
if($columnWidth >= $pct) {
|
|
|
|
|
$ukWidthClass = $uk;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($ukWidthClass === '1-1') {
|
|
|
|
|
return "uk-width-1-1";
|
|
|
|
|
} else {
|
|
|
|
|
return "uk-width-$ukWidthClass@m";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************************
|
|
|
|
|
* HOOKS
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Hook called before each Inputfield::render
|
|
|
|
|
*
|
|
|
|
|
* This updates the Inputfield classes and settings for Uikit.
|
|
|
|
|
*
|
|
|
|
|
* - themeBorder: none, hide, card, line
|
|
|
|
|
* - themeOffset: s, m, l
|
|
|
|
|
* - themeInputSize: s, m, l
|
|
|
|
|
* - themeInputWidth: xs, s, m, l, f
|
|
|
|
|
* - themeColor: primary, secondary, warning, danger, success, highlight, none
|
|
|
|
|
* - themeBlank: no input borders, true or false
|
|
|
|
|
*
|
|
|
|
|
* @param HookEvent $event
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function hookBeforeRenderInputfield(HookEvent $event) {
|
|
|
|
|
|
|
|
|
|
/** @var Inputfield $inputfield */
|
|
|
|
|
$inputfield = $event->object;
|
|
|
|
|
$class = $inputfield->className();
|
|
|
|
|
$formSettings = $event->wire('config')->get('InputfieldForm');
|
|
|
|
|
$widths = $formSettings['ukGridWidths'];
|
|
|
|
|
$columnWidth = (int) $inputfield->getSetting('columnWidth');
|
|
|
|
|
$field = $inputfield->hasField;
|
|
|
|
|
$isFieldset = $inputfield instanceof InputfieldFieldset;
|
|
|
|
|
$isMarkup = $inputfield instanceof InputfieldMarkup;
|
|
|
|
|
$isWrapper = $inputfield instanceof InputfieldWrapper && !$isFieldset && !$isMarkup;
|
|
|
|
|
$ukWidthClass = 'uk-width-1-1';
|
|
|
|
|
$globalInputSize = $this->get('inputSize');
|
|
|
|
|
$ukGrid = $this->get('ukGrid');
|
|
|
|
|
$themeColor = '';
|
|
|
|
|
$themeBorder = '';
|
|
|
|
|
$themeOffset = '';
|
|
|
|
|
$themeInputSize = '';
|
|
|
|
|
$themeInputWidth = '';
|
|
|
|
|
$themeBlank = false;
|
|
|
|
|
$wrapClasses = array();
|
|
|
|
|
$inputClasses = array();
|
|
|
|
|
$removeInputClasses = array();
|
|
|
|
|
|
|
|
|
|
if($inputfield instanceof InputfieldForm) {
|
|
|
|
|
if($globalInputSize == 's') {
|
|
|
|
|
$inputfield->addClass('InputfieldFormSmallInputs');
|
|
|
|
|
} else if($globalInputSize == 'l') {
|
|
|
|
|
$inputfield->addClass('InputfieldFormLargeInputs');
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
} else if($inputfield instanceof InputfieldSubmit) {
|
|
|
|
|
// button
|
|
|
|
|
$inputfield->addClass('uk-width-auto uk-margin-top', 'wrapClass');
|
|
|
|
|
return; // no further settings needed for button
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($ukGrid) {
|
|
|
|
|
$ukWidthClass = $this->getUkWidthClass($columnWidth, $widths);
|
|
|
|
|
if($ukWidthClass) $wrapClasses[] = $ukWidthClass;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($isWrapper) {
|
|
|
|
|
if($ukWidthClass != 'uk-width-1-1') $inputfield->addClass($ukWidthClass, 'wrapClass');
|
|
|
|
|
return;
|
|
|
|
|
} else if($inputfield instanceof InputfieldTextarea) {
|
|
|
|
|
$inputClasses[] = $this->getClass('textarea');
|
|
|
|
|
} else if($inputfield instanceof InputfieldPassword) {
|
|
|
|
|
$inputClasses[] = $this->getClass('input-password');
|
|
|
|
|
} else if($inputfield instanceof InputfieldText) {
|
|
|
|
|
$inputClasses[] = $this->getClass('input');
|
|
|
|
|
} else if($inputfield instanceof InputfieldInteger) {
|
|
|
|
|
$inputClasses[] = $this->getClass('input');
|
|
|
|
|
} else if($inputfield instanceof InputfieldDatetime) {
|
|
|
|
|
$inputClasses[] = $this->getClass('input');
|
|
|
|
|
} else if($inputfield instanceof InputfieldCheckboxes || $inputfield instanceof InputfieldCheckbox) {
|
|
|
|
|
$inputClasses[] = $this->getClass('input-checkbox');
|
|
|
|
|
$inputfield->addClass('uk-form-controls-text', 'contentClass');
|
|
|
|
|
} else if($inputfield instanceof InputfieldRadios) {
|
|
|
|
|
$inputClasses[] = $this->getClass('input-radio');
|
|
|
|
|
$inputfield->addClass('uk-form-controls-text', 'contentClass');
|
|
|
|
|
} else if($inputfield instanceof InputfieldAsmSelect) {
|
|
|
|
|
$inputClasses[] = $this->getClass('select-asm');
|
|
|
|
|
} else if($inputfield instanceof InputfieldSelect && !$inputfield instanceof InputfieldHasArrayValue) {
|
|
|
|
|
$inputClasses[] = $this->getClass('select');
|
|
|
|
|
} else if($inputfield instanceof InputfieldFile) {
|
|
|
|
|
$themeColor = 'secondary';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($field) {
|
|
|
|
|
// pull optional uikit settings from Field object
|
|
|
|
|
$themeBorder = $field->get('themeBorder');
|
|
|
|
|
$themeOffset = $field->get('themeOffset');
|
|
|
|
|
$themeInputSize = $field->get('themeInputSize');
|
|
|
|
|
$themeInputWidth = $field->get('themeInputWidth');
|
|
|
|
|
$themeColor = $field->get('themeColor') ? $field->get('themeColor') : $themeColor;
|
|
|
|
|
$themeBlank = $field->get('themeBlank');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// determine custom settings which may be defined with Inputfield
|
|
|
|
|
if(!$themeBorder) $themeBorder = $inputfield->getSetting('themeBorder');
|
|
|
|
|
if(!$themeOffset) $themeOffset = $inputfield->getSetting('themeOffset'); // || in_array($class, $this->offsetTypes);
|
|
|
|
|
if(!$themeColor) $themeColor = $inputfield->getSetting('themeColor');
|
|
|
|
|
if(!$themeInputSize) $themeInputSize = $inputfield->getSetting('themeInputSize');
|
|
|
|
|
if(!$themeInputWidth) $themeInputWidth = $inputfield->getSetting('themeInputWidth');
|
|
|
|
|
if(!$themeBlank) $themeBlank = $inputfield->getSetting('themeBlank');
|
|
|
|
|
|
|
|
|
|
if(!$themeBorder) {
|
|
|
|
|
if($formSettings['useBorders'] === false || in_array($class, $this->noBorderTypes)) {
|
|
|
|
|
$themeBorder = (!$columnWidth || $columnWidth == 100) ? 'none' : 'hide';
|
|
|
|
|
} else if(in_array($class, $this->cardTypes)) {
|
|
|
|
|
$themeBorder = 'card';
|
|
|
|
|
} else {
|
|
|
|
|
$themeBorder = 'line';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($themeInputSize && $globalInputSize != $themeInputSize) {
|
|
|
|
|
if($globalInputSize === 's') {
|
|
|
|
|
$removeInputClasses[] = 'uk-form-small';
|
|
|
|
|
} else if($globalInputSize === 'l') {
|
|
|
|
|
$removeInputClasses[] = 'uk-form-large';
|
|
|
|
|
}
|
|
|
|
|
if($themeInputSize === 'm') {
|
|
|
|
|
$inputClasses[] = 'uk-form-medium';
|
|
|
|
|
} else if($themeInputSize === 's') {
|
|
|
|
|
$inputClasses[] = 'uk-form-small';
|
|
|
|
|
} else if($themeInputSize === 'l') {
|
|
|
|
|
$inputClasses[] = 'uk-form-large';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($themeInputWidth) {
|
|
|
|
|
$inputWidthClasses = array(
|
|
|
|
|
'xs' => 'uk-form-width-xsmall',
|
|
|
|
|
's' => 'uk-form-width-small',
|
|
|
|
|
'm' => 'uk-form-width-medium',
|
|
|
|
|
'l' => 'uk-form-width-large',
|
|
|
|
|
'f' => 'InputfieldMaxWidth',
|
|
|
|
|
);
|
|
|
|
|
$inputfield->removeClass($inputWidthClasses);
|
|
|
|
|
if(isset($inputWidthClasses[$themeInputWidth])) {
|
|
|
|
|
$inputClasses[] = $inputWidthClasses[$themeInputWidth];
|
|
|
|
|
if($themeInputWidth != 'f') $inputClasses[] = 'InputfieldSetWidth';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($themeBlank) {
|
|
|
|
|
$inputClasses[] = 'uk-form-blank';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($themeColor) {
|
|
|
|
|
$wrapClasses[] = 'InputfieldIsColor';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch($themeColor) {
|
|
|
|
|
case 'primary': $wrapClasses[] = 'InputfieldIsPrimary'; break;
|
|
|
|
|
case 'secondary': $wrapClasses[] = 'InputfieldIsSecondary'; break;
|
|
|
|
|
case 'warning': $wrapClasses[] = 'InputfieldIsWarning'; break;
|
|
|
|
|
case 'danger': $wrapClasses[] = 'InputfieldIsError'; break;
|
|
|
|
|
case 'success': $wrapClasses[] = 'InputfieldIsSuccess'; break;
|
|
|
|
|
case 'highlight': $wrapClasses[] = 'InputfieldIsHighlight'; break;
|
|
|
|
|
case 'none': break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch($themeBorder) {
|
|
|
|
|
case 'none': $wrapClasses[] = 'InputfieldNoBorder'; break;
|
|
|
|
|
case 'hide': $wrapClasses[] = 'InputfieldHideBorder'; break;
|
|
|
|
|
case 'card': $wrapClasses[] = 'uk-card uk-card-default'; break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($themeOffset && $themeOffset !== 'none') {
|
|
|
|
|
$wrapClasses[] = 'InputfieldIsOffset';
|
|
|
|
|
if($themeOffset === 's') {
|
|
|
|
|
$wrapClasses[] = 'InputfieldIsOffsetSm';
|
|
|
|
|
} else if($themeOffset === 'l') {
|
|
|
|
|
$wrapClasses[] = 'InputfieldIsOffsetLg';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(count($inputClasses)) {
|
|
|
|
|
$inputfield->addClass(implode(' ', $inputClasses));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(count($removeInputClasses)) {
|
|
|
|
|
$inputfield->removeClass($removeInputClasses);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(count($wrapClasses)) {
|
|
|
|
|
$inputfield->addClass(implode(' ', $wrapClasses), 'wrapClass');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Hook after Inputfield::getConfigInputfields() to add theme-specific configuration settings
|
|
|
|
|
*
|
|
|
|
|
* @param HookEvent $event
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function hookAfterInputfieldGetConfigInputfields(HookEvent $event) {
|
|
|
|
|
|
|
|
|
|
/** @var Inputfield $inputfield */
|
|
|
|
|
$inputfield = $event->object;
|
|
|
|
|
if($inputfield instanceof InputfieldWrapper) return;
|
|
|
|
|
/** @var InputfieldWrapper $inputfields */
|
|
|
|
|
$inputfields = $event->return;
|
|
|
|
|
if(!$inputfields instanceof InputfieldWrapper) return;
|
|
|
|
|
include_once(dirname(__FILE__) . '/config.php');
|
|
|
|
|
$configHelper = new AdminThemeUikitConfigHelper($this);
|
|
|
|
|
$configHelper->configInputfield($inputfield, $inputfields);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get fields allowed for field/template context configuration
|
|
|
|
|
*
|
|
|
|
|
* @param HookEvent $event
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function hookAfterInputfieldGetConfigAllowContext(HookEvent $event) {
|
|
|
|
|
$names = $event->return;
|
|
|
|
|
$names[] = '_adminTheme';
|
|
|
|
|
$names[] = 'themeOffset';
|
|
|
|
|
$names[] = 'themeBorder';
|
|
|
|
|
$names[] = 'themeColor';
|
|
|
|
|
$names[] = 'themeInputSize';
|
|
|
|
|
$names[] = 'themeInputWidth';
|
|
|
|
|
$names[] = 'themeBlank';
|
|
|
|
|
$event->return = $names;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Hook after MarkupAdminDataTable::render
|
|
|
|
|
*
|
|
|
|
|
* This is primarily to add support for Uikit horizontal scrolling responsive tables,
|
|
|
|
|
* which is used instead of the default MarkupAdminDataTable responsive table.
|
|
|
|
|
*
|
|
|
|
|
* @param HookEvent $event
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function hookAfterTableRender(HookEvent $event) {
|
|
|
|
|
/** @var MarkupAdminDataTable $table */
|
|
|
|
|
$table = $event->object;
|
|
|
|
|
$classes = array();
|
2024-04-04 14:37:20 +02:00
|
|
|
|
if($table->responsive) {
|
|
|
|
|
$classes[] = 'pw-table-responsive';
|
|
|
|
|
if(!wireInstanceOf($this->wire()->process, array('ProcessPageLister', 'ProcessUser'))) {
|
|
|
|
|
$classes[] = 'uk-overflow-auto';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if($table->sortable) {
|
|
|
|
|
$classes[] = 'pw-table-sortable';
|
|
|
|
|
}
|
|
|
|
|
if($table->resizable) {
|
|
|
|
|
$classes[] = 'pw-table-resizable';
|
|
|
|
|
}
|
2022-03-08 15:55:41 +01:00
|
|
|
|
if(count($classes)) {
|
|
|
|
|
$class = implode(' ', $classes);
|
|
|
|
|
$event->return = "<div class='$class'>$event->return</div>";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Event called when a page is saved or modules refreshed to clear caches
|
|
|
|
|
*
|
|
|
|
|
* @param HookEvent $event
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function hookClearCaches(HookEvent $event) {
|
|
|
|
|
|
|
|
|
|
/** @var Page|User|null $page */
|
|
|
|
|
$page = $event->arguments(0);
|
|
|
|
|
/** @var array $changes */
|
|
|
|
|
$changes = $event->arguments(1);
|
|
|
|
|
/** @var User $user */
|
|
|
|
|
$user = $this->wire('user');
|
|
|
|
|
|
|
|
|
|
if($page !== null && !($page instanceof Page)) return;
|
|
|
|
|
if(!is_array($changes)) $changes = array();
|
|
|
|
|
|
|
|
|
|
if($page === null || $page->template == 'admin' || ($page->id === $user->id && in_array('language', $changes))) {
|
|
|
|
|
/** @var Session $session */
|
|
|
|
|
$session = $this->wire('session');
|
|
|
|
|
$session->removeFor($this, 'prnav');
|
|
|
|
|
$session->removeFor($this, 'sidenav');
|
|
|
|
|
$session->message("Cleared the admin theme navigation cache (primary nav)", Notice::debug);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Hook to ProcessLogin::afterLoginURL()
|
|
|
|
|
*
|
|
|
|
|
* @param HookEvent $event
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function hookAfterLoginURL(HookEvent $event) {
|
|
|
|
|
$layout = $this->layout;
|
|
|
|
|
if(!$layout) return;
|
|
|
|
|
$url = $event->return;
|
|
|
|
|
$url .= (strpos($url, '?') !== false ? '&' : '?') . "layout=$this->layout-init";
|
|
|
|
|
$event->return = $url;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************************
|
|
|
|
|
* MARKUP RENDERING METHODS
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Render a list of breadcrumbs (list items), excluding the containing <ul>
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function ___renderBreadcrumbs() {
|
|
|
|
|
|
|
|
|
|
if(!$this->isLoggedIn || $this->isModal) return '';
|
|
|
|
|
$process = $this->wire('page')->process;
|
|
|
|
|
if($process == 'ProcessPageList') return '';
|
|
|
|
|
$breadcrumbs = $this->wire('breadcrumbs');
|
|
|
|
|
$out = '';
|
|
|
|
|
|
|
|
|
|
// don't show breadcrumbs if only one of them (subjective)
|
|
|
|
|
if(count($breadcrumbs) < 2 && $process != 'ProcessPageEdit') return '';
|
|
|
|
|
|
|
|
|
|
if(strpos($this->layout, 'sidenav') === false) {
|
|
|
|
|
$out = "<li>" . $this->renderQuickTreeLink() . "</li>";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach($breadcrumbs as $breadcrumb) {
|
|
|
|
|
$title = $breadcrumb->get('titleMarkup');
|
|
|
|
|
if(!$title) $title = $this->wire('sanitizer')->entities1($this->_($breadcrumb->title));
|
|
|
|
|
$out .= "<li><a href='$breadcrumb->url'>$title</a></li>";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($out) $out = "<ul class='uk-breadcrumb'>$out</ul>";
|
|
|
|
|
|
|
|
|
|
return $out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Render the populated “Add New” head button, or blank when not applicable
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function renderAddNewButton() {
|
|
|
|
|
|
|
|
|
|
$items = array();
|
|
|
|
|
|
|
|
|
|
foreach($this->getAddNewActions() as $item) {
|
|
|
|
|
$icon = $this->renderNavIcon($item['icon']);
|
|
|
|
|
$items[] = "<li><a href='$item[url]'>$icon$item[label]</a></li>";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!count($items)) return '';
|
|
|
|
|
|
|
|
|
|
$out = implode('', $items);
|
|
|
|
|
$label = $this->getAddNewLabel();
|
|
|
|
|
$icon = $this->renderIcon('angle-down');
|
|
|
|
|
|
|
|
|
|
$out =
|
|
|
|
|
"<button class='ui-button pw-dropdown-toggle'>$label $icon</button>" .
|
|
|
|
|
"<ul class='pw-dropdown-menu' data-at='right bottom+1'>$out</ul>";
|
|
|
|
|
|
|
|
|
|
return $out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Render runtime notices div#notices
|
|
|
|
|
*
|
|
|
|
|
* @param Notices|bool $notices
|
|
|
|
|
* @param array $options See defaults in method
|
|
|
|
|
* @return string|array
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function renderNotices($notices, array $options = array()) {
|
|
|
|
|
|
|
|
|
|
$defaults = array(
|
|
|
|
|
'groupByType' => $this->groupNotices ? true : false,
|
|
|
|
|
'messageClass' => 'NoticeMessage uk-alert uk-alert-primary', // class for messages
|
|
|
|
|
'messageIcon' => 'check-square', // default icon to show with notices
|
|
|
|
|
'warningClass' => 'NoticeWarning uk-alert uk-alert-warning', // class for warnings
|
|
|
|
|
'warningIcon' => 'exclamation-circle', // icon for warnings
|
|
|
|
|
'errorClass' => 'NoticeError uk-alert uk-alert-danger', // class for errors
|
|
|
|
|
'errorIcon' => 'exclamation-triangle', // icon for errors
|
|
|
|
|
'debugClass' => 'NoticeDebug uk-alert', // class for debug items (appended)
|
|
|
|
|
'debugIcon' => 'bug', // icon for debug notices
|
|
|
|
|
'closeClass' => 'pw-notice-remove notice-remove', // class for close notices link <a>
|
|
|
|
|
'closeIcon' => 'times', // icon for close notices link
|
|
|
|
|
'listMarkup' => "<ul class='pw-notices' id='notices'>{out}</ul><!--/notices-->",
|
|
|
|
|
'itemMarkup' =>
|
|
|
|
|
"<li class='{class}'>" .
|
|
|
|
|
"<div class='pw-container uk-container uk-container-expand'>{remove}{icon}{text}</div>" .
|
|
|
|
|
"</li>"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$options = array_merge($defaults, $options);
|
|
|
|
|
|
|
|
|
|
return parent::renderNotices($notices, $options);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Render a single top navigation item for the given page
|
|
|
|
|
*
|
|
|
|
|
* This function designed primarily to be called by the renderPrimaryNavItems() function.
|
|
|
|
|
*
|
|
|
|
|
* @param array $item
|
|
|
|
|
* @return string
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
protected function renderPrimaryNavItem(array $item) {
|
|
|
|
|
|
|
|
|
|
$title = $item['title'];
|
|
|
|
|
$out = "<li class='page-$item[id]-'>";
|
|
|
|
|
|
|
|
|
|
if(!count($item['children'])) {
|
|
|
|
|
$out .= "<a href='$item[url]'>$title</a></li>";
|
|
|
|
|
return $out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$out .=
|
|
|
|
|
"<a href='$item[url]' " .
|
|
|
|
|
"id='prnav-page-$item[id]' " .
|
|
|
|
|
"data-from='prnav-page-$item[parent_id]' " .
|
|
|
|
|
"class='pw-dropdown-toggle'>" .
|
|
|
|
|
"$title</a>";
|
|
|
|
|
|
|
|
|
|
$my = 'left-1 top';
|
|
|
|
|
if(in_array($item['name'], array('access', 'page', 'module'))) $my = 'left top';
|
|
|
|
|
$out .=
|
|
|
|
|
"<ul class='pw-dropdown-menu prnav' data-my='$my' data-at='left bottom'>" .
|
|
|
|
|
$this->renderPrimaryNavItemChildren($item['children']) .
|
|
|
|
|
"</ul>" .
|
|
|
|
|
"</li>";
|
|
|
|
|
|
|
|
|
|
return $out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Renders <li> items navigation from given nav array
|
|
|
|
|
*
|
|
|
|
|
* @param array $items
|
|
|
|
|
* @return string
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
protected function renderPrimaryNavItemChildren(array $items) {
|
|
|
|
|
$out = '';
|
|
|
|
|
|
|
|
|
|
foreach($items as $item) {
|
|
|
|
|
|
|
|
|
|
$icon = empty($item['icon']) ? '' : $this->renderNavIcon($item['icon']);
|
|
|
|
|
$title = $item['title'];
|
|
|
|
|
$out .= "<li class='page-$item[id]-'>";
|
|
|
|
|
|
|
|
|
|
if(!empty($item['children'])) {
|
|
|
|
|
$out .=
|
|
|
|
|
"<a class='pw-has-items' data-from='prnav-page-$item[parent_id]' href='$item[url]'>$icon$title</a>" .
|
|
|
|
|
"<ul>" . $this->renderPrimaryNavItemChildren($item['children']) . "</ul>";
|
|
|
|
|
|
|
|
|
|
} else if(!empty($item['navJSON'])) {
|
|
|
|
|
$item['navJSON'] = $this->wire('sanitizer')->entities($item['navJSON']);
|
|
|
|
|
$out .=
|
|
|
|
|
"<a class='pw-has-items pw-has-ajax-items' " .
|
|
|
|
|
"data-from='prnav-page-$item[parent_id]' " .
|
|
|
|
|
"data-json='$item[navJSON]' " .
|
|
|
|
|
"href='$item[url]'>$icon$title" .
|
|
|
|
|
"</a>" .
|
|
|
|
|
"<ul></ul>";
|
|
|
|
|
} else {
|
|
|
|
|
$out .= "<a href='$item[url]'>$icon$title</a>";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$out .= "</li>";
|
|
|
|
|
|
|
|
|
|
return $out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Render all top navigation items, ready to populate in ul#prnav
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function renderPrimaryNavItems() {
|
|
|
|
|
|
|
|
|
|
$cache = self::dev ? '' : $this->wire('session')->getFor($this, 'prnav');
|
|
|
|
|
if($cache) {
|
|
|
|
|
$this->markCurrentNavItems($cache);
|
|
|
|
|
return $cache;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$out = '';
|
|
|
|
|
$items = $this->getPrimaryNavArray();
|
|
|
|
|
|
|
|
|
|
foreach($items as $item) {
|
|
|
|
|
$out .= $this->renderPrimaryNavItem($item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!self::dev) $this->wire('session')->setFor($this, 'prnav', $out);
|
|
|
|
|
$this->markCurrentNavItems($out);
|
|
|
|
|
|
|
|
|
|
return $out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Render sidebar navigation that uses uk-nav
|
|
|
|
|
*
|
|
|
|
|
* The contents is the same as the Primary nav, except that output is prepared for sidebar.
|
|
|
|
|
* This method uses a session-cached version. To clear the cache, logout then log back in.
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function renderSidebarNavItems() {
|
|
|
|
|
|
|
|
|
|
// see if we can get it from the cache
|
|
|
|
|
$out = self::dev ? '' : $this->wire('session')->getFor($this, 'sidenav');
|
|
|
|
|
|
|
|
|
|
if(empty($out)) {
|
|
|
|
|
$out = $this->renderSidebarNavCache();
|
|
|
|
|
$this->wire('session')->setFor($this, 'sidenav', $out);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$out = str_replace('<!--pw-user-nav-label-->', $this->renderUserNavLabel(), $out);
|
|
|
|
|
$this->markCurrentNavItems($out);
|
|
|
|
|
|
|
|
|
|
return $out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Re-renders the sidebar nav to be cached
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
protected function renderSidebarNavCache() {
|
|
|
|
|
|
|
|
|
|
$out = '';
|
|
|
|
|
$items = $this->getPrimaryNavArray();
|
|
|
|
|
$ulAttrs = "class='uk-nav-sub uk-nav-default uk-nav-parent-icon' data-uk-nav='animation: false; multiple: true;'";
|
|
|
|
|
|
|
|
|
|
foreach($items as $item) {
|
|
|
|
|
|
|
|
|
|
$class = "page-$item[id]-";
|
|
|
|
|
$subnav = '';
|
|
|
|
|
|
|
|
|
|
foreach($item['children'] as $child) {
|
|
|
|
|
$icon = $child['icon'] ? $this->renderNavIcon($child['icon']) : '';
|
|
|
|
|
$childClass = "page-$child[id]-";
|
|
|
|
|
$childAttr = "";
|
|
|
|
|
$childNav = '';
|
|
|
|
|
if(count($child['children'])) {
|
|
|
|
|
$childClass .= ' uk-parent';
|
|
|
|
|
$childNavList = $this->renderPrimaryNavItemChildren($child['children']);
|
|
|
|
|
$childIcon = $this->renderNavIcon('arrow-circle-right');
|
|
|
|
|
$childNav =
|
|
|
|
|
"<ul $ulAttrs>" .
|
|
|
|
|
"<li class='pw-nav-dup'><a href='$child[url]'>$childIcon$child[title]</a></li>" .
|
|
|
|
|
$childNavList .
|
|
|
|
|
"</ul>";
|
|
|
|
|
} else if($child['navJSON']) {
|
|
|
|
|
$child['navJSON'] = $this->wire('sanitizer')->entities($child['navJSON']);
|
|
|
|
|
$childClass .= ' uk-parent';
|
|
|
|
|
$childAttr = " class='pw-has-items pw-has-ajax-items' data-json='$child[navJSON]'";
|
|
|
|
|
$childNav = "<ul $ulAttrs></ul>";
|
|
|
|
|
}
|
|
|
|
|
$subnav .= "<li class='$childClass'><a$childAttr href='$child[url]'>$icon$child[title]</a>";
|
|
|
|
|
$subnav .= $childNav . "</li>";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($subnav) {
|
|
|
|
|
$icon = $this->renderNavIcon($item['icon']);
|
|
|
|
|
$class .= " uk-parent";
|
|
|
|
|
$subnav =
|
|
|
|
|
"<ul $ulAttrs>" .
|
|
|
|
|
"<li class='pw-nav-dup'><a href='$item[url]'>$icon$item[title]</a></li>" .
|
|
|
|
|
$subnav .
|
|
|
|
|
"</ul>";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$out .=
|
|
|
|
|
"<li class='$class'><a href='$item[url]' id='sidenav-page-$item[id]'>$item[title]</a>" .
|
|
|
|
|
$subnav .
|
|
|
|
|
"</li>";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// render user nav
|
|
|
|
|
$out .=
|
|
|
|
|
"<li class='uk-parent'>" .
|
|
|
|
|
"<a href='#'><!--pw-user-nav-label--></a>" .
|
|
|
|
|
"<ul $ulAttrs>" . $this->renderUserNavItems() . "</ul>" .
|
|
|
|
|
"</li>";
|
|
|
|
|
|
|
|
|
|
return $out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Identify current items in the primary nav and add appropriate classes to them
|
|
|
|
|
*
|
|
|
|
|
* This presumes that navigation items in given $out markup use "page-[id]-" classes,
|
|
|
|
|
* which will be updated consistent with the current $page.
|
|
|
|
|
*
|
|
|
|
|
* @param $out
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
protected function markCurrentNavItems(&$out) {
|
|
|
|
|
$page = $this->wire('page');
|
|
|
|
|
foreach($page->parents()->and($page) as $p) {
|
|
|
|
|
$out = str_replace("page-$p-", "page-$p- uk-active", $out);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Render label for user masthead dropdown nav item
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function renderUserNavLabel() {
|
|
|
|
|
/** @var User $user */
|
|
|
|
|
$user = $this->wire('user');
|
|
|
|
|
$userLabel = $this->get('userLabel');
|
|
|
|
|
$userAvatar = $this->get('userAvatar');
|
|
|
|
|
$defaultIcon = 'user-circle';
|
|
|
|
|
|
|
|
|
|
if(strpos($userLabel, '{') !== false) {
|
|
|
|
|
if(strpos($userLabel, '{Name}') !== false) {
|
|
|
|
|
$userLabel = str_replace('{Name}', ucfirst($user->name), $userLabel);
|
|
|
|
|
} else if(strpos($userLabel, '{name}') !== false) {
|
|
|
|
|
$userLabel = str_replace('{name}', $user->name, $userLabel);
|
|
|
|
|
}
|
|
|
|
|
if(strpos($userLabel, '{') !== false) {
|
|
|
|
|
$userLabel = $user->getText($userLabel, true, true);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$userLabel = $this->wire('sanitizer')->entities($userLabel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($userAvatar) {
|
|
|
|
|
if($userAvatar === 'gravatar') {
|
|
|
|
|
if($user->email) {
|
|
|
|
|
$url = "https://www.gravatar.com/avatar/" . md5(strtolower(trim($user->email))) . "?s=80&d=mm&r=g";
|
|
|
|
|
$userAvatar = "<img class='pw-avatar' src='$url' alt='$user->name' /> ";
|
|
|
|
|
} else {
|
|
|
|
|
$userAvatar = $this->renderNavIcon("$defaultIcon fa-lg");
|
|
|
|
|
}
|
|
|
|
|
} else if(strpos($userAvatar, 'icon.') === 0) {
|
|
|
|
|
list(,$icon) = explode('.', $userAvatar);
|
|
|
|
|
$userAvatar = $this->renderNavIcon("$icon fa-lg");
|
|
|
|
|
} else if(strpos($userAvatar, ':')) {
|
|
|
|
|
list($fieldID, $fieldName) = explode(':', $userAvatar);
|
|
|
|
|
$field = $this->wire('fields')->get($fieldName);
|
|
|
|
|
if(!$field || !$field->type instanceof FieldtypeImage) {
|
|
|
|
|
$field = $this->wire('fields')->get((int) $fieldID);
|
|
|
|
|
}
|
|
|
|
|
if($field && $field->type instanceof FieldtypeImage) {
|
|
|
|
|
$value = $user->get($field->name);
|
|
|
|
|
if($value instanceof Pageimages) $value = $value->first();
|
|
|
|
|
if($value instanceof Pageimage) {
|
|
|
|
|
$value = $value->size(60, 60);
|
|
|
|
|
$userAvatar = "<img class='pw-avatar' src='$value->url' alt='$user->name' /> ";
|
|
|
|
|
} else {
|
|
|
|
|
$userAvatar = $this->renderNavIcon("$defaultIcon fa-lg");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$userAvatar = '';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($userAvatar) $userLabel = $userAvatar . $userLabel;
|
|
|
|
|
|
|
|
|
|
return $userLabel;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Render navigation for the “user” menu
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function renderUserNavItems() {
|
|
|
|
|
|
|
|
|
|
$items = $this->getUserNavArray();
|
|
|
|
|
$out = '';
|
|
|
|
|
|
|
|
|
|
foreach($items as $item) {
|
|
|
|
|
$label = $this->wire('sanitizer')->entities($item['title']);
|
|
|
|
|
$icon = isset($item['icon']) ? $this->renderNavIcon($item['icon']) : ' ';
|
|
|
|
|
$target = isset($item['target']) ? " target='$item[target]'" : '';
|
|
|
|
|
$out .= "<li><a$target href='$item[url]'>$icon$label</a></li>";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Render link that opens the quick page-tree panel
|
|
|
|
|
*
|
|
|
|
|
* @param string $icon Icon to use for link (default=sitemap)
|
|
|
|
|
* @param string $text Optional text to accompany icon (default=empty)
|
|
|
|
|
* @return string
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function renderQuickTreeLink($icon = 'tree', $text = '') {
|
|
|
|
|
$tree = $this->_('Tree');
|
|
|
|
|
$url = $this->wire('urls')->admin . 'page/';
|
|
|
|
|
return
|
|
|
|
|
"<a class='pw-panel' href='$url' data-tab-text='$tree' data-tab-icon='$icon' title='$tree'>" .
|
|
|
|
|
$this->renderNavIcon($icon) . $text .
|
|
|
|
|
"</a>";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the URL to the ProcessWire or brand logo (or <img> tag)
|
|
|
|
|
*
|
|
|
|
|
* @param array $options
|
|
|
|
|
* - `getURL` (bool): Return only the URL? (default=false)
|
|
|
|
|
* - `getNative` (bool): Return only the ProcessWire brand logo? (default=false)
|
|
|
|
|
* - `alt` (string): Alt attribute for <img> tag (default=auto)
|
|
|
|
|
* - `height` (string): Height style to use for SVG images (default='')
|
|
|
|
|
* @return string
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function getLogo(array $options = array()) {
|
|
|
|
|
|
|
|
|
|
/** @var Config $config */
|
|
|
|
|
$config = $this->wire('config');
|
|
|
|
|
/** @var Sanitizer $sanitizer */
|
|
|
|
|
$sanitizer = $this->wire('sanitizer');
|
|
|
|
|
|
|
|
|
|
$defaults = array(
|
|
|
|
|
'getURL' => false,
|
|
|
|
|
'getNative' => false,
|
|
|
|
|
'alt' => '',
|
|
|
|
|
'height' => '',
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$options = array_merge($defaults, $options);
|
|
|
|
|
$logoURL = $this->get('logoURL');
|
|
|
|
|
$logoQS = '';
|
|
|
|
|
$svg = false;
|
|
|
|
|
|
|
|
|
|
if(empty($logoURL) || $options['getNative'] || strpos($logoURL, '//') !== false) {
|
|
|
|
|
$native = true;
|
|
|
|
|
$logoURL = $this->url() . self::logo;
|
|
|
|
|
} else {
|
|
|
|
|
if(strpos($logoURL, '?')) list($logoURL, $logoQS) = explode('?', $logoURL, 2);
|
|
|
|
|
$logoURL = $config->urls->root . ltrim($logoURL, '/');
|
|
|
|
|
$logoURL = $sanitizer->entities($logoURL);
|
|
|
|
|
$native = false;
|
|
|
|
|
$svg = strtolower(pathinfo($logoURL, PATHINFO_EXTENSION)) === 'svg';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$alt = $options['alt'];
|
|
|
|
|
if(empty($alt) && $this->wire()->user->isLoggedin()) {
|
|
|
|
|
$alt = "ProcessWire $config->version";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$class = 'pw-logo ' . ($native ? 'pw-logo-native' : 'pw-logo-custom');
|
|
|
|
|
$attr = "class='$class' alt='$alt' ";
|
|
|
|
|
|
|
|
|
|
if($svg) {
|
|
|
|
|
if($options['height']) $attr .= "style='height:$options[height]' ";
|
|
|
|
|
if(strpos($logoQS, 'uk-svg') === 0) {
|
|
|
|
|
// if logo has "?uk-svg" query string, add uk-svg attribute which makes it styleable via CSS/LESS (PR#77)
|
|
|
|
|
$attr .= 'uk-svg ';
|
|
|
|
|
$logoQS = str_replace(array('uk-svg&', 'uk-svg'), '', $logoQS);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($logoQS) $logoURL .= '?' . $sanitizer->entities($logoQS);
|
|
|
|
|
|
|
|
|
|
$img = "<img src='$logoURL' $attr/>";
|
|
|
|
|
|
|
|
|
|
return $options['getURL'] ? $logoURL : $img;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the URL to the ProcessWire or brand logo
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function getLogoURL() {
|
|
|
|
|
return $this->getLogo(array('getURL' => true));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get URL to this admin theme
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
* @since 3.0.171
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function url() {
|
|
|
|
|
return $this->wire()->config->urls->modules . 'AdminTheme/AdminThemeUikit/';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get disk path to this admin theme
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
* @since 3.0.171
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function path() {
|
|
|
|
|
return __DIR__ . '/';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the primary Uikit CSS URL to use
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
* @since 3.0.178 Was not hookable in prior versions
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function ___getUikitCSS() {
|
|
|
|
|
|
|
|
|
|
$config = $this->wire()->config;
|
|
|
|
|
$cssUrl = $this->get('cssURL');
|
|
|
|
|
|
|
|
|
|
if($cssUrl) { // a custom css URL was set in the theme config
|
|
|
|
|
if(strpos($cssUrl, '//') === false) $cssUrl = $config->urls->root . ltrim($cssUrl, '/');
|
|
|
|
|
return $this->wire()->sanitizer->entities($cssUrl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
require_once(__DIR__ . '/AdminThemeUikitCss.php');
|
|
|
|
|
|
|
|
|
|
$settings = $config->AdminThemeUikit;
|
|
|
|
|
if(!is_array($settings)) $settings = array();
|
|
|
|
|
$settings['requireCssVersion'] = self::requireCssVersion;
|
|
|
|
|
|
|
|
|
|
if(self::upgrade) {
|
|
|
|
|
$settings['upgrade'] = true;
|
|
|
|
|
$settings['replacements'] = array('../pw/images/' => 'images/');
|
|
|
|
|
} else {
|
|
|
|
|
if(empty($settings['customCssFile'])) $settings['customCssFile'] = '/site/assets/admin.css';
|
|
|
|
|
$path = 'wire/modules/AdminTheme/AdminThemeUikit/uikit-pw/images/';
|
|
|
|
|
$back = str_repeat('../', substr_count(trim($settings['customCssFile'], '/'), '/'));
|
|
|
|
|
$settings['replacements'] = array(
|
|
|
|
|
'url(../pw/images/' => "url($back$path",
|
|
|
|
|
'url("../pw/images/' => "url(\"$back$path",
|
|
|
|
|
'url(pw/images/' => "url($back$path",
|
|
|
|
|
'url("pw/images/' => "url(\"$back$path"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$css = new AdminThemeUikitCss($this, $settings);
|
|
|
|
|
|
|
|
|
|
return $css->getCssFile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get Javascript that must be present in the document <head>
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function getHeadJS() {
|
|
|
|
|
|
|
|
|
|
$config = $this->wire()->config;
|
|
|
|
|
$data = $config->js('adminTheme');
|
|
|
|
|
if(!is_array($data)) $data = array();
|
|
|
|
|
$data['logoAction'] = (int) $this->logoAction;
|
|
|
|
|
$data['toggleBehavior'] = (int) $this->toggleBehavior;
|
|
|
|
|
$config->js('adminTheme', $data);
|
|
|
|
|
|
|
|
|
|
return parent::getHeadJS();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Module configuration
|
|
|
|
|
*
|
|
|
|
|
* @param InputfieldWrapper $inputfields
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public function getModuleConfigInputfields(InputfieldWrapper $inputfields) {
|
|
|
|
|
parent::getModuleConfigInputfields($inputfields);
|
|
|
|
|
include_once(__DIR__ . '/config.php');
|
|
|
|
|
$configHelper = new AdminThemeUikitConfigHelper($this);
|
|
|
|
|
$configHelper->configModule($inputfields);
|
|
|
|
|
}
|
|
|
|
|
}
|