t('Meta tags'), 'description' => t('Meta tags fieldset.'), 'weight' => 10, ); return $extras; } /** * Implements hook_form_alter(). */ function nodewords_form_alter(&$form, &$form_state, $form_id) { $bool = ( isset($form['type']) && isset($form['#node']) && $form_id == $form['type']['#value'] . '_node_form' && variable_get('nodewords_edit_metatags_' . $form['type']['#value'], TRUE) ); if ($bool) { $node = $form['#node']; $form['nodewords'] = nodewords_form( NODEWORDS_TYPE_NODE, !empty($node->nodewords) ? $node->nodewords : array(), array( 'tag_options' => array('node_type' => $form['type']['#value']), ) ); } } /** * Implements hook_form_FORM_ID_alter(). */ function nodewords_form_node_type_form_alter(&$form, &$form_state) { if (isset($form['#node_type'])) { $form['nodewords'] = array( '#type' => 'fieldset', '#title' => t('Meta tags settings'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#group' => 'additional_settings', ); $form['nodewords']['nodewords_edit_metatags'] = array( '#type' => 'checkbox', '#title' => t('Allow editing of meta tags'), '#description' => t('If selected, the node edit form will allow the users with the right permissions to edit the meta tags associated with nodes of this content type.'), '#default_value' => variable_get('nodewords_edit_metatags_' . $form['#node_type']->type, TRUE), ); $form['nodewords']['metatags_generation'] = array( '#type' => 'fieldset', '#title' => t('Node meta tags generation options'), '#description' => t('These options change how a meta tag content is generated from the node content. These settings apply to specific meta tags.'), '#collapsible' => TRUE, ); $options = array( NODEWORDS_GENERATION_NEVER => t('Do not generate meta tags content'), NODEWORDS_GENERATION_WHEN_EMPTY => t('Generate meta tag content when the meta tag content is empty (default)'), NODEWORDS_GENERATION_ALWAYS => t('Always generate the meta tag content'), ); $form['nodewords']['metatags_generation']['nodewords_metatags_generation_method'] = array( '#type' => 'radios', '#options' => $options, '#default_value' => variable_get( 'nodewords_metatags_generation_method_' . $form['#node_type']->type, NODEWORDS_GENERATION_WHEN_EMPTY ), ); $options = array( NODEWORDS_GENERATION_BODY => t('Generate meta tags content from the node body'), NODEWORDS_GENERATION_TEASER => t('Generate meta tags content from the node teaser (default)'), NODEWORDS_GENERATION_TEASER_BODY => t('Generate meta tags content from the node teaser, or the node body when the node teaser is empty'), ); $form['nodewords']['metatags_generation']['nodewords_metatags_generation_source'] = array( '#type' => 'radios', '#title' => t('Generation source'), '#options' => $options, '#default_value' => variable_get( 'nodewords_metatags_generation_source_' . $form['#node_type']->type, NODEWORDS_GENERATION_TEASER ), ); $form['nodewords']['metatags_generation']['nodewords_use_alt_attribute'] = array( '#type' => 'checkbox', '#title' => t('Replace the tag IMG content with the attribute ALT'), '#default_value' => variable_get( 'nodewords_use_alt_attribute_' . $form['#node_type']->type, TRUE ), ); $options = array( 'imagebrowser' => 'imagebrowser.module', 'img_assist' => 'img_assist.module', ); $form['nodewords']['metatags_generation']['nodewords_filter_modules_output'] = array( '#type' => 'checkboxes', '#title' => t('Filter the text added by third-party modules in the node teaser'), '#options' => $options, '#default_value' => variable_get( 'nodewords_filter_modules_output_' . $form['#node_type']->type, array() ), '#checkall' => TRUE, ); $form['nodewords']['metatags_generation']['nodewords_filter_regexp'] = array( '#type' => 'textfield', '#title' => t('Custom regular expression'), '#description' => t('A regular expression used to filter the text added in the node teaser from a third-party module. The regular expression uses the Perl compatible syntax. Slashes are assumed as delimiters and all expressions are case-sensitive.'), '#element_validate' => array('nodewords_filter_regex_validate'), '#default_value' => variable_get( 'nodewords_filter_regexp_' . $form['#node_type']->type, '' ), '#field_prefix' => '/', '#field_suffix' => '/', '#size' => 60, ); foreach (nodewords_get_possible_tags() as $name => $info) { $function = $info['callback'] . '_settings_form'; $options = array(); if (function_exists($function)) { $function($form, 'node_type_form', $options); } } } } /** * Implements hook_form_FORM_ID_alter(). */ function nodewords_form_taxonomy_form_term_alter(&$form, &$form_state) { $bool = (isset($form['tid']['#value']) && !isset($form_state['confirm_delete']) && !isset($form_state['confirm_parents']) ); if ($bool) { $id = $form['tid']['#value']; if (!empty($form_state['values']['nodewords'])) { $tags = $form_state['values']['nodewords']; } elseif (isset($id) && is_numeric($id)) { $tags = nodewords_load_tags(NODEWORDS_TYPE_TERM, $id); } else { $tags = array(); } $form['nodewords'] = nodewords_form( NODEWORDS_TYPE_TERM, $tags ); // Ensure the submit & delete buttons are at the bottom. Hopefully. $form['submit']['#weight'] = 1000; $form['delete']['#weight'] = 1001; } } /** * Implements hook_form_FORM_ID_alter(). */ function nodewords_form_taxonomy_form_vocabulary_alter(&$form, &$form_state) { if (isset($form['vid']['#value'])) { $id = $form['vid']['#value']; if (!empty($form_state['values']['nodewords'])) { $tags = $form_state['values']['nodewords']; } elseif (isset($id) && is_numeric($id)) { $tags = nodewords_load_tags(NODEWORDS_TYPE_VOCABULARY, $id); } else { $tags = array(); } $form['nodewords'] = nodewords_form( NODEWORDS_TYPE_VOCABULARY, $tags ); // Ensure the submit & delete buttons are at the bottom. Hopefully. $form['submit']['#weight'] = 1000; $form['delete']['#weight'] = 1001; } } /** * Implemenation of hook_help(). */ function nodewords_help($path, $arg) { switch ($path) { case 'admin/content/nodewords/meta-tags': $output = '

' . t('On this page you can enter the default values for the meta tags of your site.') . '

'; break; case 'admin/content/nodewords/meta-tags/errorpage_403': $output = '

' . t('On this page you can enter the meta tags for the access denied error page of your site.') . '

'; break; case 'admin/content/nodewords/meta-tags/errorpage_404': $output = '

' . t('On this page you can enter the meta tags for the page not found error page of your site.') . '

'; break; case 'admin/content/nodewords/meta-tags/frontpage': $output = '

' . t('On this page you can enter the meta tags for the front page of your site.') . '

'; break; case 'admin/content/nodewords/meta-tags/custom': $output = '

' . t('On this page you can enter the meta tags for other pages of your site. The meta tags set in these page are used before the ones set for nodes or user profiles, and they can ovverride those meta tags.') . '

'; break; default: $output = ''; break; } return $output; } /** * Implements hook_menu(). */ function nodewords_menu() { $admin_access = array('administer meta tags'); $items = array(); $items['admin/content/nodewords'] = array( 'title' => 'Meta tags', 'page callback' => 'drupal_get_form', 'page arguments' => array('nodewords_settings_form'), 'description' => 'Configure HTML meta tags for all the content.', 'access arguments' => $admin_access, 'type' => MENU_NORMAL_ITEM, 'file' => 'nodewords.admin.inc', ); $items['admin/content/nodewords/settings'] = array( 'title' => 'Settings', 'access arguments' => $admin_access, 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, 'file' => 'nodewords.admin.inc', ); $items['admin/content/nodewords/meta-tags'] = array( 'title' => 'Default and specific meta tags', 'page callback' => 'drupal_get_form', 'page arguments' => array('nodewords_tags_form'), 'access arguments' => $admin_access, 'type' => MENU_LOCAL_TASK, 'file' => 'nodewords.admin.inc', 'weight' => -9, ); $items['admin/content/nodewords/meta-tags/default'] = array( 'title' => 'Default values', 'access arguments' => $admin_access, 'type' => MENU_DEFAULT_LOCAL_TASK, 'file' => 'nodewords.admin.inc', 'weight' => -10, ); $items['admin/content/nodewords/meta-tags/frontpage'] = array( 'title' => 'Front page', 'page callback' => 'drupal_get_form', 'page arguments' => array('nodewords_tags_form', (string) NODEWORDS_TYPE_FRONTPAGE), 'access arguments' => $admin_access, 'type' => MENU_LOCAL_TASK, 'file' => 'nodewords.admin.inc', 'weight' => -9, ); $items['admin/content/nodewords/meta-tags/offline'] = array( 'title' => 'Site off-line page', 'page callback' => 'drupal_get_form', 'page arguments' => array('nodewords_tags_form', (string) NODEWORDS_TYPE_OFFLINE), 'access arguments' => $admin_access, 'type' => MENU_LOCAL_TASK, 'file' => 'nodewords.admin.inc', 'weight' => -8, ); $items['admin/content/nodewords/meta-tags/errorpage_403'] = array( 'title' => 'Error 403 page', 'page callback' => 'drupal_get_form', 'page arguments' => array('nodewords_tags_form', (string) NODEWORDS_TYPE_ERRORPAGE, '403'), 'access arguments' => $admin_access, 'type' => MENU_LOCAL_TASK, 'file' => 'nodewords.admin.inc', 'weight' => -7, ); $items['admin/content/nodewords/meta-tags/errorpage_404'] = array( 'title' => 'Error 404 page', 'page callback' => 'drupal_get_form', 'page arguments' => array('nodewords_tags_form', (string) NODEWORDS_TYPE_ERRORPAGE, '404'), 'access arguments' => $admin_access, 'type' => MENU_LOCAL_TASK, 'file' => 'nodewords.admin.inc', 'weight' => -6, ); // Only show the forum settings page if the core Blog module is enabled. if (module_exists('blog')) { $items['admin/content/nodewords/meta-tags/blog'] = array( 'title' => 'Blog main page', 'page callback' => 'drupal_get_form', 'page arguments' => array('nodewords_tags_form', (string) NODEWORDS_TYPE_BLOG), 'access arguments' => $admin_access, 'type' => MENU_LOCAL_TASK, 'file' => 'nodewords.admin.inc', 'weight' => -5, ); } // Only show the forum settings page if the core Forum module is enabled. if (module_exists('forum')) { $items['admin/content/nodewords/meta-tags/forum'] = array( 'title' => 'Forum main page', 'page callback' => 'drupal_get_form', 'page arguments' => array('nodewords_tags_form', (string) NODEWORDS_TYPE_FORUM), 'access arguments' => $admin_access, 'type' => MENU_LOCAL_TASK, 'file' => 'nodewords.admin.inc', 'weight' => -4, ); } $items['admin/content/nodewords/meta-tags/custom'] = array( 'title' => 'Custom pages', 'page callback' => 'drupal_get_form', 'page arguments' => array('nodewords_custom_pages_overview'), 'access arguments' => $admin_access, 'type' => MENU_LOCAL_TASK, 'weight' => 20, 'file' => 'nodewords.admin.inc', ); $items['admin/content/nodewords/meta-tags/custom/add'] = array( 'title' => 'Add custom pages meta tags', 'page callback' => 'drupal_get_form', 'page arguments' => array('nodewords_custom_pages_edit'), 'access arguments' => $admin_access, 'type' => MENU_CALLBACK, 'file' => 'nodewords.admin.inc', ); $items['admin/content/nodewords/meta-tags/custom/%nodewords_page/delete'] = array( 'title' => 'Delete custom pages meta tags', 'page callback' => 'drupal_get_form', 'page arguments' => array('nodewords_custom_pages_confirm_delete', 5), 'access arguments' => $admin_access, 'parent' => 'admin/content/nodewords/meta-tags/custom', 'type' => MENU_CALLBACK, 'file' => 'nodewords.admin.inc', ); $items['admin/content/nodewords/meta-tags/custom/%nodewords_page/edit'] = array( 'title' => 'Edit custom pages meta tags', 'page callback' => 'drupal_get_form', 'page arguments' => array('nodewords_custom_pages_edit', 5), 'access arguments' => $admin_access, 'parent' => 'admin/content/nodewords/meta-tags/custom', 'type' => MENU_CALLBACK, 'file' => 'nodewords.admin.inc', ); return $items; } /** * Implements hook_node_operations(). */ function nodewords_node_operations() { $operations = array( 'delete_metatags' => array( 'label' => t('Delete meta tags'), 'callback' => 'nodewords_mass_update', 'callback arguments' => array('type' => NODEWORDS_TYPE_NODE, 'operation' => 'delete'), ), ); return $operations; } /** * Implements hook_node_type(). */ function nodewords_node_type($op, $info) { if ($op == 'delete') { $variables = array( 'nodewords_metatags_generation_method_', 'nodewords_metatags_generation_source_', 'nodewords_edit_metatags_', 'nodewords_filter_modules_output_', 'nodewords_filter_regexp_', 'nodewords_use_alt_attr_', 'nodewords_use_teaser_', ); foreach ($variables as $variable) { variable_del($variable . $info->type); } } } /** * Implements hook_nodeapi(). */ function nodewords_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) { switch ($op) { case 'delete': nodewords_delete_tags(NODEWORDS_TYPE_NODE, $node->nid); break; case 'insert': case 'update': if (isset($node->nodewords)) { nodewords_save_tags(NODEWORDS_TYPE_NODE, $node->nid, $node->nodewords, TRUE); } break; case 'load': return array( 'nodewords' => nodewords_load_tags(NODEWORDS_TYPE_NODE, $node->nid), ); case 'prepare translation': if (isset($node->translation_source->nodewords)) { $node->nodewords = $node->translation_source->nodewords; } break; case 'update index': $output_tags = array(); $tag_options = array( 'type' => NODEWORDS_TYPE_NODE, 'id' => $node->nid, 'output' => 'update index', ); if (isset($node->nodewords)) { // Prepare the tags. foreach (nodewords_get_possible_tags() as $name => $info) { $bool = ( !empty($info['templates']['search index']) && function_exists($function = $info['callback'] . '_prepare') ); if ($bool) { $function( $output_tags, isset($node->nodewords[$name]) ? $node->nodewords[$name] : array(), $tag_options ); } } drupal_alter('nodewords_tags', $output_tags, $tag_options); $output = _nodewords_output_tags($output_tags, 'update index'); drupal_alter('nodewords_tags_output', $output, $tag_options); return $output; } return ''; } } /** * Implements hook_perm(). */ function nodewords_perm() { return array('administer meta tags'); } /** * Implements hook_preprocess_page(). */ function nodewords_preprocess_page(&$variables) { // Do not do anything when running in install or update mode. if (defined('MAINTENANCE_MODE')) { return; } $options = _nodewords_detect_type_and_id(); $output_tags = array(); $options += array( 'default' => nodewords_load_tags(), 'output' => 'head', ); if ($options['type'] == NODEWORDS_TYPE_PAGER) { foreach (nodewords_get_possible_tags() as $name => $info) { $bool = ( !empty($info['context']['allowed']) && in_array(NODEWORDS_TYPE_PAGER, $info['context']['allowed']) && function_exists($function = $info['callback'] . '_prepare') ); if ($bool) { $function($output_tags, array(), $options); } } } else { // User profiles meta tags are not enabled. if ($options['type'] == NODEWORDS_TYPE_USER && !variable_get('nodewords_enable_user_metatags', TRUE)) { return; } // If the visitor doesn't have access to this node, don't load anything. elseif ($options['type'] == NODEWORDS_TYPE_NODE && !node_access('view', node_load($options['id']))) { $tags = array(); } // Otherwise, load the tags for this page. else { $tags = nodewords_load_tags($options['type'], $options['id']); } // Prepare the tags. foreach (nodewords_get_possible_tags() as $name => $info) { if (function_exists($function = $info['callback'] . '_prepare')) { $function($output_tags, isset($tags[$name]) ? $tags[$name] : array(), $options); } } } drupal_alter('nodewords_tags', $output_tags, $options); // Title tag has its own page preprocess variable and must be handled // separately. if (isset($output_tags['page_title'])) { if (!empty($output_tags['page_title'])) { drupal_set_title($output_tags['page_title']); $variables['head_title'] = strip_tags(drupal_get_title()); } unset($output_tags['page_title']); } $output = _nodewords_output_tags($output_tags); drupal_alter('nodewords_tags_output', $output, $options); // Output the tags to the header. drupal_set_html_head($output); $variables['head'] = drupal_get_html_head(); } /** * Implements hook_preprocess_maintenance_page(). */ function nodewords_preprocess_maintenance_page(&$variables) { nodewords_preprocess_page($variables); } /** * Implements hook_taxonomy(). */ function nodewords_taxonomy($op, $type, $array = NULL) { if (isset($array)) { if ($type == 'term') { $type = NODEWORDS_TYPE_TERM; $id = $array['tid']; } elseif ($type == 'vocabulary') { $type = NODEWORDS_TYPE_VOCABULARY; $id = $array['vid']; } else { return; } switch ($op) { case 'delete': nodewords_delete_tags($type, $id); break; case 'insert': case 'update': if (isset($array['nodewords'])) { nodewords_save_tags($type, $id, $array['nodewords'], TRUE); } break; } } } /** * Implements hook_theme(). */ function nodewords_theme() { return array( 'nodewords_custom_pages_overview' => array( 'arguments' => array('form' => array()), 'file' => 'nodewords.admin.inc', ), ); } /** * Implements hook_user(). */ function nodewords_user($op, &$edit, &$account, $category = NULL) { global $user; switch ($op) { case 'load': if (arg(0) == 'user' && !empty($account->uid) && variable_get('nodewords_enable_user_metatags', TRUE)) { $account->nodewords = nodewords_load_tags(NODEWORDS_TYPE_USER, $account->uid); } break; case 'delete': nodewords_delete_tags(NODEWORDS_TYPE_USER, $account->uid); break; case 'insert': case 'update': if (isset($edit['nodewords'])) { nodewords_save_tags(NODEWORDS_TYPE_USER, $account->uid, $edit['nodewords'], TRUE); } $edit['nodewords'] = NULL; break; case 'form': $bool = ( ( user_access('administer meta tags') || $user->uid == $account->uid ) && variable_get('nodewords_enable_user_metatags', TRUE) && $category == 'account' ); if ($bool) { $tags = nodewords_load_tags(NODEWORDS_TYPE_USER, $account->uid); $form['nodewords'] = nodewords_form( NODEWORDS_TYPE_USER, $tags ); return $form; } return array(); } } /** * Implements hook_user_operations(). */ function nodewords_user_operations() { $operations = array( 'delete_metatags' => array( 'label' => t('Delete meta tags'), 'callback' => 'nodewords_mass_update', 'callback arguments' => array('type' => NODEWORDS_TYPE_USER, 'operation' => 'delete'), ), ); return $operations; } /** * Delete tags from table. */ function nodewords_delete_tags($type, $id) { db_query("DELETE FROM {nodewords} WHERE type = %d AND id = %d", $type, $id); if ($type == NODEWORDS_TYPE_PAGE) { db_query("DELETE FROM {nodewords_custom} WHERE pid = %d", $id); } } /** * Return the form used to set the meta tags values. * * @param $type * The object to which the meta tags are associated (node, user, taxonomy * term, etc...). * @param $tags * The meta tags array as returned by nodewords_load_tags(). * * @return * An array as requested by the form API. */ function nodewords_form($type, $tags, $options = array()) { $default_options = array( 'fieldset' => TRUE, 'fieldset:title' => t('Meta tags'), 'fieldset:weight' => 20, ); $default_tag_options = array( 'default' => nodewords_load_tags(), 'type' => $type, ); $edit_tags = variable_get('nodewords_edit', array()); $form = array(); $options += $default_options; if (isset($options['tag_options']) && is_array($options['tag_options'])) { $tag_options = $options['tag_options'] + $default_tag_options; } else { $tag_options = $default_tag_options; } $tags_info = nodewords_get_possible_tags(); foreach ($tags_info as $name => $info) { $bool = ( ( user_access('administer meta tags') ) || !empty($edit_tags[$name]) ); if ($bool) { $bool = ( ( !empty($info['context']['allowed']) && is_array($info['context']['allowed']) && !in_array($type, $info['context']['allowed']) ) || ( !empty($info['context']['denied']) && is_array($info['context']['denied']) && in_array($type, $info['context']['denied']) ) ); if ($bool) { continue; } $bool = ( ( user_access('administer meta tags') ) || ( !empty($info['permission']) && user_access($info['permission']) ) ); if ($bool) { if (function_exists($function = $info['callback'] . '_form')) { $function($form, isset($tags[$name]) ? $tags[$name] : array(), $tag_options); } } } } if (!empty($form) && $options['fieldset']) { $form['#type'] = 'fieldset'; $form['#title'] = $options['fieldset:title']; $form['#tree'] = TRUE; $form['#collapsible'] = TRUE; $form['#collapsed'] = TRUE; $form['#weight'] = $options['fieldset:weight']; $form['#group'] = 'additional_settings'; } return $form; } /** * Query all the modules implementing meta tags and return the list of meta tags. * * @param $load * If TRUE, the file containing the code implementing the meta tags will be loaded. * * @return * An array containing the list of meta tags definitions. */ function nodewords_get_possible_tags($load = FALSE) { static $tags_info = array(); if ($load) { $tags_info = array(); } if (empty($tags_info)) { // Allow third-party modules to alter the meta tags list, or to add new // meta tags. foreach (module_implements('nodewords_tags_info') as $module) { if (module_hook($module, 'nodewords_api')) { $info = module_invoke($module, 'nodewords_api'); $version = '0.0'; if (isset($info)) { if (is_string($info)) { $version = $info; } elseif (is_array($info) && isset($info['version'])) { $version = $info['version']; if ($load && !empty($info['file'])) { $include_file = $info['file']; if (isset($info['path'])) { $include_file = $info['path'] . '/' . $include_file; } include_once $include_file; } } } $bool = ( version_compare($version, NODEWORDS_MINIMUM_API_VERSION, '<') || version_compare($version, NODEWORDS_API_VERSION, '>') ); if ($bool) { continue; } $result = module_invoke($module, 'nodewords_tags_info'); if (isset($result) && is_array($result)) { $tags_info = array_merge($tags_info, $result); } } } } return $tags_info; } /** * Return the term object matching a term ID. This is a modified version of * taxonomy_get_term() which uses db_rewrite_sql(). * * @param $tid * A term's ID. * @param $uid * The user ID; if not passed, the function will use the global user ID. * * @return * A term object, or FALSE. Results are statically cached. */ function nodewords_get_term($tid, $uid = NULL) { global $user; static $terms = array(); if (!isset($uid)) { $uid = $user->uid; } if (!isset($terms[$uid][$tid])) { $terms[$uid][$tid] = db_fetch_object( db_query( db_rewrite_sql('SELECT * FROM {term_data} t WHERE t.tid = %d', 't', 'tid'), $tid ) ); } return !empty($terms[$uid][$tid]) ? $terms[$uid][$tid] : FALSE; } /** * Load tags from table. */ function nodewords_load_tags($type = NODEWORDS_TYPE_DEFAULT, $id = 0) { static $queries = array(); // If the metatags haven't been loaded before, load them. if (!isset($queries[$type][$id])) { $result = db_query("SELECT * FROM {nodewords} WHERE type = %d AND id = %d", $type, $id); $tags = array(); $tags_info = nodewords_get_possible_tags(); while ($row = db_fetch_object($result)) { if (isset($tags_info[$row->name])) { $tags[$row->name] = unserialize($row->content); } } // If no metatags are found for this term, try loading the vocabulary's. if (empty($tags) && $type == NODEWORDS_TYPE_TERM) { $tags = nodewords_load_tags(NODEWORDS_TYPE_VOCABULARY, db_result(db_query('SELECT vid FROM {term_data} WHERE tid = %d', $id))); } // Cache the metatags for later. $queries[$type][$id] = $tags; } return $queries[$type][$id]; } /** * Delete the nodes meta tags. * * @param $ids * An array of IDs. * @param $type * The type of the object associated with the IDs (NODEWORDS_TYPE_NODE, NODEWORDS_TYPE_USER, * NODEWORDS_TYPE_PAGER, NODEWORDS_TYPE_PAGE, ...). * @param $operation * The operation to execute (currently implemented: delete). */ function nodewords_mass_update($ids, $type, $operation = 'delete') { if ($operation == 'delete') { if (($count = count($ids))) { if ($count <= 10) { db_query("DELETE FROM {nodewords} WHERE id IN (" . db_placeholders($ids, 'int') . ") AND type = %d", array_merge($ids, array($type)) ); if ($type == NODEWORDS_TYPE_PAGE) { db_query("DELETE FROM {nodewords_custom} WHERE pid IN (" . db_placeholders($ids, 'int') . ")", $ids); } drupal_set_message(t('The update has been performed.')); } else { $batch = array( 'operations' => array( array('_nodewords_mass_delete_batch_process', array($ids, $type)) ), 'finished' => '_nodewords_mass_update_batch_finished', 'title' => t('Processing'), 'progress_message' => '', 'error_message' => t('The update has encountered an error.'), 'file' => drupal_get_path('module', 'nodewords') .'/nodewords.admin.inc', ); batch_set($batch); } } } } /** * Create the content of a meta tag from a node teaser. * * @param $node * The node object the meta tag refers to. * @param $content * The meta tag content. * @param $options * An array of options; currently, the only option used is the maximum allowed * length. * * @return * The string used as meta tag content. */ function nodewords_metatag_from_node_content($node, $content, $options = array()) { // The method used to generate the summary string. $method = variable_get('nodewords_metatags_generation_method_' . $node->type, NODEWORDS_GENERATION_WHEN_EMPTY); // If not generating an automatic description, return immediately. if ($method == NODEWORDS_GENERATION_NEVER || ($method == NODEWORDS_GENERATION_WHEN_EMPTY && !empty($content))) { return $content; } // Proceed as normal. $result = ''; $source = variable_get('nodewords_metatags_generation_source_' . $node->type, NODEWORDS_GENERATION_TEASER); // If generating an automatic description, determine the source. if (!empty($node->teaser) && ($source == NODEWORDS_GENERATION_TEASER || $source == NODEWORDS_GENERATION_TEASER_BODY)) { $result = $node->teaser; } elseif (!empty($node->body) && ($source == NODEWORDS_GENERATION_BODY || ($source == NODEWORDS_GENERATION_TEASER_BODY && empty($node->teaser)))) { $result = $node->body; } // Clean up the text by running it through applicable filters. if (!empty($result)) { // Check for the presence of the PHP evaluator filter in the current format. // If the text contains PHP code, do not split it up to prevent parse errors. $filters = filter_list_format($node->format); if (isset($filters['php/0']) && strpos($result, 'format, FALSE); // Ensure there's a setting for controlling the maximum summary length. if (!isset($options['size'])) { $options['size'] = variable_get('nodewords_max_size', 350); } // Optionally replace the tag IMG with its ALT attribute. if (variable_get('nodewords_use_alt_attribute', FALSE)) { $result = preg_replace("/]*alt=[\"']([^\"']*)[\"'][^>]*>/i", ' ($1) ', $result); } // Strip off all the HTML tags. $result = strip_tags($result); // Remove the strings added from third-party modules. $modules = array_filter(variable_get('nodewords_filter_modules_output_' . $node->type, array())); $regexps = array( 'imagebrowser' => '/\[ibimage[^\]]*\]/i', 'img_assist' => '/\[img_assist[^\]]*\]/i', ); foreach ($regexps as $module => $regexp) { if (isset($modules[$module])) { $result = preg_replace($regexp, '', $result); } } // Remove the text matching the type-specific regular expression. if ($regexp = trim(variable_get('nodewords_filter_regexp_' . $node->type, ''))) { $result = preg_replace('/' . $regexp . '/', '', $result); } // Remove line breaks. $result = preg_replace('/(\r\n?|\n)/', ' ', $result); // Remove any leading & trailing whitespace. $result = trim($result); // Remove excess inline whitespace. $result = preg_replace('/\s\s+/', ' ', $result); // Trim the filtered text to the maximum length allowed. $result = node_teaser($result, $node->format, $options['size']); } } return $result; } /** * This function is used from the menu system when a menu callback path contains * %nodewords_page_load. */ function nodewords_page_load($pid) { return _nodewords_get_custom_pages_data($pid); } /** * Update or insert tags in the table. */ function nodewords_save_tags($type, $id, $tags, $log_message = FALSE) { global $user; $done = FALSE; $tags_info = nodewords_get_possible_tags(); $types_str = array( NODEWORDS_TYPE_BLOG => t('blog main page'), NODEWORDS_TYPE_DEFAULT => t('default'), NODEWORDS_TYPE_ERRORPAGE => t('HTTP error page'), NODEWORDS_TYPE_FORUM => t('forum main page'), NODEWORDS_TYPE_FRONTPAGE => t('front page'), NODEWORDS_TYPE_NODE => t('node'), NODEWORDS_TYPE_OFFLINE => t('site offline page'), NODEWORDS_TYPE_PAGE => t('custom page'), NODEWORDS_TYPE_PAGER => t('list page'), NODEWORDS_TYPE_TERM => t('taxonomy term'), NODEWORDS_TYPE_USER => t('user profile'), NODEWORDS_TYPE_VOCABULARY => t('vocabulary'), ); foreach ($tags as $name => $content) { if (isset($tags_info[$name])) { $content = serialize($content); $result = db_fetch_object( db_query_range( "SELECT * FROM {nodewords} WHERE type = %d AND id = %d AND name = '%s'", $type, $id, $name, 0, 1 ) ); if ($result === FALSE) { $row = new stdClass(); $row->type = $type; $row->id = $id; $row->name = $name; } else { $row = $result; } $row->content = $content; drupal_write_record('nodewords', $row, $result !== FALSE ? 'mtid' : array()); if (!$done) { watchdog('nodewords', 'User %name changed the meta tags for type %type (ID %id).', array('%name' => $user->name, '%type' => isset($types_str[$type]) ? $types_str[$type] : t('unknown'), '%id' => $id)); $done = TRUE; } } } // Reload the tags so that future API calls get the correct data. nodewords_load_tags($type, $id); } /** * Remove the duplicates from a list of items separated from the separator, * preserving the order in which they appear. * @param $text * The string containing the list of items concatenated using $separator. * @param $separator * The string used to split the string into an array. A space will be appended * to the string before it is used to create the string from the array of * unique items found in the string passed as argument. * @param $max_items * The maximum number of items accepted in the returned array; the default * value is -1, which means no limit. * * @return * A string containing only unique items present in the string of concatenated * items. */ function nodewords_unique_values($text, $separator = ',', $max_items = -1) { $lc_values = array(); $unique_values = array(); if (empty($text)) { return ''; } foreach (array_filter(array_map('trim', explode($separator, $text))) as $item) { $lowercase = drupal_strtolower($item); if (!in_array($lowercase, $lc_values)) { $lc_values[] = $lowercase; $unique_values[] = $item; } } if ($max_items > 0) { $unique_values = array_slice($unique_values, 0, $max_items); } return implode("$separator", $unique_values); } /** * Return the absolute URL of a path. * * Return the absolute URL of a path built using the base URL saved in the * Drupal variable nodewords_base_url. * * @param $path * The path for which the absolute must be built. * @param $options * An array of options as used by url(). */ function nodewords_url($path, $options = array()) { // Remove the trailing slash, it will be added in the url() function. $base_url = rtrim(variable_get('nodewords_base_url', ''), '/'); $options += array( 'alias' => TRUE, 'absolute' => TRUE, 'fragment' => '', 'query' => '', 'prefix' => '' ); // Identify if the URL alias should be used. if (variable_get('nodewords_use_path_alias', TRUE)) { // Note: the url() function's 'alias' argument is to specify that the // path is *already* an alias, it is not for requesting an alias be loaded. $options['alias'] = FALSE; } $options['base_url'] = empty($base_url) ? NULL : $base_url; return url($path, $options); } /** * Implementation of hook_migrate_api(). */ function nodewords_migrate_api() { return array( 'api' => 1, 'integration modules' => array( 'nodewords' => array('status' => FALSE), ), ); } /** * Internal functions */ /** * Try to guess the $type and $id by looking at $_GET['q']. */ function _nodewords_detect_type_and_id() { $arg = arg(); $default = array('type' => NODEWORDS_TYPE_PAGE, 'id' => 0); // Do not do anything when running in install or update mode. if (defined('MAINTENANCE_MODE')) { return array('type' => NODEWORDS_TYPE_NONE); } if (variable_get('site_offline', 0) && !user_access('administer site configuration')) { // User will see the site-offline page. return array('type' => NODEWORDS_TYPE_OFFLINE, 'id' => 0); } $headers = drupal_get_headers(); if (preg_match('@HTTP/1\.[01]\x20+403@', $headers)) { return array('type' => NODEWORDS_TYPE_ERRORPAGE, 'id' => 403); } if (preg_match('@HTTP/1\.[01]\x20+404@', $headers)) { return array('type' => NODEWORDS_TYPE_ERRORPAGE, 'id' => 404); } $bool = ( !variable_get('nodewords_list_repeat', FALSE) && isset($_REQUEST['page']) && intval($_REQUEST['page']) > 0 ); if ($bool) { return array('type' => NODEWORDS_TYPE_PAGER, 'id' => 0); } if (drupal_is_front_page() && variable_get('nodewords_use_frontpage_tags', TRUE)) { return array('type' => NODEWORDS_TYPE_FRONTPAGE, 'id' => 0); } if (module_exists('blog') && $_GET['q'] == 'blog') { return array('type' => NODEWORDS_TYPE_BLOG, 'id' => 0); } if (module_exists('forum') && $_GET['q'] == 'forum') { return array('type' => NODEWORDS_TYPE_FORUM, 'id' => 0); } // Check all of the custom page paths. foreach (_nodewords_get_pages_paths() as $pid => $path) { // The path is a system path. if (drupal_match_path($_GET['q'], $path)) { return array('type' => NODEWORDS_TYPE_PAGE, 'id' => $pid); } // The path is a URL alias. $alias = drupal_get_path_alias($_GET['q']); if ($alias != $_GET['q'] && drupal_match_path($alias, $path)) { return array('type' => NODEWORDS_TYPE_PAGE, 'id' => $pid); } } if (!isset($arg[0])) { return $default; } _nodewords_load_all_includes(); $result = array( 'type' => NODEWORDS_TYPE_NONE, 'id' => 0, ); foreach (module_implements('nodewords_type_id') as $module) { $function = $module . '_nodewords_type_id'; $function($result, $arg); if ($result['type'] != NODEWORDS_TYPE_NONE) { return $result; } } return $default; } /** * Load the page meta tags data from the cache. * * @param $id * The ID of the page to load; by default the function loads all the custom * pages data. */ function _nodewords_get_custom_pages_data($id = NULL) { static $pages; if (!isset($pages)) { $pages = array(); $result = db_query("SELECT * FROM {nodewords_custom} ORDER BY weight ASC"); while ($page = db_fetch_object($result)) { $page->tags = nodewords_load_tags(NODEWORDS_TYPE_PAGE, $page->pid); $pages[$page->pid] = $page; } } return isset($id) ? (isset($pages[$id]) ? $pages[$id] : FALSE) : $pages; } /** * Answer a listing of page paths, stored in a static cache. */ function _nodewords_get_pages_paths() { static $paths; if (!isset($paths)) { $paths = array(); $result = db_query("SELECT pid, path FROM {nodewords_custom} ORDER BY weight ASC"); while ($page = db_fetch_object($result)) { $paths[$page->pid] = $page->path; } } return $paths; } /** * Load the files in the directory "includes" basing on the enabled modules. */ function _nodewords_load_all_includes() { $dir = drupal_get_path('module', 'nodewords') . '/includes'; // Update this array when new files are added. $includes = array( 'forum', 'image', 'node', 'taxonomy', 'taxonomy_menu', 'uc_catalog', 'user', ); foreach ($includes as $include) { if (module_exists($include) && !module_hook($include, 'nodewords_type_id')) { module_load_include('inc', 'nodewords', 'includes/' . $include); } } } /** * Render the meta tag values as HTML. * * @param $tags * An array of tags. * * @param $output_type * The type of output, 'head' (default), or 'update index'. * * @return * A string containing the HTML output for the META tag. */ function _nodewords_output_tags($tags, $output_type = 'head') { $output = array(); $tags_info = nodewords_get_possible_tags(); $weights = array(); foreach ($tags as $name => $content) { if ($content === '') { continue; } // Check if it's a property meta tag (which may contain an XML namespace). $is_property = ( $output_type == 'head' && isset($tags_info[$name]['templates']['head'][$name]) && $tags_info[$name]['templates']['head'][$name] == NODEWORDS_META_PROPERTY ); $parts = $is_property ? array($name, $name) : explode(':', $name); // Ensure that the previous assigned output template was cleared. $template = NULL; if (!isset($parts[1])) { $parts[1] = $parts[0]; } if ($output_type == 'update index') { $bool = ( isset($tags_info[$parts[0]]['templates']['search index'][$parts[1]]) ); if ($bool) { // The '%content' element will be added later. $replace = array( '%attributes' => empty($tags_info[$parts[0]]['attributes'][$parts[1]]) ? '' : drupal_attributes($tags_info[$parts[0]]['attributes'][$parts[1]]), ); $template = $tags_info[$parts[0]]['templates']['search index'][$parts[1]]; $weight = isset($tags_info[$parts[0]]['weight'][$parts[1]]) ? $tags_info[$parts[0]]['weight'][$parts[1]] : 0; } } else { $bool = ( isset($tags_info[$parts[0]]['templates']['head'][$parts[1]]) && ($meta_name = check_plain(decode_entities(strip_tags($parts[1])))) ); if ($bool) { // The '%content' element will be added later. $replace = array( '%name' => $meta_name, '%attributes' => empty($tags_info[$parts[0]]['attributes'][$parts[1]]) ? '' : drupal_attributes($tags_info[$parts[0]]['attributes'][$parts[1]]), ); $template = $tags_info[$parts[0]]['templates']['head'][$parts[1]]; $weight = isset($tags_info[$parts[0]]['weight'][$parts[1]]) ? $tags_info[$parts[0]]['weight'][$parts[1]] : 0; if (!is_string($template)) { switch ($template) { case NODEWORDS_META: $template = ''; break; case NODEWORDS_HTTP_EQUIV: $template = ''; break; case NODEWORDS_LINK_REL: $template = ''; break; case NODEWORDS_LINK_REV: $template = ''; break; // This requires theme customizations in order for the output to // remain valid XHTML, see the OpenGraph section of the README.txt // for further details. case NODEWORDS_META_PROPERTY: $template = ''; break; default: $template = ''; break; } } } } if (!empty($template)) { // Allow the meta tags to have multiple values. if (!empty($tags_info[$name]['multiple'])) { $content = explode("\n", $content); } // Simplify the output handling. if (!is_array($content)) { $content = array($content); } // Now add the content element(s) to the replacement array. foreach ($content as $content_item) { $replace['%content'] = str_replace(''', "'", trim(check_plain(decode_entities(strip_tags($content_item))))); $output[] = strtr($template, $replace); $weights[] = $weight; } } } if (count($output)) { array_multisort($weights, $output); return implode("\n", $output); } else { return ''; } } /** * Provide a complete path for the current URL, optionally using a value * manually assigned via a form. * * @param $content * An array of settings for this current field, optionally including the * manually assigned path. * @param $options * An array of options for this current page. * * @return * Fully qualified absolute URL for the current page. */ function _nodewords_prepare_path($content, $options) { // Need to compare the value against system base path. $base_path = base_path(); // Remove the base path from the front of the URL. if (!empty($content['value']) && strpos($content['value'], $base_path) === 0) { $content['value'] = drupal_substr($content['value'], drupal_strlen($base_path)); } // Start with a system path. if (empty($content['value'])) { $path = ''; switch ($options['type']) { case NODEWORDS_TYPE_FRONTPAGE: $path = ''; break; case NODEWORDS_TYPE_NODE: $path = 'node/' . $options['id']; break; case NODEWORDS_TYPE_PAGE: $path = $_GET['q']; break; case NODEWORDS_TYPE_TERM: // Accommodate other modules that change the path via hook_term_path(). $term = taxonomy_get_term($options['id']); $path = module_invoke('taxonomy', 'term_path', $term); break; case NODEWORDS_TYPE_USER: $path = 'user/' . $options['id']; break; } if (!empty($path)) { $content['value'] = $path; } } if (!empty($content['value'])) { return check_url(nodewords_url($content['value'], $options)); } else { return ''; } } /** * Extract or generate the description for the current object. * * @param $tags * The array of tags currently generated. * @param $content * An array of settings for this current field, optionally including the * manually assigned path. * @param $options * An array of options for this current page. * @param $tag_name * The name of the tag being generated. * * @return * The final string to be used for the description tag. */ function _nodewords_prepare_description(&$tags, $content, $options, $tag_name) { // Will need this. if (!isset($content['value'])) { $content['value'] = ''; } // If this is a node, try to load it then see if the description can or needs // to be automatically generated. if ($options['type'] == NODEWORDS_TYPE_NODE && $node = node_load($options['id'])) { $autogenerate = variable_get('nodewords_metatags_generation_method_' . $node->type, NODEWORDS_GENERATION_WHEN_EMPTY); if ($autogenerate == NODEWORDS_GENERATION_ALWAYS || (empty($content['value']) && $autogenerate == NODEWORDS_GENERATION_WHEN_EMPTY)) { $content['value'] = nodewords_metatag_from_node_content($node, $content['value']); } } // Load the default if no value is present. if (empty($content['value']) && !empty($options['default'][$tag_name]['value'])) { $content['value'] = $options['default'][$tag_name]['value']; } $tags[$tag_name] = $content['value']; }