You are here

path_metatags.module in Path metatags 7

Provide core functions for path metatags module.

File

path_metatags.module
View source
<?php

/**
 * @file
 * Provide core functions for path metatags module.
 */

/**
 * Implements hook_html_head_alter().
 */
function path_metatags_html_head_alter(&$head_elements) {

  // See if current page has metatags.
  $metatags = path_metatags_load_variant(current_path());
  if ($metatags) {

    // Build metatags.
    foreach ($metatags as $metatag => $metatag_data) {
      if ($metatag != 'title') {

        // Apply metatags as head element.
        $head_meta[$metatag] = array(
          '#type' => 'html_tag',
          '#tag' => 'meta',
          '#attributes' => array(
            $metatag_data['group'] => $metatag,
            'content' => $metatag_data['content'],
          ),
        );
        $build_meta[$metatag] = $metatag_data['group'];
      }
    }

    // Search and remove duplicates in meta tags.
    foreach ($head_elements as $name => $element) {
      if (!isset($element['#tag']) || $element['#tag'] != 'meta') {
        continue;
      }

      // Unset head element by its name.
      if (isset($element['#name']) && isset($build_meta[$element['#name']])) {
        unset($head_elements[$name]);
      }

      // Search for duplicates in element attributes.
      if (isset($element['#attributes'])) {
        foreach ($element['#attributes'] as $group => $value) {
          if (!is_array($value) && !empty($build_meta[$value]) && $build_meta[$value] == $group) {
            unset($head_elements[$name]);
          }
        }
      }
    }
    if (!empty($head_meta)) {

      // Add chance for other modules to alter header.
      drupal_alter('path_metatags_view', $head_meta);

      // Add meta information to site header.
      $head_elements += $head_meta;
    }
  }
}

/**
 * Implements hook_preprocess_html().
 */
function path_metatags_preprocess_html(&$vars) {
  $metatags = path_metatags_load_variant(current_path());
  if ($metatags && !empty($metatags['title']['content'])) {
    $vars['head_title'] = check_plain($metatags['title']['content']);
  }
}

/**
 * Implements hook_ctools_plugin_directory().
 */
function path_metatags_ctools_plugin_directory($owner, $plugin_type) {
  if ($owner == 'ctools') {
    return 'plugins/' . $plugin_type;
  }
}

/**
 * Load metatags based on path and visibility settings.
 *
 * @param $path
 * @return array|bool
 */
function path_metatags_load_variant($path) {
  if (!$path) {
    return FALSE;
  }

  // Select all variants matching current path.
  $variants = path_metatags_load_by_path($path);

  // Check if current path maches variant.
  // When first variant is found - return it.
  foreach ($variants as $metatags) {

    // Replace placeholder in path with '*'.
    // Example: 'node/%node/view' -> 'node/*/view'.
    $matched_path = preg_replace("\n        /\\/%    # start with slash-percent\n        [^\\/]+  # all symbols except for the slash\n        /x", '/*', $metatags->path);
    if (drupal_match_path($path, $matched_path)) {

      // Load metatags' contexts from current path.
      $contexts = path_metatags_get_contexts_from_arguments($metatags->arguments);

      // If metatags contains broken context
      // it means that unable to load context from URL.
      if (isset($contexts['broken_context'])) {
        continue;
      }

      // Check if metatags is accessable.
      if (!empty($metatags->access)) {
        $access = ctools_access($metatags->access, $contexts);
        if (!$access) {
          continue;
        }
      }

      // Build suitable metatags variant.
      return _path_metatags_build_metatags($metatags, $contexts);
    }
  }
  return FALSE;
}

/**
 *
 * Build metatags navigation from loaded path metatags variant.
 *
 * @param  $path_metatags
 *   Object with path metatags variant loaded from database.
 * @param $contexts
 *   Ctools contexts from current URL.
 * @return array
 *   Array with metatags.
 */
function _path_metatags_build_metatags($path_metatags, $contexts = array()) {

  // Add hook_path_metatags_view() for other developers.
  module_invoke_all('path_metatags_view', $path_metatags, $contexts);

  // Add custom ctools context for site.
  $site = new ctools_context('site');
  $site->plugin = 'site';
  $site->keyword = 'site';
  $contexts['site'] = $site;

  // Convert arguments from url to contexts.
  if (!empty($contexts) && !empty($path_metatags->metatags_values)) {
    foreach ($path_metatags->metatags_values as $key => $value) {

      // Replace placeholders by current context values.
      $path_metatags->metatags_values[$key] = ctools_context_keyword_substitute($value, array(), $contexts);
    }
  }
  $build = array();
  $metatags = path_metatags_load_metatags_list();
  if (!empty($path_metatags->metatags)) {
    foreach ($path_metatags->metatags as $i => $metatag) {

      // Check if current metatag exists in list of metatags.
      if (!empty($metatags[$metatag])) {

        // Display metatag only if it has value.
        if (!empty($path_metatags->metatags_values[$i])) {
          $metatag_value = $path_metatags->metatags_values[$i];

          // Remove all HTML tags if needed.
          $remove_tags = variable_get('path_metatags_skip_tags', 1);
          if ($remove_tags) {
            $metatag_value = trim(strip_tags($metatag_value));
          }

          // If metatag value still exists add it to build array.
          if (!empty($metatag_value)) {

            // Information about metatag from hook_path_metatag_info().
            $metatag_info = $metatags[$metatag];

            // Build output with metatag information about current page.
            $build[$metatag] = array(
              'group' => $metatag_info['group'],
              'content' => $metatag_value,
            );
          }
        }
      }
    }
  }
  return $build;
}

/**
 * Load list of metatags.
 *
 * @return array|mixed
 */
function path_metatags_load_metatags_list() {
  $metatags = array();
  foreach (module_implements('path_metatags_info') as $module) {
    $metatags += call_user_func($module . '_path_metatags_info');
  }

  // Last chance for modules to alter metatags list.
  drupal_alter('path_metatags_info', $metatags);
  return $metatags;
}

/**
 * Save path metatags.
 *
 * @param  $path_metatags
 *   Object with path metatags data.
 * @return int
 *   ID of inserted/updated path metatags.
 */
function path_metatags_save($path_metatags) {
  ctools_include('export');

  // Build array with full access data.
  if (!empty($path_metatags->access) && !empty($path_metatags->logic)) {
    $path_metatags->access['logic'] = $path_metatags->logic;
  }
  elseif (empty($path_metatags->access)) {
    $path_metatags->access = array();
  }

  // Ctools will serialize data itself.
  $path_metatags->data = array(
    'metatags' => $path_metatags->metatags,
    'metatags_values' => $path_metatags->metatags_values,
    'translatable' => $path_metatags->translatable,
    'arguments' => $path_metatags->arguments,
    'access' => $path_metatags->access,
  );
  $save_result = ctools_export_crud_save('path_metatags', $path_metatags);

  // FALSE means error while saving.
  if ($save_result) {

    // Remove data from ctools object cache table.
    path_metatags_object_cache_clear($path_metatags->machine_name);

    // Allow modules to know that path_metatags were saved.
    $path_metatags->is_new = $save_result == SAVED_NEW;
    module_invoke_all('path_metatags_save', $path_metatags);
    cache_clear_all('path_metatags', 'cache', TRUE);
  }

  // Return saving result, SAVED_NEW or SAVED_UPDATED.
  return $save_result;
}

/**
 * Delete path metatag.
 *
 * @param  $name
 *   Path metatag's name.
 * @return void
 */
function path_metatags_delete($name) {
  $path_metatags = path_metatags_load_by_name($name);

  // Inform modules about deleting path_metatags.
  module_invoke_all('path_metatags_delete', $path_metatags);

  // Call ctools functions to remove object correctly.
  ctools_export_crud_delete('path_metatags', $path_metatags);
  path_metatags_object_cache_clear($name);
  cache_clear_all('path_metatags', 'cache', TRUE);
}

/**
 * Prepare raw object from Ctools to normal path_metatags object.
 *
 * @param $path_metatags_raw
 *    Object loaded from database or ctools_export_load_object().
 * @return object $path_metatags.
 */
function path_metatags_load_prepare($path_metatags_raw) {

  // Merge metatags data with parent for more flattening structure.
  $path_metatags = (object) array_merge((array) $path_metatags_raw, $path_metatags_raw->data);
  $path_metatags->disabled = isset($path_metatags->disabled) ? $path_metatags->disabled : FALSE;
  $path_metatags->is_overwritten = $path_metatags->export_type == (EXPORT_IN_DATABASE | EXPORT_IN_CODE);
  unset($path_metatags->data);
  return $path_metatags;
}

/**
 * Load path metatags by ID.
 *
 * @param  $path_id
 *   ID of path metatags that should be loaded
 * @return object
 *   Loaded path metatags
 */
function path_metatags_load($path_id) {

  // Cache it because Ctools cache is not helpful for 'conditions' loading.
  $paths =& drupal_static(__FUNCTION__);
  if (!isset($paths[$path_id])) {
    ctools_include('export');
    $result = ctools_export_load_object('path_metatags', 'conditions', array(
      'path_id' => $path_id,
    ));
    $path_metatags = reset($result);
    if (!empty($path_metatags)) {

      // Merge metatags data with parent for more flattening structure.
      $path_metatags = path_metatags_load_prepare($path_metatags);
      $paths[$path_id] = $path_metatags;
    }
  }
  return isset($paths[$path_id]) ? $paths[$path_id] : FALSE;
}

/**
 * Load path metatags by name.
 *
 * @param  $name
 *   Path metatags's name.
 * @return object
 *   Object with path metatags.
 */
function path_metatags_load_by_name($name) {
  ctools_include('export');
  $result = ctools_export_load_object('path_metatags', 'names', array(
    $name,
  ));
  if (!empty($result[$name])) {

    // Merge metatags data with parent for more flattening structure.
    $path_metatags = path_metatags_load_prepare($result[$name]);
  }
  return isset($path_metatags) ? $path_metatags : FALSE;
}

/**
 * Load multiple objects by names.
 *
 * @param $names
 * @return array
 */
function path_metatags_load_by_name_multiple($names) {
  ctools_include('export');
  $result = ctools_export_load_object('path_metatags', 'names', $names);

  // Merge metatags data with parent for more flattening structure.
  foreach ($result as $name => $path_metatags) {
    $result[$name] = path_metatags_load_prepare($path_metatags);
  }
  return $result;
}

/**
 * Load all path metatags from database and code.
 *
 * @return array
 *    Array of path_metatags objects.
 */
function path_metatags_load_all() {
  $data =& drupal_static(__FUNCTION__);
  if (empty($data)) {
    ctools_include('export');
    $data = ctools_export_load_object('path_metatags', 'all');

    // Make objects more developer-friendly.
    $data = array_map('path_metatags_load_prepare', $data);

    // Order by weight.
    uasort($data, '_path_metatags_sort_weight');
  }
  return $data;
}

/**
 * Load enabled path_metatags by path.
 *
 * @param string $path
 *    Current page path.
 * @return array
 *    Array of path_metatags sorted by weight.
 */
function path_metatags_load_by_path($path) {
  $data =& drupal_static(__FUNCTION__);
  $pattern_needle = path_metatags_path_pattern($path);
  if (empty($data[$pattern_needle])) {
    $cache = cache_get(__FUNCTION__);
    if (!empty($cache)) {
      $data = $cache->data;
    }
    else {

      // Do heavy work and cache results.
      ctools_include('export');

      // No need to sort variants by weight because path_metatags_load_all()
      // already sorted all data.
      $result = path_metatags_load_all();
      foreach ($result as $path_metatags) {
        if (empty($path_metatags->disabled)) {
          $pattern = path_metatags_path_pattern($path_metatags->path);
          $data[$pattern][] = $path_metatags;
        }
      }
      cache_set(__FUNCTION__, $data, 'cache');
    }
  }
  return isset($data[$pattern_needle]) ? $data[$pattern_needle] : array();
}

/**
 * Load ctools contexts from path arguments.
 *
 * @param $arguments
 *   URL arguments.
 * @param bool $empty
 *   Define load context for empty arguments or not
 * @return array
 *   Array with context plugins.
 */
function path_metatags_get_contexts_from_arguments($arguments, $empty = FALSE) {
  $contexts = array();

  // Include ctools library for contexts.
  ctools_include('context');
  if (!empty($arguments)) {

    // Get contexts from arguments.
    foreach ($arguments as $keyword => $arg) {
      if (!empty($arg['argument'])) {
        $argument = ctools_get_argument($arg['argument']);
        if (isset($arg['settings'])) {
          $argument = array_merge($argument, $arg['settings']);
        }

        // See what we should return: empty contexts or from path arguments.
        $arg = $empty ? NULL : arg($arg['position']);

        // Build context.
        $context = call_user_func($argument['context'], $arg, $argument, $empty);
        if (!empty($context)) {
          $context->keyword = $keyword;
          $context->identifier = $argument['identifier'];
          $contexts[$keyword] = $context;
        }
        else {
          $contexts['broken_context'] = $keyword;
        }
      }
    }
  }
  return $contexts;
}

/**
 * Get path metatags data from cache.
 *
 * @param  $name
 *   Machine name of path metatags that should be loaded.
 * @param bool $skip_cache
 *   Skip current cache or not.
 * @return object
 *   Return cached object.
 */
function path_metatags_object_cache_get($name, $skip_cache = FALSE) {
  ctools_include('object-cache');
  return ctools_object_cache_get('path_metatags', $name, $skip_cache);
}

/**
 * Cache path metatags data.
 *
 * @param  $name
 *   Machine name of path metatags.
 * @param  $data
 *   Data to store.
 * @return void
 */
function path_metatags_object_cache_set($name, $data) {
  ctools_include('object-cache');
  $data = (object) $data;
  ctools_object_cache_set('path_metatags', $name, $data);
}

/**
 * Clear ctools object cache.
 *
 * @param $name
 *   Path metatags name.
 */
function path_metatags_object_cache_clear($name) {
  ctools_include('object-cache');
  ctools_object_cache_clear('path_metatags', $name);
}

/**
 * Create sql pattern from url.
 * Replaces all path arguments except the 1st one with %-symbol.
 *
 * @param string $path
 * @return string pattern
 */
function path_metatags_path_pattern($path) {
  $cache =& drupal_static(__FUNCTION__);
  if (empty($cache[$path])) {

    // Example: 'node/%node/view' -> 'node/%/%.
    $cache[$path] = preg_replace("\n            /\\/     # start with slash\n            [^\\/]+  # all symbols except for the slash\n            /x", '/%', $path);
  }
  return $cache[$path];
}

/**
 * This is version of drupal_sort_weight() for objects.
 * Function used by uasort to sort array of objects by weight.
 *
 * @param $a
 * @param $b
 * @return int
 */
function _path_metatags_sort_weight($a, $b) {
  $a_weight = isset($a->weight) ? $a->weight : 0;
  $b_weight = isset($b->weight) ? $b->weight : 0;
  if ($a_weight == $b_weight) {
    return 0;
  }
  return $a_weight < $b_weight ? -1 : 1;
}

/**
 * Ctools export callback.
 *
 * @param $path_metatags
 * @param $indent
 * @return string
 */
function path_metatags_export($path_metatags, $indent) {

  // Structure object according to schema.
  $path_metatags->data = array(
    'metatags' => $path_metatags->metatags,
    'metatags_values' => $path_metatags->metatags_values,
    'translatable' => $path_metatags->translatable,
    'arguments' => $path_metatags->arguments,
    'access' => $path_metatags->access,
  );
  return ctools_export_object('path_metatags', $path_metatags, $indent);
}

/**
 * Ctools list callback of all avalible path_metatags objects.
 *
 * @return array
 */
function path_metatags_export_list() {
  $list = array();
  $result = path_metatags_load_all();
  foreach ($result as $path_metatags) {
    $string = $path_metatags->name . " (" . $path_metatags->machine_name . ")";
    $list[$path_metatags->machine_name] = check_plain($string);
  }
  return $list;
}

/**
 * Implements hook_path_metatags_info().
 */
function path_metatags_ui_path_metatags_info() {
  $metatags['author'] = array(
    'group' => 'name',
  );
  $metatags['copyright'] = array(
    'group' => 'name',
  );
  $metatags['description'] = array(
    'group' => 'name',
  );
  $metatags['document-state'] = array(
    'group' => 'name',
  );
  $metatags['generator'] = array(
    'group' => 'name',
  );
  $metatags['keywords'] = array(
    'group' => 'name',
  );
  $metatags['resource-type'] = array(
    'group' => 'name',
  );
  $metatags['revisit'] = array(
    'group' => 'name',
  );
  $metatags['robots'] = array(
    'group' => 'name',
  );
  $metatags['url'] = array(
    'group' => 'name',
  );
  $metatags['Content-Script-Type'] = array(
    'group' => 'http-equiv',
  );
  $metatags['Content-Style-Type'] = array(
    'group' => 'http-equiv',
  );
  $metatags['Content-Type'] = array(
    'group' => 'http-equiv',
  );
  $metatags['Expires'] = array(
    'group' => 'http-equiv',
  );
  $metatags['Pragma'] = array(
    'group' => 'http-equiv',
  );
  $metatags['title'] = array(
    'group' => 'title',
  );
  $metatags['refresh'] = array(
    'group' => 'http-equiv',
  );
  $metatags['imagetoolbar'] = array(
    'group' => 'http-equiv',
  );
  return $metatags;
}

Functions

Namesort descending Description
path_metatags_ctools_plugin_directory Implements hook_ctools_plugin_directory().
path_metatags_delete Delete path metatag.
path_metatags_export Ctools export callback.
path_metatags_export_list Ctools list callback of all avalible path_metatags objects.
path_metatags_get_contexts_from_arguments Load ctools contexts from path arguments.
path_metatags_html_head_alter Implements hook_html_head_alter().
path_metatags_load Load path metatags by ID.
path_metatags_load_all Load all path metatags from database and code.
path_metatags_load_by_name Load path metatags by name.
path_metatags_load_by_name_multiple Load multiple objects by names.
path_metatags_load_by_path Load enabled path_metatags by path.
path_metatags_load_metatags_list Load list of metatags.
path_metatags_load_prepare Prepare raw object from Ctools to normal path_metatags object.
path_metatags_load_variant Load metatags based on path and visibility settings.
path_metatags_object_cache_clear Clear ctools object cache.
path_metatags_object_cache_get Get path metatags data from cache.
path_metatags_object_cache_set Cache path metatags data.
path_metatags_path_pattern Create sql pattern from url. Replaces all path arguments except the 1st one with %-symbol.
path_metatags_preprocess_html Implements hook_preprocess_html().
path_metatags_save Save path metatags.
path_metatags_ui_path_metatags_info Implements hook_path_metatags_info().
_path_metatags_build_metatags Build metatags navigation from loaded path metatags variant.
_path_metatags_sort_weight This is version of drupal_sort_weight() for objects. Function used by uasort to sort array of objects by weight.