t('Meta tags'));
return $blocks;
case 'view':
$block = array();
switch ($delta) {
case 0:
$tags = nodewords_get();
if (isset($tags['keywords'])) {
$tags['keywords'] = str_replace(',', ', ', $tags['keywords']);
}
$block['subject'] = t('Meta tags');
$block['content'] = theme('nodewords_content', $tags);
break;
}
return $block;
}
}
/**
* Implemenation of hook_help().
*/
function nodewords_help($path, $arg) {
switch ($path) {
case 'admin/content/nodewords/frontpage':
return t('On this page you can enter the meta tags for the front page of your site.');
}
}
/**
* Implementation of hook_menu().
*/
function nodewords_menu() {
$items['admin/content/nodewords'] = array(
'title' => 'Meta tags',
'page callback' => 'drupal_get_form',
'page arguments' => array('nodewords_admin_settings_form'),
'description' => 'Configure HTML meta tags for all content.',
'access arguments' => array('administer meta tags'),
'type' => MENU_NORMAL_ITEM,
'file' => 'nodewords.admin.inc',
);
$items['admin/content/nodewords/global'] = array(
'title' => 'Settings',
'access arguments' => array('administer meta tags'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
'file' => 'nodewords.admin.inc',
);
$items['admin/content/nodewords/frontpage'] = array(
'title' => 'Front page',
'page callback' => 'drupal_get_form',
'page arguments' => array('nodewords_admin_frontpage_form'),
'access arguments' => array('administer meta tags'),
'type' => MENU_LOCAL_TASK,
'file' => 'nodewords.admin.inc',
);
return $items;
}
/**
* Implementation of hook_init()
* (code executed on non-cached page)
*/
function nodewords_init() {
$tags = nodewords_get();
foreach ($tags as $name => $content) {
if (!empty($content)) {
drupal_set_html_head('');
}
}
}
/**
* Implementation of hook_nodeapi().
*/
function nodewords_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
switch ($op) {
case 'delete':
if (user_access('edit meta tags')) {
_nodewords_delete('node', $node->nid);
}
break;
case 'insert':
case 'update':
if (isset($node->nodewords) && user_access('edit meta tags')) {
_nodewords_set('node', $node->nid, $node->nodewords);
}
break;
case 'update index':
$output = '
'. $node->nodewords['keywords'] .'
';
$output .= ''. $node->nodewords['description'] .'
';
return $output;
case 'load':
return array(
'nodewords' => _nodewords_load('node', $node->nid),
);
}
}
/**
* Implementation of hook_user()
*/
function nodewords_user($op, &$edit, &$account, $category = NULL) {
switch ($op) {
case 'load':
$account->nodewords = _nodewords_load('user', $account->uid);
break;
case 'delete':
if (user_access('edit meta tags')) {
_nodewords_delete('user', $account->uid);
}
break;
case 'insert':
case 'update':
if (isset($edit['nodewords']) && user_access('edit meta tags')) {
_nodewords_set('user', $account->uid, $edit['nodewords']);
}
break;
case 'form':
if (user_access('edit meta tags')) {
$tags = _nodewords_load('user', $account->uid);
$form['nodewords'] = _nodewords_form('user', $tags);
return $form;
}
break;
}
}
/**
* Implementation of hook_form_alter().
*/
function nodewords_form_alter(&$form, $form_state, $form_id) {
if ($form_id == 'node_type_form' && isset($form['#node_type'])) {
$form['workflow']['nodewords'] = array(
'#type' => 'checkbox',
'#title' => t('Allow editing of meta tags'),
'#default_value' => variable_get('nodewords_'. $form['#node_type']->type, 1),
'#description' => t('Users with the edit meta tags permission will be able to edit the meta tags for this content type.'),
);
return;
}
if (!user_access('edit meta tags')) {
return;
}
if (isset($form['type']) && $form_id == $form['type']['#value'] .'_node_form') {
if (variable_get('nodewords_'. $form['type']['#value'], 1)) {
$type = 'node';
$id = $form['nid']['#value'];
}
}
elseif ($form_id == 'taxonomy_form_vocabulary') {
$type = 'vocabulary';
$id = $form['vid']['#value'];
$form['submit']['#weight'] = 45;
$form['delete']['#weight'] = 50;
}
elseif ($form_id == 'taxonomy_form_term') {
$type = 'term';
$id = $form['tid']['#value'];
$form['submit']['#weight'] = 45;
$form['delete']['#weight'] = 50;
}
/* TODO: reenable panels support
elseif ($form_id == 'panels_edit_form') {
$type = 'panels';
$id = $form['did']['#value'];
$form['submit']['#weight'] = 45;
$form['#submit'][] = 'nodewords_panels_handler';
}
elseif ($form_id == 'panels_delete_confirm') {
$form['#submit'][] = 'nodewords_panels_handler';
}
*/
elseif ($form_id == 'views_ui_edit_view_form') {
$type = 'views';
$id = $form_state['view']->vid;
$form['buttons']['#weight'] = 45;
$form['buttons']['save']['#submit'][] = 'nodewords_views_handler';
}
elseif ($form_id == 'views_ui_delete_confirm') {
$form['#submit'][] = 'nodewords_views_handler';
}
if (isset($type)) {
if (!empty($form_state['values']['nodewords'])) {
$tags = $form_state['values']['nodewords'];
}
else if (isset($id) && is_numeric($id)) {
$tags = _nodewords_load($type, $id);
}
else {
$tags = array();
}
$form['nodewords'] = _nodewords_form($type, $tags);
}
}
/**
* Implementation of hook_perm().
*/
function nodewords_perm() {
return array('administer meta tags', 'edit meta tags');
}
/**
* Implementation of hook_taxonomy().
*/
function nodewords_taxonomy($op, $type, $object = NULL) {
if ($type == 'term') {
$id = $object['tid'];
}
elseif ($type == 'vocabulary') {
$id = $object['vid'];
}
else {
return;
}
switch ($op) {
case 'delete':
if (user_access('edit meta tags')) {
_nodewords_delete($type, $id);
}
break;
case 'insert':
case 'update':
if (isset($object['nodewords']) && user_access('edit meta tags')) {
_nodewords_set($type, $id, $object['nodewords']);
}
break;
}
}
/**
* Handling of edit/delete form for panels.
*/
/* TODO: reenable panels support
function nodewords_panels_handler($form, &$form_state) {
switch ($form_state['values']['op']) {
case t('Save'):
if (isset($form_state['values']['nodewords']) && user_access('administer meta tags')) {
_nodewords_set('panels', $form_state['values']['did'], $form_state['values']['nodewords']);
}
break;
case t('Delete'):
if ($form_state['values']['confirm']) {
_nodewords_delete('panels', $form_state['values']['did']);
}
break;
}
}
*/
/**
* Handling of edit/delete form for views.
*/
function nodewords_views_handler($form, &$form_state) {
switch ($form_state['clicked_button']['#value']) {
case t('Save'):
if (isset($form_state['values']['nodewords']) && user_access('administer meta tags')) {
if ($form_state['view']->vid > 0) {
_nodewords_set('views', $form_state['view']->vid, $form_state['values']['nodewords']);
}
}
break;
case t('Delete'):
if ($form_state['values']['confirm']) {
_nodewords_delete('views', $form_state['view']->vid);
}
break;
}
}
/**
* Theming functions.
*/
/**
* hook_theme() registry
*/
function nodewords_theme() {
return array(
'nodewords_content' => array(
'arguments' => array('tags'),
),
);
}
/**
* Displays the defined meta tags $tags as content, eg in a
* block or body.
*
* @param $tags
* Associative array of defined tags.
* No need to 'check_plain' on content.
* @return
* Formatted HTML.
*/
function theme_nodewords_content($tags) {
$output = '';
foreach ($tags as $name => $content) {
if (!empty($content)) {
$class = "meta-tags-$name";
$output .= ''. $name .'';
$output .= ''. $content .'';
}
}
if (!empty($output)) {
$output = ''. $output .'
';
}
return $output;
}
/**
* Nodewords API functions.
*/
/**
* Get the defined meta tags for $type / $id.
*
* @param $type
* Realm of the object the meta tags are associated with.
* This is one of the following: 'node', 'page', 'term',
* 'vocabulary'.
* @param $ids
* Id (or path) of the object to get the meta tags from.
* This is one of the following:
* - 'node' => array of 'nid' of the node
* - 'user' => array of 'uid' of the user
* - 'page' => array of 'path' of the displayed page
* - 'term' => array of 'tid' of the term
* - 'vocabulary' => array of 'vid' of the vocabulary
* - 'panels' => array of 'did' of the panel
* - 'views' => array of 'vid' of the view
* If $type or $ids is not set, an attempt will be made to
* get it from $_GET['q'].
* @param $filtered
* If TRUE, only the meta tags that the user configured for
* output will be returned.
* If FALSE, all meta tags will be returned.
* @return
* An associative array of the defined meta tags.
*/
function nodewords_get($type = NULL, $ids = NULL, $filtered = TRUE) {
// Autodetect if $type and/or $ids is not set
if ($type == NULL || $ids == NULL) {
$result = _nodewords_detect_type_and_ids();
$type = $result['type'];
$ids = $result['ids'];
}
if (!is_array($ids)) {
$ids = array($ids);
}
// Load the values from the database
if (count($ids) == 1 && ($type != 'node' || node_access('view', node_load($ids[0])))) {
$tags = _nodewords_load($type, $ids[0]);
}
else {
$tags = array();
}
// Pages with more than one node/term/vocabulary/...
if ($type == 'term') {
if (isset($tags['keywords'])) {
$terms = array($tags['keywords']);
}
else {
$terms = array();
}
foreach ($ids as $id) {
$term = taxonomy_get_term($id);
if ($term) {
$terms[] = $term->name;
}
}
if (count($terms)) {
$tags['keywords'] = implode(',', $terms);
}
}
// Prepare tags for output
$tags = _nodewords_prepare($type, $ids, $tags, $filtered);
return $tags;
}
/**
* Database access functions.
*/
/**
* Update or insert tags in the table
*/
function _nodewords_set($type, $id, $tags) {
foreach ($tags as $name => $content) {
if (empty($content)) {
_nodewords_delete_tag($type, $id, $name);
}
else {
_nodewords_update_tag($type, $id, $name, $content);
}
}
}
/**
* Delete tags from table
*/
function _nodewords_delete($type, $id) {
return db_query("DELETE FROM {nodewords} WHERE type = '%s' AND id = '%s'", $type, $id);
}
/**
* Load tags from table
*/
function _nodewords_load($type, $id) {
$tags = array();
$result = db_query("SELECT * FROM {nodewords} WHERE type = '%s' AND id = '%s'", $type, $id);
while ($row = db_fetch_object($result)) {
$tags[$row->name] = $row->content;
}
if (empty($tags) && $type == 'term') {
return _nodewords_load('vocabulary', db_result(db_query('SELECT vid FROM {term_data} WHERE tid = %d', $id)));
}
return $tags;
}
/**
* Set one tag
*/
function _nodewords_update_tag($type, $id, $name, $content) {
$result = db_query("SELECT * FROM {nodewords} WHERE type = '%s' AND id = '%s' AND name = '%s'", $type, $id, $name);
if (db_result($result) == FALSE) {
db_query("INSERT INTO {nodewords} (type, id, name, content) VALUES ('%s', '%s', '%s', '%s')", $type, $id, $name, $content);
}
else {
db_query("UPDATE {nodewords} SET content = '%s' WHERE type = '%s' AND id = '%s' AND name = '%s'", $content, $type, $id, $name);
}
}
/**
* Delete one tag
*/
function _nodewords_delete_tag($type, $id, $name) {
db_query("DELETE FROM {nodewords} WHERE type = '%s' and id = '%s' AND name = '%s'", $type, $id, $name);
}
/**
* Helper functions - forms.
*/
/**
* Create a form - returns a $form variable
*/
function _nodewords_form($type, $tags) {
$settings = _nodewords_get_settings();
$form = array();
foreach (_nodewords_get_possible_tags() as $tag) {
$function = 'nodewords_'. $tag .'_form';
if ($settings['edit'][$tag] && function_exists($function)) {
$tags[$tag] = isset($tags[$tag]) ? $tags[$tag] : '';
$element = $function($type, $tags[$tag], $settings);
if ($element) {
$form[$tag] = $element;
}
}
}
if (!empty($form)) {
$form['#type'] = 'fieldset';
$form['#title'] = t('Meta tags');
$form['#tree'] = TRUE;
$form['#collapsible'] = TRUE;
$form['#collapsed'] = empty($tags);
$form['#weight'] = 20;
}
return $form;
}
/**
* Helper functions - settings.
*/
/**
* Load default and user-defined settings. If $defaults => default settings are returned.
*/
function _nodewords_get_settings($defaults = FALSE) {
static $settings = NULL;
static $default_settings = array(
'use_teaser' => 1,
'max_size' => 255,
'db_max_size' => 255,
'keywords_vids' => array(),
'global' => array(
'copyright' => '',
'geourl' => '',
'keywords' => '',
'robots' => 'index,follow',
),
'head' => array(
'abstract' => 1,
'copyright' => 1,
'description' => 1,
'geourl' => 1,
'keywords' => 1,
'Revisit-After' => 1,
'robots' => 1,
),
'edit' => array(
'abstract' => 0,
'copyright' => 0,
'description' => 1,
'geourl' => 0,
'keywords' => 1,
'Revisit-After' => 0,
'robots' => 0,
)
);
if ($defaults) {
return $default_settings;
}
if ($settings == NULL) {
$settings = variable_get('nodewords', array());
foreach ($default_settings as $key => $value) {
if (is_array($value)) {
$settings[$key] = isset($settings[$key]) ? array_merge($value, $settings[$key]) : $value;
}
elseif (!isset($settings[$key])) {
$settings[$key] = $value;
}
}
}
return $settings;
}
/**
* Return a list of possible output tags
*/
function _nodewords_get_possible_tags() {
static $tags;
if (!isset($tags)) {
$tags = array();
$path = drupal_get_path('module', 'nodewords') .'/metatags';
$files = file_scan_directory($path, '.*\.inc$');
foreach ($files as $filename => $file) {
include_once($filename);
$tags[] = basename($filename, '.inc');
}
}
return $tags;
}
/**
* Return a list of viewable output tags
*/
function _nodewords_get_viewable_tags($where = 'head') {
$settings = _nodewords_get_settings();
$output = array();
foreach ($settings[$where] as $name => $viewable) {
if ($viewable) {
$output[] = $name;
}
}
return $output;
}
/**
* Helper functions - other.
*/
/**
* Invoke a hook_nodewords() operation in all modules.
*
* @param &$tags
* A tags object.
* @param $op
* A string containing the name of the nodeapi operation.
* @param $a3, $a4
* Additional parameters to pass through this hook
* @return
* The returned value of the invoked hooks.
*/
function _nodewords_invoke(&$tags, $op, $a3, $a4) {
$return = array();
foreach (module_implements('nodewords') as $name) {
$function = $name .'_nodewords';
$result = $function($tags, $op, $a3, $a4);
if (isset($result) && is_array($result)) {
$return = array_merge($return, $result);
}
else if (isset($result)) {
$return[] = $result;
}
}
return $return;
}
/**
* Prepare the tags so they are ready for output. This includes:
* - setting default values if the tag is empty
* - adding or altering some content (eg add global keywords)
*/
function _nodewords_prepare($type, $ids, $tags, $filtered = TRUE) {
$settings = _nodewords_get_settings();
_nodewords_invoke($tags, 'prepare', $type, $ids);
// Prepare the tags
foreach (_nodewords_get_possible_tags() as $tag) {
$function = 'nodewords_'. $tag .'_prepare';
if (function_exists($function)) {
$tags[$tag] = $function($type, $ids, isset($tags[$tag]) ? $tags[$tag] : '', $settings);
}
}
// Filter out tags the user has chosen not to see
if ($filtered) {
$tags = _nodewords_filter_viewable($tags);
}
// EXCEPTION - QUICK HACK - DANGER AHEAD - BRAIN SWITCH-OFF
if (isset($tags['geourl'])) {
$tags['geourl'] = str_replace(array(' ', ' '), '', $tags['geourl']);
$tags['geo.position'] = str_replace(',', ';', $tags['geourl']);
$tags['ICBM'] = str_replace(';', ',', $tags['geourl']);
// http://drupal.org/node/103399 : we are unable to drupal_get_title(),
// so unfortunately, only node-pages can get a DC.title.
if ($type == 'node' && count($ids) == 1) {
$node = node_load($ids[0]);
$tags['DC.title'] = $node->title;
}
elseif ($type == 'panels' && count($ids) == 1) {
if (function_exists('panels_api_version') && module_exists('panels_page')) {
$version = panels_api_version();
if ($version[0] == 2 && $version[1] == 0) {
$panel = db_fetch_object(db_query("SELECT * FROM {panels_page} WHERE did = %d", $ids[0]));
}
}
elseif (db_table_exists('panels_info')) {
$panel = db_fetch_object(db_query("SELECT * FROM {panels_info} WHERE did = %d", $ids[0]));
}
if ($panel) {
$tags['DC.title'] = $panel->title;
}
}
else {
$tags['DC.title'] = variable_get('site_name', 'Drupal');
if (variable_get('site_slogan', '')) {
$tags['DC.title'] .= ' | '. variable_get('site_slogan', '');
}
}
unset($tags['geourl']);
}
// Check 'content' of each tag.
$tags = array_map('_nodewords_check_content', $tags);
return $tags;
}
/**
* Remove the meta tags from $tags that the user chose not to show.
*/
function _nodewords_filter_viewable($tags, $where = 'head') {
$output = array();
if (is_array($tags)) {
$viewables = _nodewords_get_viewable_tags($where);
foreach ($viewables as $name) {
if (isset($tags[$name])) {
$output[$name] = $tags[$name];
}
}
}
return $output;
}
/**
* Remove any content from the $tag that is not allowed in a meta content attribute.
*/
function _nodewords_check_content($text) {
$settings = _nodewords_get_settings();
$size = $settings['max_size'];
if (function_exists('preg_replace')) {
$pattern = '/
]*alt=["\']([^"\']*)["\'][^>]*>/i';
$replacement = '${1}';
$text = preg_replace($pattern, $replacement, $text);
}
$text = strip_tags($text);
$text = check_plain($text);
$needles = array(' ', "\r", "\n", ''');
$replaces = array(' ', ' ', ' ', "'");
$text = str_replace($needles, $replaces, $text);
$text = trim($text);
$text = preg_replace('/\s+/', ' ', $text);
if ($size > 0 && drupal_strlen($text) > $size) {
$text = truncate_utf8($text, $size);
$length = strrpos($text, ' '); //TODO: is this UTF safe?
if (!is_bool($length)) {
$text = substr($text, 0, $length); //TODO: is this UTF safe?
}
}
// Do not index pager pages.
if (isset($_GET['page']) && is_numeric($_GET['page'])) {
$text = preg_replace('!^index!', 'noindex', $text);
}
return $text;
}
/**
* Try to guess the $type and $ids by looking at $_GET['q'].
*/
function _nodewords_detect_type_and_ids() {
if (!variable_get('nodewords-repeat', 1) && isset($_REQUEST['page']) && intval($_REQUEST['page']) > 0) {
return array('type' => 'none', 'ids' => array());
}
if (drupal_is_front_page()) {
if (variable_get('nodewords-use_front', 1)) {
return array('type' => 'page', 'ids' => array(''));
}
}
/* TODO: reenable panels support
if (module_exists('panels')) {
if (function_exists('panels_api_version') && module_exists('panels_page')) {
$version = panels_api_version();
if ($version[0] == 2 && $version[1] == 0) {
$panel = db_fetch_array(db_query("SELECT * FROM {panels_page} WHERE path = '%s'", $_GET['q']));
}
}
elseif (db_table_exists('panels_info')) {
$panel = db_fetch_array(db_query("SELECT * FROM {panels_info} WHERE path = '%s'", $_GET['q']));
}
if ($panel) {
return array('type' => 'panels', 'ids' => array($panel['did']));
}
}
*/
if (module_exists('views')) {
$result = db_query("SELECT vid, display_options FROM {views_display} WHERE display_plugin = '%s'", 'page');
while ($view = db_fetch_object($result)) {
$display_options = unserialize($view->display_options);
$pos = strpos($_GET['q'], $display_options['path']);
if ($pos === 0) {
return array('type' => 'views', 'ids' => array($view->vid));
}
}
}
switch (arg(0)) {
case 'node':
// Node paths: node/$nid
if (is_numeric(arg(1)) && !arg(2)) {
return array('type' => 'node', 'ids' => arg(1));
}
break;
case 'taxonomy':
// Taxonomy paths: term/$tid , term/$tid1+$tid2 , vocabulary/$vid
if (arg(1) == 'term' || arg(1) == 'vocabulary') {
$ids = preg_split('![+, ]!', arg(2));
if (count($ids)) {
return array('type' => arg(1), 'ids' => $ids);
}
}
break;
case 'forum':
// Forum paths: forum/$tid , forum/
if (is_numeric(arg(1))) {
return array('type' => 'term', 'ids' => arg(1));
}
elseif (is_null(arg(1))) {
return array('type' => 'vocabulary', 'ids' => variable_get('forum_nav_vocabulary', 0));
}
break;
case 'image':
// Image gallery paths: image/ , image/???/$tid
if (is_null(arg(1))) {
return array('type' => 'vocabulary', 'ids' => variable_get('image_gallery_nav_vocabulary', 0));
}
elseif (is_numeric(arg(2))) {
return array('type' => 'term', 'ids' => arg(2));
}
break;
case 'taxonomy_menu':
// Taxonomy menu paths: taxonomy_menu/$vid, taxonomy_menu/$vid/$tid
if (!is_null(arg(2)) && is_numeric(arg(2))) {
return array('type' => 'term', 'ids' => arg(2));
}
elseif (is_numeric(arg(1))) {
return array('type' => 'vocabulary', 'ids' => arg(1));
}
break;
case 'user':
// User page paths: user/$uid.
if (!is_null(arg(2)) && is_numeric(arg(2))) {
return array('type' => 'user', 'ids' => array(arg(1)));
}
break;
}
return array('type' => 'none', 'ids' => array());
}