simplemeta.module in Simple Meta 7
Same filename and directory in other branches
SimpleMeta module.
File
simplemeta.moduleView source
<?php
/**
* @file
* SimpleMeta module.
*/
/**
* Implements hook_init().
*/
function simplemeta_init() {
if (variable_get('simplemeta_form_enable', TRUE) && user_access('administer simplemeta') && !path_is_admin(current_path())) {
$modulepath = drupal_get_path('module', 'simplemeta');
drupal_add_css($modulepath . '/css/simplemeta.css', array(
'preprocess' => FALSE,
));
drupal_add_js($modulepath . '/js/simplemeta.js', array(
'preprocess' => FALSE,
));
}
}
/**
* Implements hook_permission().
*/
function simplemeta_permission() {
return array(
'administer simplemeta' => array(
'title' => t('Administer SimpleMeta'),
),
);
}
/**
* Implements hook_menu().
*/
function simplemeta_menu() {
$items = array();
$items['admin/content/simplemeta'] = array(
'title' => 'SimpleMeta',
'page callback' => 'simplemeta_meta_list',
'access arguments' => array(
'administer simplemeta',
),
'file' => 'simplemeta.admin.inc',
);
$items['admin/content/simplemeta/list'] = array(
'title' => 'SimpleMeta List',
'page callback' => 'simplemeta_meta_list',
'access arguments' => array(
'administer simplemeta',
),
'type' => MENU_DEFAULT_LOCAL_TASK,
'file' => 'simplemeta.admin.inc',
'weight' => 0,
);
$items['admin/content/simplemeta/add'] = array(
'title' => 'Add SimpleMeta',
'page callback' => 'simplemeta_add',
'page arguments' => array(),
'access arguments' => array(
'administer simplemeta',
),
'type' => MENU_LOCAL_TASK,
'file' => 'simplemeta.admin.inc',
'weight' => 1,
);
$items['admin/content/simplemeta/%simplemeta_meta/edit'] = array(
'title' => 'Add SimpleMeta',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'simplemeta_meta_form',
3,
),
'access arguments' => array(
'administer simplemeta',
),
'type' => MENU_CALLBACK,
);
$items['admin/content/simplemeta/%simplemeta_meta/delete'] = array(
'title' => 'Delete SimpleMeta',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'simplemeta_meta_delete_confirm',
3,
),
'access arguments' => array(
'administer simplemeta',
),
'type' => MENU_CALLBACK,
'file' => 'simplemeta.admin.inc',
);
$items['admin/content/simplemeta/settings'] = array(
'title' => 'Settings',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'simplemeta_settings_form',
),
'access arguments' => array(
'administer simplemeta',
),
'file' => 'simplemeta.admin.inc',
);
return $items;
}
/**
* Implements hook_page_alter().
*/
function simplemeta_page_alter(&$page) {
global $language;
if (variable_get('simplemeta_form_enable', TRUE) && user_access('administer simplemeta') && !path_is_admin(current_path())) {
$path = $_GET['q'];
$lang = $language->language;
$meta = simplemeta_meta_load_by_path($path, $lang);
// Try to load language-neutral
if (!$meta) {
$meta = simplemeta_meta_load_by_path($path);
}
if (!$meta) {
$meta = new stdClass();
$meta->path = $path;
$meta->data = array();
$meta->language = '';
}
$form = drupal_get_form('simplemeta_page_meta_form', $meta);
$page['page_bottom']['simplemeta'] = array(
'#markup' => drupal_render($form),
);
}
}
/**
* Implements hook_flush_caches().
*/
function simplemeta_flush_caches() {
return array(
'cache_simplemeta',
);
}
/**
* Implements hook_theme().
*/
function simplemeta_theme() {
return array(
'simplemeta_meta_list' => array(
'render element' => 'items',
'file' => 'simplemeta.theme.inc',
),
'simplemeta_meta_title' => array(
'render element' => 'meta',
'file' => 'simplemeta.theme.inc',
),
'simplemeta_meta_description' => array(
'render element' => 'meta',
'file' => 'simplemeta.theme.inc',
),
'simplemeta_meta_keywords' => array(
'render element' => 'meta',
'file' => 'simplemeta.theme.inc',
),
);
}
/**
* Implements $module_preprocess_$hook().
*/
function simplemeta_preprocess_html(&$vars) {
global $language;
if ($meta = simplemeta_get_page_meta($_GET['q'], $language->language)) {
// Set page title.
if (!empty($meta->data['title'])) {
if (module_exists('token')) {
$meta->data['title'] = token_replace($meta->data['title']);
}
$vars['head_title'] = check_plain($meta->data['title']);
}
foreach ($meta->view as $key => $item) {
if (module_exists('token')) {
$item = token_replace($item);
}
$head_item = array(
'#type' => 'markup',
'#markup' => $item,
);
drupal_add_html_head($head_item, 'simplemeta_' . $key);
}
}
}
/**
* Implements hook_simplemeta_info().
*/
function simplemeta_simplemeta_info() {
$info = array();
$info['title'] = array(
'title' => t('Title'),
'form' => 'simplemeta_form_title',
'theme' => 'simplemeta_meta_title',
);
$info['description'] = array(
'title' => t('Description'),
'form' => 'simplemeta_form_description',
'theme' => 'simplemeta_meta_description',
);
$info['keywords'] = array(
'title' => t('Keywords'),
'form' => 'simplemeta_form_keywords',
'theme' => 'simplemeta_meta_keywords',
);
return $info;
}
/**
* Meta title form element callback.
*
* @param object $meta
* meta object
*
* @return array
* form element
*/
function simplemeta_form_title($meta) {
$form = array(
'#type' => 'textfield',
'#title' => t('Title'),
'#maxlength' => 255,
'#default_value' => isset($meta->data['title']) ? $meta->data['title'] : '',
);
return $form;
}
/**
* Meta description form element callback.
*
* @param object $meta
* meta data
*
* @return array
* form element
*/
function simplemeta_form_description($meta) {
$form = array(
'#type' => 'textarea',
'#title' => t('Description'),
'#default_value' => isset($meta->data['description']) ? $meta->data['description'] : '',
);
return $form;
}
/**
* Meta keywords form element callback.
*
* @param object $meta
* meta object
*
* @return array
* form element
*/
function simplemeta_form_keywords($meta) {
$form = array(
'#type' => 'textfield',
'#title' => t('Keywords'),
'#maxlength' => 255,
'#default_value' => isset($meta->data['keywords']) ? $meta->data['keywords'] : '',
);
return $form;
}
/**
* Save meta data.
*
* @param object $meta
* meta data
*/
function simplemeta_meta_save($meta) {
$is_new = !isset($meta->sid) && !simplemeta_meta_load_by_path($meta->path, $meta->language);
$meta->fit = _simplemeta_meta_calculate_fit($meta->path);
$record = clone $meta;
$record->data = serialize($record->data);
if ($is_new) {
$result = drupal_write_record('simplemeta', $record);
if (!empty($record->sid)) {
$meta->sid = $record->sid;
}
module_invoke_all('simplemeta', $meta, 'insert');
}
else {
$result = drupal_write_record('simplemeta', $record, array(
'sid',
));
module_invoke_all('simplemeta', $meta, 'update');
}
return $result;
}
/**
* Load meta data.
*
* @param int $sid
* SimpleMeta data id
*
* @return object|FALSE
* object representing metadata or FALSE on failure
*/
function simplemeta_meta_load($sid) {
$result = db_select('simplemeta', 's')
->fields('s')
->condition('s.sid', $sid, '=')
->execute();
if ($meta = $result
->fetchObject()) {
$meta->data = unserialize($meta->data);
return $meta;
}
return FALSE;
}
/**
* Load meta data by path.
*
* @param string $path
* page's path to fetch meta data
*
* @param string $language
* language code
*
* @return object|FALSE
* object representing metadata or FALSE on failure
*/
function simplemeta_meta_load_by_path($path, $language = '') {
$result = db_select('simplemeta', 's')
->fields('s')
->condition('s.path', $path, '=')
->condition('s.language', $language, '=')
->execute();
if ($meta = $result
->fetchObject()) {
$meta->data = unserialize($meta->data);
return $meta;
}
return FALSE;
}
/**
* Get page's meta data.
*
* @staticvar array $meta
*
* @param string $path
* page's path to fetch meta data
*
* @param string $language
* language code
*
* @param bool $reset
* whether to reset static cache
*
* @return object|FALSE
* object representing metadata or FALSE on failure
*/
function simplemeta_get_page_meta($path = NULL, $language = '', $reset = FALSE) {
static $meta = array();
if (!isset($path)) {
$path = $_GET['q'];
}
if (!isset($meta[$path]) || $reset) {
$meta[$path] = FALSE;
$cid = $path . ':' . $language;
if ($cache = cache_get($cid, 'cache_simplemeta')) {
$meta[$path] = $cache->data;
}
else {
$row = NULL;
$original_map = arg(NULL, $path);
$parts = array_slice($original_map, 0, MENU_MAX_PARTS);
$ancestors = simplemeta_path_get_ancestors($parts);
array_unshift($ancestors, $path);
$query = db_select('simplemeta', 's')
->fields('s')
->condition('s.path', $ancestors, 'IN')
->condition('s.language', $language, '=')
->orderBy('s.fit', 'DESC')
->range(0, 1);
$row = $query
->execute()
->fetchObject();
// If there is no language-specific meta, try to load language-neutral.
if (!$row && $language) {
$query = db_select('simplemeta', 's')
->fields('s')
->condition('s.path', $ancestors, 'IN')
->condition('s.language', '', '=')
->orderBy('s.fit', 'DESC')
->range(0, 1);
$row = $query
->execute()
->fetchObject();
}
if ($row) {
$row->data = unserialize($row->data);
$row->view = array();
$info = simplemeta_get_info();
foreach ($info as $key => $definition) {
$themed = theme($definition['theme'], array(
'meta' => $row,
));
if ($themed) {
$row->view[$key] = $themed;
}
}
cache_set($cid, $row, 'cache_simplemeta');
$meta[$path] = $row;
}
}
}
return $meta[$path];
}
/**
* Delete meta data.
*
* @param int $sid
* SimpleMeta id
*/
function simplemeta_meta_delete($sid) {
$meta = simplemeta_meta_load($sid);
if ($meta) {
db_delete('simplemeta')
->condition('sid', $sid, '=')
->execute();
module_invoke_all('simplemeta', $meta, 'delete');
}
}
/**
* SimpleMeta form builder.
*/
function simplemeta_meta_form($form, &$form_state, $meta) {
// Let's use _ as prefix to not conflict with other elements.
$form['_meta'] = array(
'#type' => 'value',
'#value' => $meta,
);
if (!isset($meta->path)) {
$form['_path'] = array(
'#type' => 'textfield',
'#title' => t('Path'),
'#description' => t('% may be used as placeholder for system pathes, for example, news/archive/%'),
'#required' => TRUE,
);
}
else {
$form['_path'] = array(
'#type' => 'value',
'#value' => $meta->path,
);
}
if (variable_get('simplemeta_language_enable', FALSE)) {
$form['_language'] = array(
'#type' => 'select',
'#title' => t('Language'),
'#options' => _simplemeta_langauge_list(),
'#default_value' => $meta->language,
);
// Do not allow change language for existing meta.
if (isset($meta->sid)) {
$form['_language']['#disabled'] = TRUE;
$form['_language']['#value'] = $meta->language;
}
}
else {
$form['_language'] = array(
'#type' => 'value',
'#value' => $meta->language,
);
}
$form += simplemeta_get_form_elements($meta);
$form['_buttons'] = array(
'#type' => 'actions',
);
$form['_buttons']['save'] = array(
'#type' => 'submit',
'#value' => t('Save'),
'#validate' => array(
'simplemeta_meta_form_validate_save',
),
'#submit' => array(
'simplemeta_meta_form_submit_save',
),
'#attributes' => array(
'class' => array(
'button-save',
),
),
);
if (!empty($meta->sid)) {
$form['_buttons']['delete'] = array(
'#type' => 'link',
'#title' => t('Delete'),
'#href' => 'admin/content/simplemeta/' . $meta->sid . '/delete',
);
}
return $form;
}
/**
* SimpleMeta form validation callback on save.
*/
function simplemeta_meta_form_validate_save($form, &$form_state) {
$values = $form_state['values'];
$meta = $values['_meta'];
$path = $values['_path'];
$lang = $values['_language'];
$normal_path = drupal_get_normal_path($path);
if ($path != $normal_path) {
$path = $normal_path;
}
if (!url_is_external($path)) {
$parsed_link = parse_url($path);
if ($path != $parsed_link['path']) {
$path = $parsed_link['path'];
}
// @todo do we need to check the access?
// @see menu_edit_item_validate()
if (!trim($path)) {
form_set_error('_path', t('Path is invalid'));
}
$form_state['values']['_path'] = $path;
}
else {
form_set_error('_path', t('Path can be only internal'));
}
if (isset($meta->sid) && !simplemeta_meta_load($meta->sid)) {
form_set_error('_meta', t("Meta #%sid doesn't exist anymore", array(
'%id' => $meta->sid,
)));
}
elseif (!isset($meta->sid) && simplemeta_meta_load_by_path($path, $lang)) {
form_set_error('_meta', t("Meta for this page in this language already exists"));
}
$info = simplemeta_get_info();
foreach ($info as $key => $definition) {
if (isset($definition['validate']) && function_exists($definition['validate'])) {
$function = $definition['validate'];
$function($form, $form_state);
}
}
}
/**
* SimpleMeta form submit callback on save.
*/
function simplemeta_meta_form_submit_save($form, &$form_state) {
$values = $form_state['values'];
$meta = $values['_meta'];
$meta->path = $values['_path'];
$meta->language = $values['_language'];
$meta->data = array_intersect_key($values, simplemeta_get_form_elements());
$info = simplemeta_get_info();
foreach ($info as $key => $definition) {
if (isset($definition['submit']) && function_exists($definition['submit'])) {
$function = $definition['submit'];
// @todo should we pass the $form? Think about
$function($meta, $form_state);
}
}
simplemeta_meta_save($meta);
cache_clear_all('*', 'cache_simplemeta', TRUE);
drupal_set_message(t('Meta has been saved'));
$form_state['redirect'] = 'admin/content/simplemeta/list';
}
/**
* On-page SimpleMeta form builder.
*/
function simplemeta_page_meta_form($form, &$form_state, $meta) {
$form_id = 'simplemeta_meta_form';
$_form_state = $form_state;
$_form_state['build_info'] = array(
'args' => array(
$meta,
),
);
// Use simplemeta_meta_form as a base so alter hooks for simplemeta_meta_form will work here too.
// @todo do not use simplemeta_meta_form as a base, but hook_form_alter() (and its more specific version) will need to alter both forms (in case changes need to appear on both).
$form = drupal_retrieve_form($form_id, $_form_state);
drupal_prepare_form($form_id, $form, $_form_state);
if (variable_get('simplemeta_language_enable', FALSE)) {
$form['_language']['#disabled'] = FALSE;
unset($form['_language']['#value']);
$form['_language']['#ajax'] = array(
'wrapper' => 'simplemeta-meta-form-ajax-wrapper',
'callback' => 'simplemeta_page_meta_form_ajax_language',
);
}
$form['_buttons']['save']['#submit'][] = 'simplemeta_page_meta_form_submit_save';
if (!empty($meta->sid)) {
$form['_buttons']['delete'] = array(
'#type' => 'submit',
'#value' => t('Delete'),
'#attributes' => array(
'class' => array(
'button-delete',
),
),
'#submit' => array(
'simplemeta_page_meta_form_submit_delete',
),
);
}
$form['#prefix'] = '<div id="simplemeta-meta-form-ajax-wrapper">';
$form['#suffix'] = '</div>';
$form['#attributes']['class'] = array(
'simplemeta-meta-form-ajax',
);
// Use the same id attribute for both admin and on-page forms.
$form['#id'] = 'simplemeta-meta-form';
return $form;
}
/**
* On-page SimpleMeta form submit callback on save.
*/
function simplemeta_page_meta_form_submit_save($form, &$form_state) {
unset($form_state['redirect']);
}
/**
* On-page SimpleMeta form submit callback on delete.
*/
function simplemeta_page_meta_form_submit_delete($form, &$form_state) {
simplemeta_meta_delete($form_state['values']['_meta']->sid);
cache_clear_all('*', 'cache_simplemeta', TRUE);
drupal_set_message(t('Meta has been deleted'));
}
/**
* Get info about meta elements from modules.
*
* Basically, invokes all implementations of hook_simplemeta_info().
* Caches info in the {cache} table.
*
* @param bool $reset
* indicates whether use cache or get info from implementations directly
*
* @return array
* info
*/
function simplemeta_get_info($reset = FALSE) {
$cid = 'simplemeta:info';
if (!$reset && ($cache = cache_get($cid, 'cache'))) {
return $cache->data;
}
$info = array();
foreach (module_implements('simplemeta_info') as $module) {
$function = $module . '_simplemeta_info';
$result = $function();
$info = array_merge($info, $result);
}
cache_set($cid, $info, 'cache');
return $info;
}
/**
* Get all implemented form elements for SimpleMeta form.
*/
function simplemeta_get_form_elements($meta = NULL, $reset = FALSE) {
$info = simplemeta_get_info($reset);
$form = array();
foreach ($info as $key => $definition) {
$function = $definition['form'];
if (function_exists($function)) {
$form[$key] = $function($meta);
}
}
return $form;
}
/**
* AJAX callback for language element of the simplemeta_page_meta_form.
*/
function simplemeta_page_meta_form_ajax_language($form, $form_state) {
$_meta = $form_state['values']['_meta'];
$path = $_meta->path;
$lang = $form_state['values']['_language'];
$meta = simplemeta_meta_load_by_path($path, $lang);
// Try to load language-neutral
if (!$meta) {
$meta = new stdClass();
$meta->path = $path;
$meta->data = array();
$meta->language = $lang;
}
$form_state = array(
'build_info' => array(
'args' => array(
$meta,
),
),
'rebuild_info' => array(
'copy' => array(
'#build_id' => TRUE,
'#action' => TRUE,
),
),
'values' => array(),
) + form_state_defaults();
return drupal_rebuild_form('simplemeta_page_meta_form', $form_state, $form);
}
/**
* Get path ancestors of the path (represented as parts) to find page's SimpleMeta configuration.
* @see menu_get_ancestors()
*/
function simplemeta_path_get_ancestors($parts) {
$number_parts = count($parts);
$ancestors = array();
$length = $number_parts - 1;
$end = (1 << $number_parts) - 1;
// menu_masks actually takes defined menu paths (via hook_menu() implementation) into account.
// @todo function probably should return list of all possible ancestors (but it's a bigger list actually).
$masks = variable_get('menu_masks');
// If the optimized menu_masks array is not available use brute force to get
// the correct $ancestors and $placeholders returned. Do not use this as the
// default value of the menu_masks variable to avoid building such a big
// array.
if (!$masks) {
$masks = range(511, 1);
}
// Only examine patterns that actually exist as router items (the masks).
foreach ($masks as $i) {
if ($i > $end) {
// Only look at masks that are not longer than the path of interest.
continue;
}
elseif ($i < 1 << $length) {
// We have exhausted the masks of a given length, so decrease the length.
--$length;
}
// Path patterns which have less parts than original path must end with %
// this also includes shorter paths without %
if ($length < $number_parts - 1 && $i & 1) {
continue;
}
$current = '';
for ($j = $length; $j >= 0; $j--) {
// Check the bit on the $j offset.
if ($i & 1 << $j) {
// Bit one means the original value.
$current .= $parts[$length - $j];
}
else {
// Bit zero means means wildcard.
$current .= '%';
}
// Unless we are at offset 0, add a slash.
if ($j) {
$current .= '/';
}
}
$ancestors[] = $current;
}
return $ancestors;
}
/**
* Calculate fit of the path i.e. how specific path is.
*
* @param string $path
* path to calculate fit
*
* @return int
* fit
*
* @see _menu_router_build()
*/
function _simplemeta_meta_calculate_fit($path) {
$fit = 0;
$parts = explode('/', $path, MENU_MAX_PARTS);
$number_parts = count($parts);
$slashes = $number_parts - 1;
foreach ($parts as $k => $part) {
if ($part != '%') {
$fit |= 1 << $slashes - $k;
}
}
return $fit;
}
/**
* Get options for languages.
*/
function _simplemeta_langauge_list() {
$languages = language_list();
$options = array();
foreach ($languages as $language) {
$options[$language->language] = $language->name;
}
$options = array(
'' => t('- Language neutral -'),
) + $options;
return $options;
}
Functions
Name | Description |
---|---|
simplemeta_flush_caches | Implements hook_flush_caches(). |
simplemeta_form_description | Meta description form element callback. |
simplemeta_form_keywords | Meta keywords form element callback. |
simplemeta_form_title | Meta title form element callback. |
simplemeta_get_form_elements | Get all implemented form elements for SimpleMeta form. |
simplemeta_get_info | Get info about meta elements from modules. |
simplemeta_get_page_meta | Get page's meta data. |
simplemeta_init | Implements hook_init(). |
simplemeta_menu | Implements hook_menu(). |
simplemeta_meta_delete | Delete meta data. |
simplemeta_meta_form | SimpleMeta form builder. |
simplemeta_meta_form_submit_save | SimpleMeta form submit callback on save. |
simplemeta_meta_form_validate_save | SimpleMeta form validation callback on save. |
simplemeta_meta_load | Load meta data. |
simplemeta_meta_load_by_path | Load meta data by path. |
simplemeta_meta_save | Save meta data. |
simplemeta_page_alter | Implements hook_page_alter(). |
simplemeta_page_meta_form | On-page SimpleMeta form builder. |
simplemeta_page_meta_form_ajax_language | AJAX callback for language element of the simplemeta_page_meta_form. |
simplemeta_page_meta_form_submit_delete | On-page SimpleMeta form submit callback on delete. |
simplemeta_page_meta_form_submit_save | On-page SimpleMeta form submit callback on save. |
simplemeta_path_get_ancestors | Get path ancestors of the path (represented as parts) to find page's SimpleMeta configuration. |
simplemeta_permission | Implements hook_permission(). |
simplemeta_preprocess_html | Implements $module_preprocess_$hook(). |
simplemeta_simplemeta_info | Implements hook_simplemeta_info(). |
simplemeta_theme | Implements hook_theme(). |
_simplemeta_langauge_list | Get options for languages. |
_simplemeta_meta_calculate_fit | Calculate fit of the path i.e. how specific path is. |