array( 'title' => t('Administer Skinr'), ), 'edit skin settings' => array( 'title' => t('Edit skin settings.'), ), 'edit advanced skin settings' => array( 'title' => t('Edit advanced skin settings'), 'description' => t('Edit advanced skin settings, such as custom CSS classes.'), ), ); } /** * Implements hook_menu(). */ function skinr_ui_menu() { $items['admin/appearance/skinr'] = array( 'title' => 'Skinr', 'description' => 'Manage your skinr settings and rules, import and export skinr settings for each theme, and configure skinr\'s overlay settings.', 'page callback' => 'drupal_get_form', 'page arguments' => array('skinr_ui_list'), 'access arguments' => array('administer skinr'), 'file' => 'skinr_ui.admin.inc', 'type' => MENU_LOCAL_TASK, ); $items['admin/appearance/skinr/list'] = array( 'title' => 'List', 'description' => t('Manage Skinr settings.'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); // Themes. $items['admin/appearance/skinr/skins'] = array( 'title' => 'Skins', 'description' => 'Manage which skins are available when changing Skinr settings.', 'page callback' => 'drupal_get_form', 'page arguments' => array('skinr_ui_admin_skin_infos'), 'access arguments' => array('administer skinr'), 'file' => 'skinr_ui.admin.inc', 'type' => MENU_LOCAL_TASK, ); $items['admin/appearance/skinr/skins/select'] = array( 'title' => 'List', 'description' => 'Manage your skins.', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -1, ); $items['admin/appearance/skinr/skins/settings/%skinr_ui_skin_info'] = array( 'title' => 'Skin settings', 'title callback' => 'skinr_ui_skin_info_title', 'title arguments' => array(5), 'description' => 'Manage which options are available for each Skin when changing Skinr settings.', 'page callback' => 'drupal_get_form', 'page arguments' => array('skinr_ui_admin_skin_infos_settings', 5), 'access arguments' => array('administer skinr'), 'file' => 'skinr_ui.admin.inc', 'type' => MENU_LOCAL_TASK, ); // Rules. $items['admin/appearance/skinr/rules'] = array( 'title' => 'Rules', 'page callback' => 'skinr_rules', 'type' => MENU_LOCAL_TASK, 'access arguments' => array('administer skinr'), 'weight' => 1, 'description' => t('Configure region and page level Skinr rules.'), 'file' => 'skinr_ui.rules.inc', ); $items['admin/appearance/skinr/rules/add'] = array( 'title' => 'Create a new rule', 'page callback' => 'drupal_get_form', 'page arguments' => array('skinr_rule_add'), 'type' => MENU_LOCAL_ACTION, 'access arguments' => array('administer skinr'), 'file' => 'skinr_ui.rules.inc', ); $items['admin/appearance/skinr/rules/edit'] = array( 'title' => 'Edit rule', 'page callback' => 'drupal_get_form', 'page arguments' => array('skinr_rule_edit'), 'type' => MENU_CALLBACK, 'access arguments' => array('administer skinr'), 'file' => 'skinr_ui.rules.inc', ); $items['admin/appearance/skinr/rules/delete'] = array( 'title' => 'Delete rule', 'page callback' => 'drupal_get_form', 'page arguments' => array('skinr_rule_delete_confirm'), 'type' => MENU_CALLBACK, 'access arguments' => array('administer skinr'), 'file' => 'skinr_ui.rules.inc', ); // Import & Export. $items['admin/appearance/skinr/import'] = array( 'title' => 'Import', 'page callback' => 'drupal_get_form', 'page arguments' => array('skinr_ui_import_form'), 'type' => MENU_LOCAL_TASK, 'access arguments' => array('administer skinr'), 'weight' => 2, 'description' => t('Import Skinr settings.'), 'file' => 'skinr_ui.admin.inc', ); $items['admin/appearance/skinr/export'] = array( 'title' => 'Export', 'page callback' => 'drupal_get_form', 'page arguments' => array('skinr_ui_export_form'), 'type' => MENU_LOCAL_TASK, 'access arguments' => array('administer skinr'), 'weight' => 3, 'description' => t('Export Skinr settings.'), 'file' => 'skinr_ui.admin.inc', ); // Edit Skinr settings. $items['admin/appearance/skinr/edit/%skinr_js/%/%'] = array( 'title' => 'Edit skin', 'title callback' => 'skinr_ui_edit_title', 'title arguments' => array(5, 6), 'page callback' => 'skinr_ui_edit', 'page arguments' => array(4, 5, 6), // js|nojs, module, sid 'type' => MENU_CALLBACK, 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE, 'access arguments' => array('edit skin settings'), ); $items['admin/appearance/skinr/edit/%skinr_js/%/%/configure'] = array( 'title' => 'Edit skin', 'title callback' => 'skinr_ui_edit_contextual_title', 'title arguments' => array(5, 6), 'type' => MENU_DEFAULT_LOCAL_TASK, 'context' => MENU_CONTEXT_INLINE, ); // Delete Skinr settings. $items['admin/appearance/skinr/delete/%/%/%'] = array( 'title' => 'Delete skin', 'page callback' => 'drupal_get_form', 'page arguments' => array('skinr_ui_delete_confirm', 4, 5, 6), // theme, module, sid 'type' => MENU_CALLBACK, 'access arguments' => array('administer skinr'), 'file' => 'skinr_ui.admin.inc', ); // Fetch skin file info through AJAX. $items['admin/appearance/skinr/info/%'] = array( 'title' => 'Skinr file information', 'page callback' => 'skinr_ui_skin_get_files', 'page arguments' => array(4), 'type' => MENU_CALLBACK, 'access arguments' => array('administer skinr'), ); return $items; } /** * Menu argument loader; Retrieves skin information for a single skin. */ function skinr_ui_skin_info_load($name) { $skin_info = skinr_get_skin_info(); if (isset($skin_info[$name])) { return $skin_info[$name]; } return FALSE; } /** * Menu title callback; Returns the title of a skin. */ function skinr_ui_skin_info_title($skin_info) { return check_plain($skin_info['title']); } /** * Helper function to determine if ajax is used to call a function. */ function skinr_js_load($js = 'nojs') { if ($js == 'ajax') { return TRUE; } return 0; } /** * Implements hook_theme(). */ function skinr_ui_theme() { return array( 'skinr_ui_admin_skin_infos_fieldset' => array( 'render element' => 'form', 'file' => 'skinr_ui.admin.inc', ), 'skinr_ui_admin_skin_infos_summary' => array( 'variables' => array('name' => NULL, 'description' => NULL), 'file' => 'skinr_ui.admin.inc', ), 'skinr_ui_admin_skin_infos_incompatible' => array( 'variables' => array('message' => NULL), 'file' => 'skinr_ui.admin.inc', ), 'skinr_ui_admin_skin_infos_settings_fieldset' => array( 'render element' => 'form', 'file' => 'skinr_ui.admin.inc', ), 'skinr_ui_admin_skin_infos_settings_summary' => array( 'variables' => array('name' => NULL, 'description' => NULL, 'theme hooks' => NULL), 'file' => 'skinr_ui.admin.inc', ), ); } /** * Implements hook_help(). */ function skinr_ui_help($path, $arg) { if (module_exists('advanced_help')) { $advanced_help = '

' . t('Visit the help page for full documentation.', array('@skinr-help' => url('admin/advanced_help/skinr'))). '

'; } else { $advanced_help = '

' . t('Please download and enable the Advanced Help module for full Skinr documentation.') . '

'; } switch ($path) { case 'admin/appearance/skinr': return '

' . t('Below is a list of all Skinr settings in use on this site.') . '

' . $advanced_help; case 'admin/appearance/skinr/rule': return '

' . t('Below is a list of Skinr rules. Rules can be created for region and page elements. Start by creating a new rule.') . '

'; case 'admin/appearance/skinr/rule/add': return '

' . t('Choose the type of rule you wish to create. Page rules apply classes to the <body> tag. Region rules apply to the region wrapper <div> tag.') . '

'; case 'admin/appearance/skinr/import': return '

' . t('To import Skinr settings, ensure the correct theme is selected, paste exported code and click the "Import" button.') . '

'; case 'admin/appearance/skinr/export': return '

' . t('To export Skinr settings, ensure the correct theme is selected and click the "Export" button.') . '

'; break; } } /** * Menu title callback; sets the title for a Skinr settings form page. * * @param $module * The module that we're editing settings of. * @param $sid * The sid of the object we're editing settings of. */ function skinr_ui_edit_title($module, $sid) { return t('Skinr settings for !module type !sid', array('!module' => $module, '!sid' => $sid)); } /** * Menu title callback; sets the title for a Skinr settings form page. * * @param $module * The module that we're editing settings of. * @param $sid * The sid of the object we're editing settings of. */ function skinr_ui_edit_contextual_title($module, $sid) { $contextual_links = skinr_ui_get_contextual_links(); foreach ($contextual_links as $hook => $links) { $counter = 1; foreach ($links as $link) { if ($link[1][0] == $module && $link[1][1] == $sid) { if (count($links) > 1) { return t('Edit skin !number', array('!number' => $counter)); } break 2; } $counter++; } } return t('Edit skin'); } /** * Menu callback; prepares some variables and displays a Skinr edit form. * * @param $js * TRUE if called from javascript, FALSE otherwise. * @param $module * The module that we're editing settings of. * @param $sid * The sid of the object we're editing settings of. * @param $sids * An array of $sid when more than one is returned from the preprocess * index handler. Used by the javascript UI to update all elements involved. */ function skinr_ui_edit($js = FALSE, $module, $sid, $sids = NULL) { if ($js) { // Do additional ajax related stuff. } $arguments = array( 'skinr' => array( 'module' => $module, 'sid' => $sid, 'sids' => $sids, ), ); return drupal_get_form('skinr_ui_form', $arguments); } /** * Form builder for the skinr settings configuration form. * * @param $arguments * An array of arguments as passed in by skinr_ui_edit(). * * @ingroup forms */ function skinr_ui_form($form, &$form_state, $arguments) { $form = array( '#attributes' => array('class' => 'skinr-form'), ); $form['skinr']['module'] = array( '#type' => 'hidden', '#value' => !empty($form_state['skinr']['module']) ? $form_state['skinr']['module'] : $arguments['skinr']['module'], ); $form['skinr']['sid'] = array( '#type' => 'hidden', '#value' => !empty($form_state['skinr']['sid']) ? $form_state['skinr']['sid'] : $arguments['skinr']['sid'], ); if (!empty($form_state['skinr']['sids']) || !empty($arguments['skinr']['sids'])) { $form['skinr']['sids'] = array( '#type' => 'hidden', '#value' => !empty($form_state['skinr']['sids']) ? $form_state['skinr']['sids'] : $arguments['skinr']['sids'], ); } $form['submit'] = array( '#type' => 'submit', '#value' => t('Save'), '#weight' => 50, ); return $form; } /** * Implements hook_form_alter(). */ function skinr_ui_form_alter(&$form, $form_state, $form_id) { // Fix for update script. if ($form_id == 'update_script_selection_form') { return; } $config = skinr_get_config_info(); $skin_infos = skinr_get_skin_info(); $groups = skinr_get_group_info(); foreach ($config as $module => $module_settings) { if (isset($module_settings['form'][$form_id])) { if (!empty($form['skinr']) && $form['skinr']['module']['#value'] !== $module) { continue; } $form_settings = array_merge(skinr_config_info_default(), $module_settings['form'][$form_id]); // Check for access. if (!skinr_handler('access_handler', 'edit skin settings', $form_settings['access_handler'], $form, $form_state)) { // Deny access. break; } // Ensure we have the required preprocess_hook or preprocess_hook_callback. if (empty($form_settings['preprocess_hook']) && empty($form_settings['preprocess_hook_callback'])) { trigger_error(sprintf("No preprocess_hook or preprocess_hook_callback was found for form_id '%s' in module '%s'.", $form_id, $module), E_USER_ERROR); } $themes = list_themes(); ksort($themes); foreach ($themes as $theme) { if (!$theme->status) { continue; } if (!isset($form['skinr_settings'])) { $form['skinr_settings'] = array( '#tree' => TRUE, '#weight' => $form_settings['skinr_weight'], ); } $preprocess_hooks = isset($form_settings['preprocess_hook']) ? $form_settings['preprocess_hook'] : skinr_handler('preprocess_hook_callback', '', $form_settings['preprocess_hook_callback'], $form, $form_state); if (!is_array($preprocess_hooks)) { $preprocess_hooks = array($preprocess_hooks); } // If this hook is a region, and the region does not exist for this // theme, don't bother outputting any of the settings. if (strpos($preprocess_hooks[0], 'region') === 0) { // Strip the region__ part off the region name. $region = substr($preprocess_hooks[0], 8); $regions = system_region_list($theme->name, REGIONS_VISIBLE); if (!isset($regions[$region])) { continue; } } if (!$form_state['submitted']) { if ($skin = skinr_handler('data_handler', 'form', $form_settings['data_handler'], $form, $form_state, $theme->name, $module, $form_settings)) { $defaults = $skin->skins['_options']; $additional_default = isset($skin->skins['_additional']) ? $skin->skins['_additional'] : ''; } else { $defaults = array(); $additional_default = ''; } } else { // Handle preview before submit. $defaults = $form_state['values']['widgets']; $additional_default = $form_state['values']['_additional']; } if (!isset($form['skinr_settings'][$module . '_group'])) { $form['skinr_settings'][$module . '_group'] = array( '#type' => 'fieldset', '#title' => t('@skinr_title @title', array('@skinr_title' => $form_settings['skinr_title'], '@title' => $form_settings['title'])), '#description' => t($form_settings['description']) . ' ' . implode(', ', $preprocess_hooks) . '.', '#weight' => $form_settings['weight'], '#collapsible' => TRUE, '#collapsed' => $form_settings['collapsed'], ); } // Get current theme, but make sure it's not the admin theme when we're editing with AJAX. $current_theme = skinr_current_theme(TRUE); $form['skinr_settings'][$module . '_group'][$theme->name] = array( '#type' => 'fieldset', '#title' => $theme->info['name'] . ($theme->name == $current_theme ? ' (' . t('enabled + default') . ')' : ''), '#collapsible' => TRUE, '#collapsed' => $theme->name == $current_theme ? FALSE : TRUE, ); if ($theme->name == $current_theme) { $form['skinr_settings'][$module . '_group'][$theme->name]['#attributes'] = array('class' => array('skinr-ui-current-theme')); $form['skinr_settings'][$module . '_group'][$theme->name]['#weight'] = -10; } // Create individual widgets for each skin. foreach ($skin_infos as $skin_name => $skin_info) { // Check if this skin is disabled. if (empty($skin_info['status'][$theme->name])) { continue; } // Check if this skin applies to this hook. if (!is_array($skin_info['theme hooks']) || (!in_array('*', $skin_info['theme hooks']) && !_skinr_is_featured($preprocess_hooks, $skin_info['theme hooks']))) { continue; } // Create widget. if (!empty($skin_info['form callback']) && function_exists($skin_info['form callback'])) { // @todo Allow for custom form callbacks. $field = array(); } else { switch ($skin_info['type']) { case 'checkboxes': $field = array( '#type' => 'checkboxes', '#multiple' => TRUE, '#title' => t($skin_info['title']), '#options' => skinr_ui_info_options_to_form_options($skin_info['options']), '#default_value' => isset($defaults[$skin_name]) ? $defaults[$skin_name] : array(), '#description' => t($skin_info['description']), '#weight' => isset($skin_info['weight']) ? $skin_info['weight'] : NULL, ); break; case 'radios': $field = array( '#type' => 'radios', '#title' => t($skin_info['title']), '#options' => array_merge(array('' => '<none>'), skinr_ui_info_options_to_form_options($skin_info['options'])), '#default_value' => isset($defaults[$skin_name]) ? $defaults[$skin_name] : '', '#description' => t($skin_info['description']), '#weight' => isset($skin_info['weight']) ? $skin_info['weight'] : NULL, ); break; case 'select': $field = array( '#type' => 'select', '#title' => t($skin_info['title']), '#options' => array_merge(array('' => ''), skinr_ui_info_options_to_form_options($skin_info['options'])), '#default_value' => isset($defaults[$skin_name]) ? $defaults[$skin_name] : '', '#description' => t($skin_info['description']), '#weight' => isset($skin_info['weight']) ? $skin_info['weight'] : NULL, ); break; } } if (empty($skin_info['group']) || empty($groups[$skin_info['group']])) { $form['skinr_settings'][$module . '_group'][$theme->name]['widgets'][$skin_name] = $field; } else { if (!isset($form['skinr_settings'][$module . '_group'][$theme->name]['widgets'][$skin_info['group']])) { $group = $groups[$skin_info['group']]; $form['skinr_settings'][$module . '_group'][$theme->name]['widgets'][$skin_info['group']] = array( '#type' => 'fieldset', '#title' => t($group['title']), '#description' => t($group['description']), '#weight' => isset($group['weight']) ? $group['weight'] : NULL, ); } $form['skinr_settings'][$module . '_group'][$theme->name]['widgets'][$skin_info['group']][$skin_name] = $field; } } // Check for access. if (skinr_handler('access_handler', 'edit advanced skin settings', $form_settings['access_handler'], $form, $form_state)) { $form['skinr_settings'][$module . '_group'][$theme->name]['_additional'] = array( '#type' => 'textfield', '#title' => t('CSS classes'), '#size' => 40, '#description' => t('To add CSS classes manually, enter classes separated by a single space i.e. first-class second-class'), '#default_value' => $additional_default, ); // Only add validation handler once. if (!isset($form['#validate']) || !in_array('skinr_ui_form_validate', $form['#validate'])) { $form['#validate'][] = 'skinr_ui_form_validate'; } // Special for views. if (isset($form['buttons']['submit']['#validate']) && !in_array('skinr_ui_form_validate', $form['buttons']['submit']['#validate'])) { $form['buttons']['submit']['#validate'][] = 'skinr_ui_form_validate'; } } } // Add weight to additional settings and submit form elements. $form['additional_settings']['#weight'] = 39; $form['submit']['#weight'] = 40; // Only add submit handler once. // Find the property to check. $attr = &$form; foreach($form_settings['submit_handler_attach_to'] as $part) { $attr = &$attr[$part]; } if (!empty($attr) && !in_array('skinr_ui_form_submit', $attr)) { $string = $attr[] = 'skinr_ui_form_submit'; } // Keep looping, there might be other modules that implement the same form_id. } } } /** * Form validation handler for skinr_ui_form_alter(). */ function skinr_ui_form_validate($form, &$form_state) { $form_id = $form_state['values']['form_id']; $config = skinr_get_config_info(); $error = FALSE; foreach ($config as $module => $module_settings) { if (isset($module_settings['form'][$form_id]) && isset($form_state['values']['skinr_settings'][$module . '_group'])) { foreach ($form_state['values']['skinr_settings'][$module . '_group'] as $theme_name => $theme) { if (isset($theme['_additional'])) { $form_settings = array_merge(skinr_config_info_default(), $module_settings['form'][$form_id]); // Validate additional classes field. if (preg_match('/[^a-zA-Z0-9\-\_\s]/', $theme['_additional'])) { form_set_error('skinr_settings][' . $module . '_group][' . $theme_name . '][_additional', t('Additional classes for Skinr may only contain alphanumeric characters, spaces, - and _.')); $error = TRUE; } // Keep looping, there might be other modules that implement the same form_id. } } } } // Undo any grouping to ease processing on submit. if (!$error) { $groups = skinr_get_group_info(); foreach ($config as $module => $module_settings) { if (isset($module_settings['form'][$form_id]) && isset($form_state['values']['skinr_settings'][$module . '_group'])) { foreach ($form_state['values']['skinr_settings'][$module . '_group'] as $theme_name => $theme) { foreach ($groups as $group_name => $group) { if (!empty($theme['widgets'][$group_name]) && is_array($theme['widgets'][$group_name])) { $group_values = $theme['widgets'][$group_name]; unset($form_state['values']['skinr_settings'][$module . '_group'][$theme_name]['widgets'][$group_name]); $form_state['values']['skinr_settings'][$module . '_group'][$theme_name]['widgets'] = array_merge($form_state['values']['skinr_settings'][$module . '_group'][$theme_name]['widgets'], $group_values); } } } } } } } /** * Form submission handler for skinr_ui_form_alter(). */ function skinr_ui_form_submit($form, &$form_state) { $form_id = $form_state['values']['form_id']; $config = skinr_get_config_info(); $current_theme = skinr_current_theme(TRUE); foreach ($config as $module => $module_settings) { if (isset($module_settings['form'][$form_id])) { $form_settings = array_merge(skinr_config_info_default(), $module_settings['form'][$form_id]); skinr_handler('submit_handler', '', $form_settings['submit_handler'], $form, $form_state, $module, $form_settings); // Keep looping, there might be other modules that implement the same form_id. } } } /** * Implements hook_preprocess(). */ function skinr_ui_preprocess(&$variables, $hook) { $config = skinr_get_config_info(); $original_hook = $hook; $theme_registry = theme_get_registry(); if (isset($theme_registry[$hook]['original hook'])) { $original_hook = $theme_registry[$hook]['original hook']; } foreach ($config as $module => $module_settings) { if (!empty($module_settings['preprocess'][$original_hook])) { // Set contextual links. if (!empty($module_settings['contextual_links'][$original_hook])) { if ($contextual_links = skinr_handler('contextual_links', '', $module_settings['contextual_links'][$original_hook]['contextual_links_handler'], $variables)) { skinr_ui_contextual_links($variables, $original_hook, $contextual_links); } } } } } /** * Set contextual menu items for skinr. * * @param $variables * The $variables parameter from a preprocess function. * @param $hook * The $hook parameter from a preprocess function. * @param $contextual_links * An array of contextual links data as returned from Skinr's contextual * links handler. */ function skinr_ui_contextual_links(&$variables, $hook, $contextual_links) { _skinr_ui_set_contextual_links($hook, $contextual_links); $hooks = theme_get_registry(); // Determine the primary theme function argument. if (isset($hooks[$hook]['variables'])) { $keys = array_keys($hooks[$hook]['variables']); $key = $keys[0]; } else { $key = $hooks[$hook]['render element']; } if (isset($variables[$key])) { $element =& $variables[$key]; } if (isset($element) && is_array($element)) { foreach ($contextual_links as $key => $contextual_link) { $element['#contextual_links'][$key] = $contextual_link; } } } /** * Get all contextual links as returned from Skinr's contextual links handler. * * @return * An array of contextual links data. */ function skinr_ui_get_contextual_links() { return _skinr_ui_set_contextual_links(); } /** * Store contextual links internally for future use. * * @return * An array of contextual links data. */ function _skinr_ui_set_contextual_links($hook = NULL, $links = NULL) { static $contextual_links = array(); if ($hook && $links) { if (!isset($contextual_links[$hook])) { $contextual_links[$hook] = $links; } } return $contextual_links; } /** * Helper function to determine whether one of a set of hooks exists in a list * of required theme hooks. * * @param $preprocess_hooks * An array of theme hooks available to this element. * @param $theme_hooks * An array of allowed theme hooks. * * @return * TRUE if an overlap is found, FALSE otherwise. * * @todo Rename function to be more descriptive. */ function _skinr_is_featured($preprocess_hooks, $theme_hooks) { foreach ($preprocess_hooks as $preprocess_hook) { if (in_array($preprocess_hook, $theme_hooks)) { return TRUE; } } return FALSE; } /** * Helper function to retrieve a unique id for each skinr class. Used by AJAX. * * @return * A unique ID number. * * @todo Evaluate the usefulness of this function. Should it go into * a UI front-end specific file? */ function _skinr_ui_ajax_id() { static $skinr_id = 0; return ++$skinr_id; } /** * Helper function to convert an array of options, as specified in the .info * file, into an array usable by Form API. * * @param $options * An array containing at least the 'class' and 'label' keys. * * @return * A Form API compatible array of options. * * @todo Rename function to be more descriptive. */ function skinr_ui_info_options_to_form_options($options) { $form_options = array(); foreach ($options as $option_name => $option) { $form_options[$option_name] = t($option['title']); } return $form_options; }