skinr-8.x-2.0-alpha2/skinr_ui/skinr_ui.admin.inc
skinr_ui/skinr_ui.admin.inc
<?php
/**
* @file
* Admin page callbacks for the Skinr UI module.
*/
/**
* Implements hook_skinr_ui_operations().
*/
function skinr_ui_skinr_ui_operations() {
$operations = [
'enable' => [
'label' => t('Enable selected skin configuration'),
'callback' => 'skinr_ui_mass_update',
'callback arguments' => ['updates' => ['status' => 1]],
],
'disable' => [
'label' => t('Disable selected skin configuration'),
'callback' => 'skinr_ui_mass_update',
'callback arguments' => ['updates' => ['status' => 0]],
],
'delete' => [
'label' => t('Delete selected skin configuration'),
'callback' => NULL,
],
];
return $operations;
}
/**
* List skinr administration filters that can be applied.
*
* @return
* An array of filters.
*/
function skinr_ui_filters() {
// Theme filter.
$theme_handler = \Drupal::service('theme_handler');
$themes = $theme_handler->listInfo();
ksort($themes);
$options = ['[any]' => t('any')];
foreach ($themes as $theme) {
if (!$theme->status) {
continue;
}
$options[$theme->name] = $theme->info['name'];
}
$filters['theme'] = [
'title' => t('theme'),
'options' => $options,
];
// Type filter.
$filters['module'] = [
'title' => t('type'),
'options' => skinr_get_config_info(),
];
// Element filter.
$elements = \Drupal::database()->query("SELECT module, element, theme FROM {skinr_skins} GROUP BY element, module, theme");
$options = ['[any]' => t('any')];
foreach ($elements as $element) {
$skin = \Drupal::service('entity_type.manager')->getStorage('skin')->create(['element_type' => $element_type, 'element' => $element, 'theme' => $theme]);
$options[$element->element] = $skin->elementLabel();
}
$filters['element'] = [
'title' => t('element'),
'options' => $options,
];
// Skin filter.
$skin_infos = skinr_get_skin_info();
$options = ['[any]' => t('any'), '_additional' => t('Additional classes')];
foreach ($skin_infos as $skin_name => $skin_info) {
$options[$skin_name] = $skin_info['title'];
}
$filters['skin'] = [
'title' => t('skin'),
'options' => $options,
];
// Status filter.
$filters['status'] = [
'title' => t('status'),
'options' => [
'[any]' => t('any'),
'1' => t('enabled'),
'0' => t('disabled'),
],
];
// Allow modules to add filters.
drupal_alter('skinr_ui_filters', $filters);
return $filters;
}
/**
* Apply filters for skin configuration administration filters based on session.
*
* @param $query
* A SelectQuery to which the filters should be applied.
*/
function skinr_ui_build_filter_query(SelectQueryInterface $query) {
// Build query.
$filter_data = $_SESSION['skinr_ui_filters'] ?? [];
foreach ($filter_data as $index => $filter) {
[$key, $value] = $filter;
$query->condition('s.' . $key, $value);
}
}
/**
* Form builder for the Skinr administration filters form.
*
* @ingroup forms
*/
function skinr_ui_filter_form($filters_callback, &$form_state) {
$form_state['#callback'] = $filters_callback;
$session = &$_SESSION[$filters_callback];
$session = is_array($session) ? $session : [];
$filters = $filters_callback();
$i = 0;
$form['filters'] = [
'#type' => 'fieldset',
'#title' => t('Show only items where'),
'#theme' => 'exposed_filters__skinr',
];
foreach ($session as $filter) {
[$type, $value] = $filter;
$value = $filters[$type]['options'][$value];
$t_args = ['%property' => $filters[$type]['title'], '%value' => $value];
if ($i++) {
$form['filters']['current'][] = ['#markup' => t('and where %property is %value', $t_args)];
}
else {
$form['filters']['current'][] = ['#markup' => t('where %property is %value', $t_args)];
}
// Remove the option if it is already being filtered on.
unset($filters[$type]);
}
$form['filters']['status'] = [
'#type' => 'container',
'#attributes' => ['class' => ['clearfix']],
'#prefix' => ($i ? '<div class="additional-filters">' . t('and where') . '</div>' : ''),
];
$form['filters']['status']['filters'] = [
'#type' => 'container',
'#attributes' => ['class' => ['filters']],
];
foreach ($filters as $key => $filter) {
$names[$key] = $filter['title'];
$form['filters']['status']['filters'][$key] = [
'#type' => 'select',
'#title' => $filter['title'],
'#options' => $filter['options'],
'#default_value' => '[any]',
];
}
$form['filters']['status']['actions'] = [
'#type' => 'actions',
'#id' => 'skinr-exposed-filters',
'#attributes' => ['class' => ['container-inline']],
];
if (count($filters)) {
$form['filters']['status']['actions']['submit'] = [
'#type' => 'submit',
'#value' => count($session) ? t('Refine') : t('Filter'),
];
}
if (count($session)) {
$form['filters']['status']['actions']['undo'] = ['#type' => 'submit', '#value' => t('Undo')];
$form['filters']['status']['actions']['reset'] = ['#type' => 'submit', '#value' => t('Reset')];
}
drupal_add_js('misc/form.js');
return $form;
}
/**
* Mass update skin configurations, updating all skin configurations in the
* $skins array with the field values in $updates.
*
* IMPORTANT NOTE: This function is intended to work when called
* from a form submit handler. Calling it outside of the form submission
* process may not work correctly.
*
* @param array $skins
* Array of skin configuration sids to update.
* @param array $updates
* Array of key/value pairs with skin configuration field names and the
* value to update that field to.
*/
function skinr_ui_mass_update($skins, $updates) {
// We use batch processing to prevent timeout when updating a large number
// of skins.
if (count($skins) > 10) {
$batch = [
'operations' => [
['_skinr_ui_mass_update_batch_process', [$skins, $updates]],
],
'finished' => '_skinr_ui_mass_update_batch_finished',
'title' => t('Processing'),
// We use a single multi-pass operation, so the default
// 'Remaining x of y operations' message will be confusing here.
'progress_message' => '',
'error_message' => t('The update has encountered an error.'),
// The operations do not live in the .module file, so we need to
// tell the batch engine which file to load before calling them.
'file' => \Drupal::service('extension.path.resolver')->getPath('module', 'skinr_ui') . '/skinr_ui.admin.inc',
];
batch_set($batch);
}
else {
foreach ($skins as $sid) {
_skinr_ui_mass_update_helper($sid, $updates);
}
\Drupal::messenger()->addStatus(t('The update has been performed.'));
}
}
/**
* Helper function for skin configuration mass updates.
*/
function _skinr_ui_mass_update_helper($sid, $updates) {
drupal_static_reset('skinr_skin_load_multiple');
$skin = \Drupal::service('entity_type.manager')->getStorage('skin')->load($sid);
// For efficiency manually store the original skin configuration before
// applying any changes.
$skin->original = clone $skin;
foreach ($updates as $name => $value) {
$skin->$name = $value;
}
skinr_skin_save($skin);
return $skin;
}
/**
* Batch operation for skin configuration mass updates.
*/
function _skinr_ui_mass_update_batch_process($skins, $updates, &$context) {
if (!isset($context['sandbox']['progress'])) {
$context['sandbox']['progress'] = 0;
$context['sandbox']['max'] = count($skins);
$context['sandbox']['skins'] = $skins;
}
// Process skins in groups of 5.
$count = min(5, count($context['sandbox']['skins']));
for ($i = 1; $i <= $count; $i++) {
// For each sid, load the skin configuration, reset the values, and save it.
$sid = array_shift($context['sandbox']['skins']);
$skin = _skinr_ui_mass_update_helper($sid, $updates);
// Store result for post-processing in the finished callback.
$context['results'][] = $skin->skin;
// Update our progress information.
$context['sandbox']['progress']++;
}
// Inform the batch engine that we are not finished,
// and provide an estimation of the completion level we reached.
if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
$context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
}
}
/**
* Batch 'finished' callback for skin configuration mass updates.
*/
function _skinr_ui_mass_update_batch_finished($success, $results, $operations) {
if ($success) {
\Drupal::messenger()->addStatus(t('The update has been performed.'));
}
else {
\Drupal::messenger()->addError(t('An error occurred and processing did not complete.'));
$message = \Drupal::translation()->formatPlural(count($results), '1 item successfully processed:', '@count items successfully processed:');
$message .= theme('item_list', ['items' => $results]);
\Drupal::messenger()->addStatus($message);
}
}
/**
* Form submission handler for skinr_ui_filter_form().
*/
function skinr_ui_filter_form_submit($form, &$form_state) {
$filters_callback = $form_state['#callback'];
$filters = $filters_callback();
switch ($form_state['values']['op']) {
case t('Filter'):
case t('Refine'):
// Apply every filter that has a choice selected other than 'any'.
foreach ($filters as $filter => $options) {
if (isset($form_state['values'][$filter]) && $form_state['values'][$filter] != '[any]') {
// Flatten the options array to accommodate hierarchical/nested options.
$flat_options = form_options_flatten($filters[$filter]['options']);
// Only accept valid selections offered on the dropdown, block bad input.
if (isset($flat_options[$form_state['values'][$filter]])) {
$_SESSION[$filters_callback][] = [$filter, $form_state['values'][$filter]];
}
}
}
break;
case t('Undo'):
array_pop($_SESSION[$filters_callback]);
break;
case t('Reset'):
$_SESSION[$filters_callback] = [];
break;
}
}
/**
* Form validation handler for skinr_ui_list().
*
* Check if any skinr settings have been selected to perform the chosen
* 'Update option' on.
*/
function skinr_ui_admin_skins_validate($form, &$form_state) {
// Error if there are no items to select.
if (!is_array($form_state['values']['skins']) || !count(array_filter($form_state['values']['skins']))) {
form_set_error('', t('No items selected.'));
}
}
/**
* Form submission handler for skinr_ui_list().
*
* Execute the chosen 'Update option' on the selected skinr settings.
*/
function skinr_ui_admin_skins_submit($form, &$form_state) {
$operations = module_invoke_all('skinr_ui_operations');
$operation = $operations[$form_state['values']['operation']];
// Filter out unchecked nodes.
$skins = array_filter($form_state['values']['skins']);
if ($function = $operation['callback']) {
// Add in callback arguments if present.
if (isset($operation['callback arguments'])) {
$args = array_merge([$skins], $operation['callback arguments']);
}
else {
$args = [$skins];
}
call_user_func_array($function, $args);
cache_clear_all();
}
else {
// We need to rebuild the form to go to a second step. For example, to
// show the confirmation form for the deletion of nodes.
$form_state['rebuild'] = TRUE;
}
}
/**
* Form builder for the confirmation form when deleting multiple Skinr settings.
*
* @param $skins
* An array of skins to delete.
*
* @ingroup forms
*/
function skinr_ui_multiple_delete_confirm($form, &$form_state, $skins) {
$theme_handler = \Drupal::service('theme_handler');
$themes = $theme_handler->listInfo();
$form['skins'] = ['#prefix' => '<ul>', '#suffix' => '</ul>', '#tree' => TRUE];
// array_filter returns only elements with TRUE values.
$original_skins = entity_load_multiple('skin', array_keys($skins));
foreach ($skins as $sid => $value) {
$form['skins'][$sid] = [
'#type' => 'hidden',
'#value' => $sid,
'#prefix' => '<li>',
'#suffix' => t('Skin %skin for element %element of type %type for the %theme theme', ['%skin' => $original_skins[$sid]->skin, '%element' => $original_skins[$sid]->element, '%type' => $original_skins[$sid]->module, '%theme' => $themes[$original_skins[$sid]->theme]->info['name'] ?? $original_skins[$sid]->theme]) . "</li>\n",
];
}
$form['operation'] = ['#type' => 'hidden', '#value' => 'delete'];
$form['#submit'][] = 'skinr_ui_multiple_delete_confirm_submit';
$confirm_question = \Drupal::translation()->formatPlural(count($skins),
'Are you sure you want to delete this item?',
'Are you sure you want to delete these items?');
return confirm_form($form,
$confirm_question,
'admin/structure/skinr', t('This action cannot be undone.'),
t('Delete'), t('Cancel'));
}
/**
* Form submission handler for skinr_ui_multiple_delete_confirm().
*/
function skinr_ui_multiple_delete_confirm_submit($form, &$form_state) {
if ($form_state['values']['confirm']) {
entity_delete_multiple('skin', array_keys($form_state['values']['skins']));
$count = count($form_state['values']['skins']);
if ($count == 1) {
\Drupal::logger('skinr')->notice('Deleted 1 skin configuration.');
}
else {
\Drupal::logger('skinr')->notice('Deleted @count skin configurations.', ['@count' => $count]);
}
\Drupal::messenger()->addStatus(\Drupal::translation()->formatPlural($count, 'Deleted 1 skin configuration.', 'Deleted @count skin configurations.'));
}
$form_state['redirect'] = 'admin/structure/skinr';
}
/**
*
*/
function skinr_ui_get_element_title($module, $element, $theme = NULL) {
static $current_theme;
static $themes;
if (empty($theme)) {
if (!isset($current_theme)) {
$current_theme = skinr_current_theme(TRUE);
}
$theme = $current_theme;
}
$title = skinr_invoke_all('skinr_ui_element_title', $module, $element, $theme);
$title = reset($title);
if (!$title) {
if (!isset($themes)) {
$theme_handler = \Drupal::service('theme_handler');
$themes = $theme_handler->listInfo();
}
foreach ($themes as $t) {
if (!empty($t->info['hidden'])) {
continue;
}
if ($t->name == $theme) {
// Already tried this one.
continue;
}
if ($title = skinr_invoke_all('skinr_ui_element_title', $module, $element, $t->name)) {
$title = reset($title);
break;
}
}
}
return $title;
}
