123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470 |
- <?php
- /**
- * @file
- * Handles core Skinr functionality.
- */
- /**
- * The Skinr API version.
- */
- define('SKINR_VERSION', 2);
- /**
- * Show this rule on every page except the listed pages.
- */
- define('SKINR_RULE_VISIBILITY_NOTLISTED', 0);
- /**
- * Show this rule on only the listed pages.
- */
- define('SKINR_RULE_VISIBILITY_LISTED', 1);
- /**
- * Show this rule if the associated PHP code returns TRUE.
- */
- define('SKINR_RULE_VISIBILITY_PHP', 2);
- /**
- * Implements hook_help().
- */
- function skinr_help($path, $arg) {
- switch ($path) {
- case 'admin/help#skinr':
- if (module_exists('advanced_help')) {
- return t('Visit the <a href="@skinr-help">help page</a> for full documentation.', array('@skinr-help' => url('admin/advanced_help/skinr')));
- }
- else {
- return t('Please download and enable the <a href="http://drupal.org/project/advanced_help">Advanced Help</a> module for full Skinr documentation.');
- }
- break;
- }
- }
- /**
- * Implements hook_hook_info().
- */
- function skinr_hook_info() {
- $hooks = array(
- 'skinr_api_2',
- 'skinr_elements',
- 'skinr_group_info',
- 'skinr_group_info_alter',
- 'skinr_skin_info',
- 'skinr_skin_info_alter',
- 'skinr_theme_hooks',
- 'skinr_theme_hooks_alter',
- );
- $hooks = array_fill_keys($hooks, array(
- 'group' => 'skinr',
- ));
- return $hooks;
- }
- /**
- * Clears cached Skinr information.
- */
- function skinr_cache_reset() {
- cache_clear_all('skinr_', 'cache', TRUE);
- }
- /**
- * Implements hook_preprocess().
- *
- * @todo Optimize this function by removing dependencies on
- * skinr_get_skin_info() and similar resource heavy functions.
- * @todo Account for Drupal's caching being enabled and make it work.
- */
- function skinr_preprocess(&$variables, $hook) {
- // Fix for update script.
- if ($hook == 'maintenance_page') {
- return;
- }
- $current_theme = skinr_current_theme();
- $skin_info = skinr_get_skin_info();
- $theme_registry = theme_get_registry();
- $original_hook = (isset($theme_registry[$hook]['original hook']) ? $theme_registry[$hook]['original hook'] : $hook);
- // An array of $elements based on $module and $original_hook, derived from $variables.
- $array_elements = skinr_invoke_all('skinr_elements', $variables, $original_hook, 'preprocess');
- foreach ($array_elements as $module => $elements) {
- if (empty($elements)) {
- // We can receive empty arrays; if that happens, there's no point
- // in continuing.
- continue;
- }
- // Get a list of skin configuration IDs to pass to
- // skinr_skin_load_multiple().
- $params = array(
- 'theme' => $current_theme,
- 'module' => $module,
- 'element' => $elements,
- 'status' => 1,
- );
- $sids = skinr_skin_get_sids($params);
- if (empty($sids)) {
- // Noting to apply.
- continue;
- }
- $applied_skins = array();
- foreach (skinr_skin_load_multiple($sids) as $skin) {
- $applied_skins = array($skin->skin => $skin->options) + $applied_skins;
- }
- // Invoke hook_skinr_preprocess_alter() in all modules.
- // @todo Review whether this alter hook is useful or not, and if it's in
- // the right place or not.
- $context = array(
- 'hook' => $hook,
- 'variables' => &$variables,
- 'theme' => $current_theme,
- 'module' => $module,
- 'elements' => $elements,
- 'options' => $applied_skins,
- );
- drupal_alter('skinr_preprocess', $context);
- // Use drupal_process_attached() to add attachements such as JS and CSS.
- if (!empty($applied_skins)) {
- foreach ($applied_skins as $skin_name => $skin_options) {
- // Special case for _additional.
- if ($skin_name == '_additional') {
- continue;
- }
- // Make sure this skin is enabled for the current theme.
- if (isset($skin_info[$skin_name]['attached'])) {
- $elements['#attached'] = $skin_info[$skin_name]['attached'];
- drupal_process_attached($elements);
- }
- if (!is_array($skin_options)) {
- $skin_options = array($skin_options);
- }
- foreach ($skin_options as $skin_option) {
- if (isset($skin_info[$skin_name]['options'][$skin_option]['attached'])) {
- $elements['#attached'] = $skin_info[$skin_name]['options'][$skin_option]['attached'];
- drupal_process_attached($elements);
- }
- }
- }
- $variables['classes_array'] = array_merge($variables['classes_array'], skinr_flatten_skins_array($applied_skins));
- }
- }
- }
- /**
- * Returns an array of classes.
- *
- * @param $skin_options
- * An array of skin options keyed by their skin name. The key '_additional'
- * is reserved for additional classes entered by the user.
- *
- * @todo Optimize this function by removing dependencies on the resource heavy
- * skinr_get_skin_info() function.
- * @todo Rename function to reflect new functionality.
- */
- function skinr_flatten_skins_array($skin_options) {
- $skin_info = skinr_get_skin_info();
- $classes = array();
- foreach ($skin_options as $skin_name => $options) {
- if ($skin_name == '_additional') {
- $classes = array_merge($classes, $options);
- }
- else {
- foreach ($options as $option) {
- if (!empty($skin_info[$skin_name]['options'][$option]['class'])) {
- $classes = array_merge($classes, $skin_info[$skin_name]['options'][$option]['class']);
- }
- }
- }
- }
- return array_unique($classes);
- }
- // ------------------------------------------------------------------
- // Rule functions.
- /**
- * Validate a rule object.
- *
- * @param $rule
- * A rule object.
- *
- * @return
- * TRUE on success, FALSE on failure.
- */
- function skinr_rule_validate(&$rule) {
- if (empty($rule->title) || empty($rule->rule_type)) {
- return FALSE;
- }
- if (!isset($rule->node_types)) {
- $rule->node_types = array();
- }
- if (!isset($rule->roles)) {
- $rule->roles = array();
- }
- if (!isset($rule->visibility)) {
- $rule->visibility = 0;
- }
- if (!isset($rule->pages)) {
- $rule->pages = '';
- }
- if (!is_array($rule->node_types) || !is_array($rule->roles)) {
- return FALSE;
- }
- if ($rule->visibility !== 0 && $rule->visibility !== 1 && $rule->visibility !== 2) {
- return FALSE;
- }
- return TRUE;
- }
- /**
- * Save a skinr rule object.
- *
- * @param $rule
- * A rule object.
- *
- * @return
- * The rule ID.
- */
- function skinr_rule_save($rule) {
- // Make sure we're getting valid data.
- if (!skinr_rule_validate($rule)) {
- return FALSE;
- }
- $status = drupal_write_record('skinr_rules', $rule, !empty($rule->rid) ? array('rid') : array());
- return $status;
- }
- /**
- * Load a skinr rule object.
- *
- * @param $rid
- * (optional) The rule ID.
- *
- * @return
- * A rule object. If no $rid is specified an array of all rules will be
- * returned.
- */
- function skinr_rule_load($rid = NULL) {
- $rids = (isset($rid) ? array($rid) : array());
- $rules = skinr_rule_load_multiple($rids);
- return $rules ? reset($rules) : FALSE;
- }
- /**
- * Loads multiple skinr rule objects.
- *
- * @param $rids
- * An array of rule IDs. Optional.
- * @param $conditions
- * An array of conditions on the {skinr_rules} table in the form 'field' =>
- * $value.
- *
- * @return
- * An array of rule objects indexed by rid. If $rids is not provided, all
- * rules are returned.
- */
- function skinr_rule_load_multiple($rids = array(), $conditions = array()) {
- $rules = array();
- $select = db_select('skinr_rules')->fields('skinr_rules');
- if (!empty($rids)) {
- $select->condition('rid', $rids);
- }
- foreach ($conditions as $field => $condition) {
- $select->condition($field, $condition);
- }
- foreach ($select->execute() as $rule) {
- $rule->node_types = unserialize($rule->node_types);
- $rule->roles = unserialize($rule->roles);
- $rules[$rule->rid] = $rule;
- }
- return $rules;
- }
- /**
- * Delete a skinr rule object.
- *
- * @param $rid
- * The rule ID.
- */
- function skinr_rule_delete($rid) {
- if ($rule = skinr_rule_load($rid)) {
- db_delete('skinr_rules')
- ->condition('rid', $rule->rid)
- ->execute();
- db_delete('skinr_skins')
- ->condition('module', 'page')
- ->condition('element', $rule->rid)
- ->execute();
- }
- }
- /**
- * Determines if the rule should be visible for a given path.
- *
- * @param $rid
- * The rule ID.
- * @param $path
- * (optional) The path to check. Defaults to the path of the current page.
- * @param $account
- * (optional) The account to check. Defaults to currently logged in user.
- *
- * @return
- * TRUE if the rule should be visible, FALSE otherwise.
- */
- function skinr_rule_is_visible($rid, $path = NULL, $account = NULL) {
- global $user;
- if (!isset($account)) {
- $account = $user;
- }
- if ($rule = skinr_rule_load($rid)) {
- if (!isset($path)) {
- $path = $_GET['q'];
- }
- // Check role visibility.
- if (!empty($rule->roles) && ($account->uid != 1) && !count(array_intersect(array_keys($account->roles), $rule->roles))) {
- return FALSE;
- }
- // Check content type visibility.
- // If a rule has no node types associated, it is displayed for every type.
- // For rules with node types associated, if the node type does not match
- // the settings from this rule, return FALSE.
- if (!empty($rule->node_types)) {
- $node = menu_get_object('node', 1, $path);
- $node_types = node_type_get_types();
- if (arg(0, $path) == 'node' && arg(1, $path) == 'add' && arg(2, $path)) {
- $node_add_arg = strtr(arg(2, $path), '-', '_');
- }
- if (!empty($node)) {
- // This is a node or node edit page.
- if (empty($rule->node_types[$node->type])) {
- // This rule should not be displayed for this node type.
- return FALSE;
- }
- }
- elseif (isset($node_add_arg) && isset($node_types[$node_add_arg])) {
- // This is a node creation page.
- if (!isset($rule->node_types[$node_add_arg]) || !$rule->node_types[$node_add_arg]) {
- // This rule should not be displayed for this node type.
- return FALSE;
- }
- }
- else {
- // This is not a node page, remove the rule.
- return FALSE;
- }
- }
- // Match path if necessary.
- if ($rule->pages) {
- // Convert path to lowercase. This allows comparison of the same path
- // with different case. Ex: /Page, /page, /PAGE.
- $pages = drupal_strtolower($rule->pages);
- if ($rule->visibility < SKINR_RULE_VISIBILITY_PHP) {
- // Convert the Drupal path to lowercase
- $path = drupal_strtolower(drupal_get_path_alias($path));
- // Compare the lowercase internal and lowercase path alias (if any).
- $page_match = drupal_match_path($path, $pages);
- if ($path != $_GET['q']) {
- $page_match = $page_match || drupal_match_path($path, $pages);
- }
- // When $rule->visibility has a value of 0 (SKINR_RULE_VISIBILITY_NOTLISTED),
- // the rule is displayed on all pages except those listed in $rule->pages.
- // When set to 1 (SKINR_RULE_VISIBILITY_LISTED), it is displayed only on those
- // pages listed in $rule->pages.
- $page_match = !($rule->visibility xor $page_match);
- }
- elseif (module_exists('php')) {
- $page_match = php_eval($rule->pages);
- }
- else {
- $page_match = FALSE;
- }
- }
- else {
- $page_match = TRUE;
- }
- return $page_match;
- }
- return FALSE;
- }
- /**
- * Returns a list of extensions that implement this API version of Skinr.
- *
- * @return
- * An associative array whose keys are system names of extensions and whose
- * values are again associative arrays containing:
- * - type: Either 'module' or 'theme'.
- * - name: The system name of the extension.
- * - path: The path to the extension.
- * - directory: (optional) The sub-directory holding Skinr plugin files.
- * - ...: Any other properties defined by the module or theme.
- */
- function skinr_implements_api() {
- $cache = &drupal_static(__FUNCTION__);
- if (!isset($cache)) {
- if ($cached = cache_get('skinr_implements_api')) {
- $cache = $cached->data;
- return $cache;
- }
- $cache = array();
- // Collect hook_skinr_api_VERSION() module implementations. This will also
- // auto-load $module.skinr.inc files, which may contain skin/group hook
- // implementations (when not using the plugin system).
- $module_info = system_get_info('module');
- foreach (module_implements('skinr_api_' . SKINR_VERSION) as $module) {
- // Ensure that $module and the extension type is registered.
- $cache[$module] = array(
- 'type' => 'module',
- 'name' => $module,
- 'version' => isset($module_info[$module]['version']) ? $module_info[$module]['version'] : NULL,
- );
- // Check whether the hook returns any information.
- $function = $module . '_skinr_api_' . SKINR_VERSION;
- $info = $function();
- if (isset($info) && is_array($info)) {
- $cache[$module] += $info;
- }
- // If the module specified a custom path, check whether it contains a
- // $module.skinr.inc file and auto-load it. module_implements() only
- // auto-loads $module.skinr.inc in a module's root folder.
- if (isset($cache[$module]['path'])) {
- $file = $cache[$module]['path'] . '/' . $module . '.skinr.inc';
- if (file_exists(DRUPAL_ROOT . '/' . $file)) {
- $cache[$module]['include file'] = $file;
- }
- }
- // Populate defaults.
- $cache[$module] += array(
- 'path' => drupal_get_path('module', $module),
- 'directory' => NULL,
- );
- }
- // Collect the equivalent of hook_skinr_api_VERSION() implementations in
- // themes. The theme system only initializes one theme (and optionally its
- // base themes) for the current request, and the phptemplate engine only
- // loads template.php during theme initialization. Furthermore, template.php
- // is a custom concept of the phptemplate engine and does not exist for
- // other theme engines. Since we are interested in all existing
- // implementations of all enabled themes, the equivalent of the module hook
- // is a theme .info file property 'skinr' that has the sub-keys 'api' and
- // optionally 'directory' defined.
- // Account for all enabled themes and (any recursive) base themes of them,
- // regardless of whether base themes are enabled.
- $all_themes = list_themes();
- $themes = array();
- // Additionally record the base themes and sub themes of each theme, in
- // order to apply inheritance rules elsewhere. Do not assign these variables
- // as properties on the theme objects themselves, since all objects are
- // pointers (much like references) in PHP 5, so our properties would be
- // visible for everyone else who calls list_themes().
- $base_themes = array();
- $sub_themes = array();
- foreach ($all_themes as $name => $theme) {
- // If the theme is enabled, add it to the stack.
- if (!empty($theme->status)) {
- $themes[$name] = $theme;
- // Find and add all base themes of the enabled theme to the stack.
- // @see drupal_theme_initialize()
- $sub_theme_name = $name;
- while ($name && isset($all_themes[$name]->base_theme)) {
- // Record the sub theme for the base theme.
- $sub_themes[$all_themes[$name]->base_theme][$name] = $name;
- // Add the base theme to the stack.
- $name = $all_themes[$name]->base_theme;
- $themes[$name] = $all_themes[$name];
- // Record the base theme for the original sub theme.
- $base_themes[$sub_theme_name][$name] = $name;
- }
- }
- }
- foreach ($themes as $name => $theme) {
- if (isset($theme->info['skinr']['api']) && $theme->info['skinr']['api'] == SKINR_VERSION) {
- // Ensure that the theme name and the extension type is registered.
- $cache[$name] = array(
- 'type' => 'theme',
- 'name' => $name,
- 'version' => isset($theme->info['version']) ? $theme->info['version'] : NULL,
- 'base themes' => isset($base_themes[$name]) ? $base_themes[$name] : array(),
- 'sub themes' => isset($sub_themes[$name]) ? $sub_themes[$name] : array(),
- );
- // Add any additional information that has been registered.
- $cache[$name] += $theme->info['skinr'];
- // Populate defaults.
- $cache[$name] += array(
- 'path' => drupal_get_path('theme', $name),
- // Since themes cannot do anything else than registering skins and
- // groups, we default to the sub-directory 'skins'.
- 'directory' => 'skins',
- );
- // Lastly, for API consistency with modules, check whether the theme
- // contains a $theme.skinr.inc file and auto-load it, if any.
- $file = $cache[$name]['path'] . '/' . $name . '.skinr.inc';
- if (file_exists(DRUPAL_ROOT . '/' . $file)) {
- $cache[$name]['include file'] = $file;
- }
- }
- }
- cache_set('skinr_implements_api', $cache);
- }
- return $cache;
- }
- /**
- * Determine whether a module implements a hook.
- *
- * Replacement for module_hook() that only invokes modules that implement
- * the current version of Skinr API. It also supports $module.skinr.inc files
- * in themes and custom paths.
- *
- * @param $module
- * The name of the module (without the .module extension).
- * @param $hook
- * The name of the hook (e.g. "skinr_skin_info" or "skinr_theme_hooks").
- *
- * @return
- * TRUE if the module is both installed and enabled, and the hook is
- * implemented in that module.
- */
- function skinr_hook($module, $hook) {
- $function = $module . '_' . $hook;
- if (function_exists($function)) {
- return TRUE;
- }
- // If the hook implementation does not exist, check whether it may live in an
- // include file in a custom path.
- $extensions = skinr_implements_api();
- if (isset($extensions[$module])) {
- $extension = $extensions[$module];
- if (isset($extension['include file'])) {
- // The module specified a custom path. module_hook() only auto-loads
- // $module.skinr.inc in a module's root folder.
- skinr_load_include($extension['include file']);
- if (function_exists($module . '_' . $hook)) {
- return TRUE;
- }
- }
- else {
- // Run through module_hook() to auto-load $module.skinr.inc from a
- // non-custom path.
- if (module_hook($module, $hook)) {
- return TRUE;
- }
- }
- }
- return FALSE;
- }
- /**
- * Determine which modules are implementing a hook.
- *
- * Replacement for module_implements() that only invokes modules that implement
- * the current version of Skinr API. It also supports $module.skinr.inc files
- * in themes and custom paths.
- *
- * @param $hook
- * The name of the hook (e.g. "skinr_skin_info" or "skinr_theme_hooks").
- *
- * @return
- * An array with the names of the modules which are implementing this hook.
- *
- * @see skinr_exit()
- */
- function skinr_implements($hook) {
- $implementations = &drupal_static(__FUNCTION__, array());
- // Fetch implementations from cache.
- if (empty($implementations)) {
- $implementations = cache_get('skinr_implements', 'cache_bootstrap');
- if ($implementations === FALSE) {
- $implementations = array();
- }
- else {
- $implementations = $implementations->data;
- }
- }
- if (!isset($implementations[$hook])) {
- $implementations['#write_cache'] = TRUE;
- $extensions = skinr_implements_api();
- $implementations[$hook] = array();
- foreach ($extensions as $module => $extension) {
- if (isset($extension['include file'])) {
- // The module specified a custom path. module_implements() and
- // module_hook() only auto-load $module.skinr.inc in a module's
- // root folder.
- $include_file = skinr_load_include($extension['include file']);
- if (function_exists($module . '_' . $hook)) {
- $implementations[$hook][$module] = $include_file ? $extension['include file'] : FALSE;
- }
- }
- else {
- // Run through module_hook() to auto-load $module.skinr.inc from a
- // non-custom path.
- if (module_hook($module, $hook)) {
- $implementations[$hook][$module] = FALSE;
- }
- }
- }
- // Allow modules to change the weight of specific implementations but avoid
- // an infinite loop.
- if ($hook != 'skinr_implements_alter') {
- drupal_alter('skinr_implements', $implementations[$hook], $hook);
- }
- }
- else {
- foreach ($implementations[$hook] as $module => $file) {
- if ($file) {
- skinr_load_include($file);
- }
- else {
- module_hook($module, $hook);
- }
- // It is possible that a module removed a hook implementation without the
- // implementations cache being rebuilt yet, so we check whether the
- // function exists on each request to avoid undefined function errors.
- // Since module_hook() may needlessly try to load the include file again,
- // function_exists() is used directly here.
- if (!function_exists($module . '_' . $hook)) {
- // Clear out the stale implementation from the cache and force a cache
- // refresh to forget about no longer existing hook implementations.
- unset($implementations[$hook][$module]);
- $implementations['#write_cache'] = TRUE;
- }
- }
- }
- return array_keys($implementations[$hook]);
- }
- /**
- * Implements hook_exit().
- *
- * @see module_implements_write_cache()
- */
- function skinr_exit($destination = NULL) {
- $implementations = &drupal_static('skinr_implements');
- // Check whether we need to write the cache. We do not want to cache hooks
- // which are only invoked on HTTP POST requests since these do not need to be
- // optimized as tightly, and not doing so keeps the cache entry smaller.
- if (isset($implementations['#write_cache']) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD')) {
- unset($implementations['#write_cache']);
- cache_set('skinr_implements', $implementations, 'cache_bootstrap');
- }
- }
- /**
- * Invoke a hook in all enabled modules and themes that implement it.
- *
- * Replacement for module_invoke_all() that only invokes modules that implement
- * the current version of Skinr API. It also supports $module.skinr.inc files
- * in themes and custom paths.
- *
- * @param $hook
- * The name of the hook to invoke.
- * @param ...
- * Arguments to pass to the hook.
- *
- * @return
- * An array of return values of the hook implementations. If modules return
- * arrays from their implementations, those are merged into one array.
- */
- function skinr_invoke_all($hook) {
- $args = func_get_args();
- // Remove $hook from the arguments.
- unset($args[0]);
- $return = array();
- foreach (skinr_implements($hook) as $module) {
- $function = $module . '_' . $hook;
- if (function_exists($function)) {
- $result = call_user_func_array($function, $args);
- if (isset($result) && is_array($result)) {
- $return = array_merge_recursive($return, $result);
- }
- elseif (isset($result)) {
- $return[] = $result;
- }
- }
- }
- return $return;
- }
- /**
- * Loads a $module.skinr.inc include file.
- */
- function skinr_load_include($file) {
- if (is_file($file)) {
- include_once $file;
- return $file;
- }
- return FALSE;
- }
- /**
- * Includes Skinr plugin files for an extension, if any.
- *
- * @param $extension
- * The API information for an extension, as returned by skinr_implements_api().
- */
- function skinr_load_plugins($extension) {
- static $loaded = array();
- // If plugins have already been loaded for this extension, return them.
- if (isset($loaded[$extension['name']])) {
- return $loaded[$extension['name']];
- }
- $loaded[$extension['name']] = array();
- // If the extension defines a plugin directory, scan its plugins.
- if (isset($extension['directory'])) {
- $dir = DRUPAL_ROOT . '/' . $extension['path'] . '/' . $extension['directory'];
- $mask = '@^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.inc$@';
- $loaded[$extension['name']] = file_scan_directory($dir, $mask, array(
- 'key' => 'name',
- 'recurse' => TRUE,
- 'min_depth' => 1,
- 'callback' => 'skinr_include_once',
- ));
- }
- return $loaded[$extension['name']];
- }
- /**
- * file_scan_directory() callback wrapper around include_once.
- *
- * include_once is a PHP construct, not a function, so it cannot be invoked
- * directly as 'callback' in file_scan_directory().
- */
- function skinr_include_once($file) {
- include_once $file;
- }
- // -----------------------------------------------------------------------
- // Skinr data handling functions.
- /**
- * Validate a skinr object.
- *
- * @param $skin
- * A skin object.
- *
- * @return
- * TRUE on success, FALSE on failure.
- */
- function skinr_skin_validate(&$skin) {
- if (empty($skin->theme) || empty($skin->module) || empty($skin->element) || empty($skin->skin) || empty($skin->options)) {
- return FALSE;
- }
- if (!is_array($skin->options)) {
- return FALSE;
- }
- // Strip empty skins.
- $skin->options = _skinr_array_strip_empty($skin->options);
- if (empty($skin->options)) {
- return FALSE;
- }
- return TRUE;
- }
- /**
- * Save a skin object.
- *
- * @param $skin
- * A skin object.
- *
- * @return
- * TRUE on success, FALSE on failure.
- */
- function skinr_skin_save(&$skin) {
- // Make sure we're getting valid data.
- if (!skinr_skin_validate($skin)) {
- return FALSE;
- }
- // Load the stored skin configuration object, if any.
- if (!empty($skin->sid)) {
- if (!isset($skin->original)) {
- // Load an uncached version of the skin configuration object.
- $skin->original = skinr_skin_load_unchanged($skin->sid);
- }
- }
- // Let modules modify the node before it is saved to the database.
- module_invoke_all('skinr_skin_presave', $skin);
- if (!empty($skin->sid)) {
- // Record exists, so let's update.
- $status = drupal_write_record('skinr_skins', $skin, 'sid');
- module_invoke_all('skinr_skin_update', $skin);
- }
- else {
- // Insert a new record.
- $status = drupal_write_record('skinr_skins', $skin);
- module_invoke_all('skinr_skin_insert', $skin);
- }
- // Clear internal properties.
- unset($skin->original);
- // Clear the static loading cache.
- // @todo Once we have a more granular reset for skinr_skin_load_multiple(), we
- // need to use it here.
- drupal_static_reset('skinr_skin_load_multiple');
- return $status;
- }
- /**
- * Delete a skin object.
- *
- * @param $sid
- * The skin configuration ID.
- */
- function skinr_skin_delete($sid) {
- skinr_skin_delete_multiple(array($sid));
- }
- /**
- * Delete multiple skin configuration objects.
- *
- * @param $sids
- * An array of skin configuration IDs.
- */
- function skinr_skin_delete_multiple($sids) {
- $transaction = db_transaction();
- if (!empty($sids)) {
- $skins = skinr_skin_load_multiple($sids);
- try {
- foreach ($skins as $sid => $skin) {
- module_invoke_all('skinr_skin_delete', $skin);
- }
- // Delete after calling hooks so that they can query node tables as needed.
- db_delete('skinr_skins')
- ->condition('sid', $sids, 'IN')
- ->execute();
- }
- catch (Exception $e) {
- $transaction->rollback();
- watchdog_exception('skinr', $e);
- throw $e;
- }
- // Clear the skinr_skin_load_multiple cache.
- drupal_static_reset('skinr_skin_load_multiple');
- }
- }
- /**
- * Load a skin configuration object from the database.
- *
- * @param $sid
- * The skin configuration ID.
- *
- * @return
- * A fully-populated skin configuration object.
- */
- function skinr_skin_load($sid = NULL) {
- $sids = (isset($sid) ? array($sid) : array());
- $skin = skinr_skin_load_multiple($sids);
- return $skin ? reset($skin) : FALSE;
- }
- /**
- * Load skin configuration objects from the database.
- *
- * This function should be used whenever you need to load more than one skin
- * configuration from the database. Skin configurations are loaded into memory
- * and will not require database access if loaded again during the same page
- * request.
- *
- * @see skinr_skin_get_sids()
- *
- * @param $sids
- * An array of skin configuration IDs.
- *
- * @return
- * An array of skin configuration objects indexed by sid.
- */
- function skinr_skin_load_multiple($sids = array()) {
- // @todo Do we want to write a more granular cache reset?
- $skins = &drupal_static(__FUNCTION__, array());
- // Create a new variable which is either a prepared version of the $sids
- // array for later comparison with cached skin configuration objects, or FALSE
- // if no $sids were passed. The $sids array is reduced as items are loaded
- // from cache, and we need to know if it's empty for this reason to avoid
- // querying the database when all requested skin configuration objects are
- // loaded from cache.
- $passed_sids = !empty($sids) ? array_flip($sids) : FALSE;
- if ($passed_sids) {
- $sids = array_keys(array_diff_key($passed_sids, $skins));
- }
- // Load any remaining skin configurations from the database. This is the
- // case if $sids is set to FALSE (so we load all skins), or if there are any
- // sids left to load.
- if ($sids === FALSE || $sids) {
- // Build the query.
- $queried_skins = db_select('skinr_skins', 's')
- ->fields('s')
- ->condition('sid', $sids)
- ->execute()
- ->fetchAllAssoc('sid');
- foreach ($queried_skins as $sid => $skin) {
- // Unserialize options array.
- $queried_skins[$sid]->options = unserialize($skin->options);
- // Let modules modify the skin configurations.
- module_invoke_all('skinr_skin_load', $queried_skins[$sid]);
- }
- $skins += $queried_skins;
- }
- // Ensure that the returned array is ordered the same as the original
- // $sids array if this was passed in and remove any invalid sids.
- if ($passed_sids) {
- // Remove any invalid sids from the array.
- $passed_sids = array_intersect_key($passed_sids, $skins);
- $return = array();
- foreach ($passed_sids as $sid => $ignore) {
- $return[$sid] = $skins[$sid];
- }
- }
- else {
- $return = $skins;
- }
- return $return;
- }
- /**
- * Load an uncached version of a skin configuration object.
- *
- * @param $sid
- * The skin configuration ID.
- *
- * @return
- * A fully-populated skin configuration object.
- */
- function skinr_skin_load_unchanged($sid) {
- // Load an uncached version of the skin configuration object.
- $skin = db_query("SELECT * FROM {skinr_skins} WHERE sid = :sid", array(
- ':sid' => $sid,
- ))
- ->fetchObject();
- // Unserialize options array.
- $skin->options = unserialize($skin->options);
- // Let modules modify the skin configuration.
- module_invoke_all('skinr_skin_load', $skin);
- return $skin;
- }
- /**
- * Get skin configuration IDs.
- *
- * @param $filter_by
- * An associative array whose keys are:
- * - theme: (optional) The theme.
- * - module: (optional) The module.
- * - element: (optional) The element ID.
- * - skin: (optional) The skin name.
- * - status: (optional) Boolean indicating whether or not this skin
- * configuration is enabled.
- *
- * @return
- * An array of skin configuration IDs.
- */
- function skinr_skin_get_sids($filter_by = array()) {
- $query = db_select('skinr_skins', 's')
- ->fields('s', array('sid'));
- if (isset($filter_by['theme'])) {
- $query->condition('theme', $filter_by['theme']);
- }
- if (isset($filter_by['module'])) {
- $query->condition('module', $filter_by['module']);
- }
- if (isset($filter_by['element'])) {
- $query->condition('element', $filter_by['element']);
- }
- if (isset($filter_by['skin'])) {
- $query->condition('skin', $filter_by['skin']);
- }
- if (isset($filter_by['status'])) {
- $query->condition('status', $filter_by['status']);
- }
- return $query->execute()
- ->fetchCol();
- }
- /**
- * Helper function to remove empty skins from an array.
- *
- * @param $array
- * A single or multi-dimensional array to strip of empty values.
- *
- * @return
- * An array stripped of empty values.
- */
- function _skinr_array_strip_empty($array) {
- $new_array = array();
- foreach ($array as $key => $value) {
- if (is_array($value)) {
- $value = _skinr_array_strip_empty($value);
- }
- if (!empty($value)) {
- $new_array[$key] = $value;
- }
- }
- return $new_array;
- }
- /**
- * Helper function to retrieve the current theme.
- *
- * The global variable $theme_key doesn't work for our purposes when an admin
- * theme is enabled.
- *
- * @param $exclude_admin_theme
- * Optional. Set to TRUE to exclude the admin theme from possible themes to
- * return.
- *
- * @return
- * The current theme name.
- */
- function skinr_current_theme($exclude_admin_theme = FALSE) {
- global $user, $custom_theme;
- if (!empty($user->theme) && drupal_theme_access($user->theme)) {
- $current_theme = $user->theme;
- }
- elseif (!empty($custom_theme) && drupal_theme_access($custom_theme) && !($exclude_admin_theme && $custom_theme == variable_get('admin_theme', '0'))) {
- // Don't return the admin theme if we're editing skinr settings.
- $current_theme = $custom_theme;
- }
- else {
- $current_theme = variable_get('theme_default', 'bartik');
- }
- return $current_theme;
- }
- /**
- * Prepare the default status for a skin.
- *
- * @param $skin_info
- * Information about a registered skin.
- *
- * @return
- * An array of default statuses for each enabled theme.
- */
- function skinr_skin_info_status_default($skin_info) {
- $status = array();
- // Retrieve the explicit default status of the registering theme for itself.
- $base_theme_status = NULL;
- if (isset($skin_info['status'][$skin_info['source']['name']])) {
- $base_theme_status = $skin_info['status'][$skin_info['source']['name']];
- }
- // Retrieve the sub themes of the base theme that registered the skin.
- $sub_themes = array();
- if (isset($skin_info['source']['sub themes'])) {
- $sub_themes = $skin_info['source']['sub themes'];
- }
- $themes = list_themes();
- foreach ($themes as $name => $theme) {
- if (!$theme->status) {
- continue;
- }
- // If this theme is a sub theme of the theme that registered the skin, check
- // whether we need to inherit the status of the base theme to the sub theme.
- // This is the case when a skin of a base theme enables itself for the base
- // theme (not knowing about potential sub themes).
- if (isset($base_theme_status) && isset($sub_themes[$name])) {
- $status[$name] = $base_theme_status;
- }
- // Apply global default.
- $status += array($name => $skin_info['default status']);
- }
- // Lastly, apply all explicit defaults.
- $status = array_merge($status, $skin_info['status']);
- return $status;
- }
- /**
- * Retrieve the overridden status of a skin.
- *
- * @param $skin_info
- * Information about a registered skin.
- *
- * @return
- * An array of statuses for each enabled theme. If no overrides are found,
- * the status defaults will be returned.
- */
- function skinr_skin_info_status_get($skin_info) {
- return variable_get('skinr_skin_' . $skin_info['name'] . '_status', $skin_info['status']);
- }
- /**
- * Set the status of a skin. Overrides the skin plugin settings.
- *
- * @param $skin_info
- * Information about a registered skin.
- * @param $status
- * An array of statuses for each theme.
- */
- function skinr_skin_info_status_set($skin_info, $status) {
- variable_set('skinr_skin_' . $skin_info['name'] . '_status', $status);
- }
- /**
- * Helper function to prepend a path to an array of stylesheet or script filenames.
- *
- * If the url is absolute (e.g. the url start with 'http://' or 'https://')
- * the path does not get prepended.
- *
- * @param $files
- * A an array of filenames that need the path prepended.
- * @todo Adjust docs to account for arrays instead of filenames.
- * @param $path
- * The path to prepend.
- */
- function _skinr_add_path_to_files(&$files, $path) {
- foreach ($files as $key => $file) {
- if (is_array($file)) {
- if (strpos($file[0], 'http://') === 0 || strpos($file[0], 'https://') === 0 ) {
- continue;
- }
- $files[$key][0] = $path . '/' . $file[0];
- }
- else {
- if (strpos($file, 'http://') === 0 || strpos($file, 'https://') === 0) {
- continue;
- }
- $files[$key] = $path . '/' . $file;
- }
- }
- }
- /**
- * Parse a skin_infos array as returned from a skins plugin.
- *
- * This function inserts any missing defaults and updates the stylesheet and
- * script paths to be relative to Drupal's root.
- *
- * @param $skin_infos
- * An array of skins as returned from skin plugins.
- * @param $source
- * An associative array containing information about the source of the skin.
- * See skinr_implements() for details.
- *
- * @todo Merge into skinr_get_skin_info() and remove this function.
- */
- function skinr_skin_info_process(&$skin_infos, $source) {
- foreach ($skin_infos as $skin_name => $skin_info) {
- // Populate default properties.
- $skin_infos[$skin_name] += array(
- 'name' => '',
- 'title' => '',
- 'type' => 'checkboxes',
- 'description' => '',
- 'group' => 'general',
- 'theme hooks' => array('*'),
- 'attached' => array(),
- 'options' => array(),
- 'weight' => NULL,
- 'default status' => 0,
- 'status' => array(),
- );
- // Merge in name.
- $skin_infos[$skin_name]['name'] = $skin_name;
- // Merge in source information.
- $skin_infos[$skin_name]['source'] = $source;
- // Merge in default status for all themes.
- $skin_infos[$skin_name]['status'] = skinr_skin_info_status_default($skin_infos[$skin_name]);
- // Add path to stylesheets.
- if (isset($skin_infos[$skin_name]['attached']['css'])) {
- _skinr_add_path_to_files($skin_infos[$skin_name]['attached']['css'], $source['path']);
- }
- // Add path to scripts.
- if (isset($skin_infos[$skin_name]['attached']['js'])) {
- _skinr_add_path_to_files($skin_infos[$skin_name]['attached']['js'], $source['path']);
- }
- foreach ($skin_infos[$skin_name]['options'] as $option_name => $option) {
- // Add path to stylesheets.
- if (isset($option['attached']['css'])) {
- _skinr_add_path_to_files($skin_infos[$skin_name]['options'][$option_name]['attached']['css'], $source['path']);
- }
- // Add path to scripts.
- if (isset($option['attached']['js'])) {
- _skinr_add_path_to_files($skin_infos[$skin_name]['options'][$option_name]['attached']['js'], $source['path']);
- }
- // Validate class by running it through drupal_html_class().
- if (!is_array($skin_infos[$skin_name]['options'][$option_name]['class'])) {
- $skin_infos[$skin_name]['options'][$option_name]['class'] = array($skin_infos[$skin_name]['options'][$option_name]['class']);
- }
- foreach ($skin_infos[$skin_name]['options'][$option_name]['class'] as $key => $class) {
- $skin_infos[$skin_name]['options'][$option_name]['class'][$key] = drupal_html_class($class);
- }
- }
- }
- }
- /**
- * Retrieves all skins registered by modules and themes.
- *
- * @return
- * An array of skins.
- */
- function skinr_get_skin_info() {
- $skin_info = &drupal_static(__FUNCTION__);
- if (!isset($skin_info)) {
- if ($cached = cache_get('skinr_skin_info')) {
- $skin_info = $cached->data;
- return $skin_info;
- }
- $skin_info = array();
- foreach (skinr_implements_api() as $name => $extension) {
- $hooks = array();
- // Run through skinr_hook to ensure the required include gets loaded.
- if (skinr_hook($name, 'skinr_skin_info')) {
- $hooks["{$name}_skinr_skin_info"] = $extension;
- }
- // Load the extension's plugins, if any.
- if ($files = skinr_load_plugins($extension)) {
- // The base path for plugins is the directory defined by the extension.
- $dir = $extension['path'] . '/' . $extension['directory'];
- foreach ($files as $plugin => $file) {
- $hooks["{$name}_skinr_skin_{$plugin}_info"] = array(
- // The source path for a plugin is the plugin directory.
- 'path' => $dir . '/' . basename(dirname($file->uri)),
- 'filename' => $file->filename,
- ) + $extension;
- }
- }
- foreach ($hooks as $function => $source) {
- if (function_exists($function)) {
- $extension_info = $function();
- if (isset($extension_info) && is_array($extension_info)) {
- // Prepare the skin information.
- skinr_skin_info_process($extension_info, $source);
- $skin_info += $extension_info;
- }
- }
- }
- }
- // Allow modules to alter registered skin information.
- drupal_alter('skinr_skin_info', $skin_info);
- cache_set('skinr_skin_info', $skin_info);
- }
- return $skin_info;
- }
- /**
- * Retrieves all skin groups registered by modules and themes.
- *
- * @return
- * An array of groups.
- */
- function skinr_get_group_info() {
- $group_info = &drupal_static(__FUNCTION__);
- if (!isset($group_info)) {
- if ($cached = cache_get('skinr_group_info')) {
- $group_info = $cached->data;
- return $group_info;
- }
- $group_info = array();
- foreach (skinr_implements_api() as $name => $extension) {
- $hooks = array();
- // Run through skinr_hook to ensure the required include gets loaded.
- if (skinr_hook($name, 'skinr_group_info')) {
- $hooks["{$name}_skinr_group_info"] = $extension;
- }
- // Load the extension's plugins, if any.
- if ($files = skinr_load_plugins($extension)) {
- // The base path for plugins is the directory defined by the extension.
- $dir = $extension['path'] . '/' . $extension['directory'];
- foreach ($files as $plugin => $file) {
- $hooks["{$name}_skinr_group_{$plugin}_info"] = array(
- // The source path for a plugin is the plugin directory.
- 'path' => $dir . '/' . basename(dirname($file->uri)),
- 'filename' => $file->filename,
- ) + $extension;
- }
- }
- foreach ($hooks as $function => $source) {
- if (function_exists($function)) {
- $extension_info = $function();
- if (isset($extension_info) && is_array($extension_info)) {
- // Prepare the skin group information.
- foreach ($extension_info as &$group) {
- $group += array(
- 'title' => '',
- 'description' => '',
- 'weight' => 0,
- );
- }
- $group_info += $extension_info;
- }
- }
- }
- }
- // Allow modules to alter groups through hook_skinr_group_info_alter().
- drupal_alter('skinr_group_info', $group_info);
- cache_set('skinr_group_info', $group_info);
- }
- return $group_info;
- }
- /**
- * Fetch Skinr configuration data from functionality plugins.
- *
- * @return
- * An array of all configuration data.
- */
- function skinr_get_config_info() {
- $config_info = &drupal_static(__FUNCTION__);
- if (!isset($config_info)) {
- if ($cached = cache_get('skinr_config_info')) {
- $config_info = $cached->data;
- return $config_info;
- }
- $config_info = skinr_invoke_all('skinr_config_info');
- // Allow modules to alter config info via hook_skinr_config_info_alter().
- drupal_alter('skinr_config_info', $config_info);
- cache_set('skinr_config_info', $config_info);
- }
- return $config_info;
- }
- /**
- * Provide a list of all available theme hooks for a given element.
- *
- * @param $module
- * The module implementing given element.
- * @param $element
- * An element.
- *
- * @return
- * An array of theme hooks.
- */
- function skinr_theme_hooks($module, $element) {
- $theme_hooks = &drupal_static(__FUNCTION__, array());
- if (!isset($theme_hooks[$module][$element])) {
- // Invoke hook_skinr_theme_hooks() and hook_skinr_theme_hooks_alter().
- $theme_hooks[$module][$element] = skinr_invoke_all('skinr_theme_hooks', $module, $element);
- drupal_alter('skinr_theme_hooks', $theme_hooks[$module][$element], $module, $element);
- }
- return $theme_hooks[$module][$element];
- }
- /**
- * Implements hook_modules_enabled().
- */
- function skinr_modules_enabled() {
- skinr_cache_reset();
- }
- /**
- * Implements hook_modules_disabled().
- */
- function skinr_modules_disabled() {
- skinr_cache_reset();
- }
- /**
- * Implements hook_themes_enabled().
- */
- function skinr_themes_enabled() {
- skinr_cache_reset();
- }
- /**
- * Implements hook_themes_disabled().
- */
- function skinr_themes_disabled() {
- skinr_cache_reset();
- }
- /**
- * Helper function for built-in integration code.
- */
- function skinr_skinr_api_modules() {
- return array(
- 'path' => drupal_get_path('module', 'skinr') . '/modules',
- );
- }
- function block_skinr_api_2() {
- return skinr_skinr_api_modules();
- }
- function comment_skinr_api_2() {
- return skinr_skinr_api_modules();
- }
- function node_skinr_api_2() {
- return skinr_skinr_api_modules();
- }
- function views_skinr_api_2() {
- return skinr_skinr_api_modules();
- }
|