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($section) {
switch ($section) {
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($may_cache) {
$items = array();
if ($may_cache) {
$items[] = array(
'title' => t('Meta tags'),
'path' => 'admin/content/nodewords',
'callback' => 'drupal_get_form',
'callback arguments' => array('nodewords_settings_form'),
'description' => t('Configure HTML meta tags for all content.'),
'access' => user_access('administer meta tags'),
'type' => MENU_NORMAL_ITEM,
);
$items[] = array(
'title' => t('Settings'),
'path' => 'admin/content/nodewords/global',
'access' => user_access('administer meta tags'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items[] = array(
'title' => t('Front page'),
'path' => 'admin/content/nodewords/frontpage',
'callback' => 'drupal_get_form',
'callback arguments' => array('nodewords_frontpage_form'),
'access' => user_access('administer meta tags'),
'type' => MENU_LOCAL_TASK,
);
}
else {
$tags = nodewords_get();
foreach ($tags as $name => $content) {
if (!empty($content)) {
drupal_set_html_head('');
}
}
}
return $items;
}
/**
* 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':
$node->nodewords = _nodewords_load('node', $node->nid);
break;
}
}
/**
* Implementation of hook_form_alter().
*/
function nodewords_form_alter($form_id, &$form) {
if (!user_access('edit meta tags')) {
return;
}
if (isset($form['type']) && $form_id == $form['type']['#value'] . '_node_form') {
$type = 'node';
$id = $form['nid']['#value'];
} else if ($form_id == 'taxonomy_form_vocabulary') {
$type = 'vocabulary';
$id = $form['vid']['#value'];
$form['submit']['#weight'] = 45;
$form['delete']['#weight'] = 50;
} else if ($form_id == 'taxonomy_form_term') {
$type = 'term';
$id = $form['tid']['#value'];
$form['submit']['#weight'] = 45;
$form['delete']['#weight'] = 50;
} else if ($form_id == 'panels_page_edit_form') {
$type = 'panels_page';
$id = $form['pid']['#value'];
$form['submit']['#weight'] = 45;
$form['#submit']['nodewords_panels_handler'] = array();
} else if ($form_id == 'panels_page_delete_confirm') {
$form['#submit']['nodewords_panels_handler'] = array();
} else if ($form_id == 'views_edit_view') {
$type = 'views';
$id = $form['vid']['#value'];
$form['save']['#weight'] = 40;
$form['save_and_edit']['#weight'] = 45;
if (isset($form['delete'])) {
$form['delete']['#weight'] = 50;
}
$form['cancel']['#weight'] = 55;
$form['#submit']['nodewords_views_handler'] = array();
} else if ($form_id == 'views_ui_admin_delete_confirm') {
$form['#submit'] = array_merge(array('nodewords_views_handler' => array()), (array)$form['#submit']);
}
if (isset($type)) {
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;
}
}
/************************************************************************
* Menu callback: settings form.
************************************************************************/
function nodewords_settings_form() {
$settings = _nodewords_get_settings();
$form = array();
$form['nodewords'] = array(
'#tree' => TRUE,
);
$form['nodewords']['global'] = array(
'#tree' => TRUE,
);
$form['nodewords']['global']['copyright'] = array(
'#type' => 'textfield',
'#title' => t('Global copyright'),
'#default_value' => $settings['global']['copyright'],
'#size' => 60,
'#maxlength' => $settings['max_size'],
'#description' => t('Enter a short copyright statement (one line) that will be used on all pages unless specifically set.'),
);
$form['nodewords']['global']['geourl'] = array(
'#type' => 'textfield',
'#title' => t('Global GeoURL'),
'#default_value' => $settings['global']['geourl'],
'#size' => 60,
'#maxlength' => $settings['max_size'],
'#description' => t('Enter a GeoURL (latitude, longitude) that will be used on all pages unless specifically set.'),
);
$form['nodewords']['global']['keywords'] = array(
'#type' => 'textfield',
'#title' => t('Global keywords'),
'#default_value' => $settings['global']['keywords'],
'#size' => 60,
'#maxlength' => $settings['max_size'],
'#description' => t('Enter a comma separated list of global keywords. These global keywords will be added after the page-specific keywords on all pages.'),
);
if (function_exists('taxonomy_get_vocabularies')) {
$select = array();
foreach (taxonomy_get_vocabularies() as $vocabulary) {
$select[$vocabulary->vid] = check_plain($vocabulary->name);
}
if (count($select) > 0) {
$form['nodewords']['keywords_vids'] = array(
'#type' => 'select',
'#title' => t('Auto-keywords vocabularies'),
'#default_value' => $settings['keywords_vids'],
'#options' => $select,
'#description' => t('Select the vocabularies which contain terms you want to add to the keywords meta tag for nodes. The terms of these vocabularies are added before the global keywords but after the page-specific keywords.'),
'#multiple' => TRUE,
);
}
}
$form['nodewords']['use_teaser'] = array(
'#type' => 'checkbox',
'#title' => t('Use the teaser of the page if the meta description is not set.'),
'#default_value' => $settings['use_teaser'],
);
$form['nodewords']['global']['robots'] = array(
'#type' => 'select',
'#title' => t('Default robots meta tag'),
'#options' => array(
'index,follow' => 'ALL=INDEX,FOLLOW',
'noindex,follow' => 'NOINDEX,FOLLOW',
'index,nofollow' => 'INDEX,NOFOLLOW',
'noindex,nofollow' => 'NONE=NOINDEX,NOFOLLOW',
),
'#multiple' => FALSE,
'#default_value' => $settings['global']['robots'],
'#description' => t('The ROBOTS meta tag offers a simple mechanisme to indicate to web robots and crawlers wether the page should be indexed (INDEX or NOINDEX) and whether links on the page should be followed (FOLLOW or NOFOLLOW). Here you can enter the default robots meta tag to use for all pages. If unsure, select "ALL=INDEX,FOLLOW".'),
);
$form['nodewords']['max_size'] = array(
'#type' => 'textfield',
'#title' => t('Text length'),
'#default_value' => $settings['max_size'],
'#size' => 6,
'#maxlength' => 6,
'#description' => t('The maximum number of characters the content of a meta tag can contain.'),
);
$form['nodewords']['edit'] = array(
'#type' => 'fieldset',
'#title' => t('Tags to show on edit form'),
'#tree' => TRUE,
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#description' => t('Select the meta tags you want to be able to edit on the edit page of nodes, terms and vocabularies.'),
);
$form['nodewords']['head'] = array(
'#type' => 'fieldset',
'#title' => t('Tags to output in html head'),
'#tree' => TRUE,
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#description' => t('Select the meta tags you want to appear in the HEAD section of the HTML pages.'),
);
foreach (_nodewords_get_possible_tags() as $name) {
foreach (array('edit', 'head') as $where) {
$form['nodewords'][$where][$name] = array(
'#type' => 'checkbox',
'#title' => ucfirst($name), //TODO: is this UTF8 safe?
'#default_value' => $settings[$where][$name],
);
}
}
return system_settings_form($form);
}
/************************************************************************
* Menu callback: front page settings form.
************************************************************************/
function nodewords_frontpage_form() {
$tags = _nodewords_load('page', '');
$form = array();
$form['nodewords'] = _nodewords_form('page', $tags);
unset($form['nodewords']['#title']);
unset($form['nodewords']['#type']);
if (empty($form['nodewords'])) {
$form['nodewords'] = array('#value' => t('Currently no meta tags can be assigned to the front page because you have disabled all tags to show on the edit forms. Enable some meta tags to show on edit forms first.', array('!nodewords-settings-url' => url('admin/content/nodewords'))));
}
else {
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
}
return $form;
}
function nodewords_frontpage_form_submit($form_id, $form_values) {
if ($form_values['op'] == t('Submit')) {
_nodewords_set('page', '', $form_values['nodewords']);
drupal_set_message(t('The meta tags for the front page have been saved.'));
}
}
/************************************************************
* Handling of views and panels_page.
************************************************************/
/**
* Handling of edit/delete form for panels_page.
*/
function nodewords_panels_handler($form_id, $form_values) {
switch ($_POST['op']) {
case t('Save'):
if (isset($form_values['nodewords']) && user_access('administer meta tags')) {
_nodewords_set('panels_page', $form_values['pid'], $form_values['nodewords']);
}
break;
case t('Delete'):
if ($form_values['confirm']) {
_nodewords_delete('panels_page', $form_values['pid']);
}
break;
}
}
/**
* Handling of edit/delete form for views.
*/
function nodewords_views_handler($form_id, $form_values) {
switch ($_POST['op']) {
case t('Save'):
case t('Save and edit'):
if (isset($form_values['nodewords']) && user_access('administer meta tags')) {
if ($form_values['vid'] > 0) {
_nodewords_set('views', $form_values['vid'], $form_values['nodewords']);
}
else if ($result = db_fetch_object(db_query("SELECT * FROM {view_view} WHERE url = '%s'", $form_values['url']))) {
_nodewords_set('views', $result->vid, $form_values['nodewords']);
}
}
break;
case t('Delete'):
if ($form_values['confirm']) {
_nodewords_delete('views', $form_values['vid']);
}
break;
}
}
/************************************************************
* Theming functions.
************************************************************/
/**
* 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
* - 'page' => array of 'path' of the displayed page
* - 'term' => array of 'tid' of the term
* - 'vocabulary' => array of 'vid' of the vocabulary
* - 'panels_page' => array of 'pid' 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;
}
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_num_rows($result) == 0) {
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)) {
$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,
'keywords_vids' => array(),
'global' => array('copyright' => '',
'geourl' => '',
'keywords' => '',
'robots' => 'index,follow'),
'head' => array('abstract' => 1,
'copyright' => 1,
'description' => 1,
'geourl' => 1,
'keywords' => 1,
'robots' => 1),
'edit' => array('abstract' => 0,
'copyright' => 0,
'description' => 1,
'geourl' => 0,
'keywords' => 1,
'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;
}
else if (!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') . '/tags';
$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.
************************************************************/
/*
* 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();
/* Prepare the tags */
foreach (_nodewords_get_possible_tags() as $tag) {
$function = 'nodewords_'. $tag .'_prepare';
if (function_exists($function)) {
$tags[$tag] = $function($type, $ids, $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;
}
else if ($type == 'panels_page' && count($ids) == 1) {
$panel = db_fetch_object(db_query("SELECT * FROM {panels_page} WHERE pid = %d", $ids[0]));
if ($panel) {
$tags['DC.title'] = $panel_page->title;
}
}
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) {
$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'];
$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?
}
}
return $text;
}
/*
* Try to guess the $type and $ids by looking at $_GET['q'].
*/
function _nodewords_detect_type_and_ids() {
if ($_GET['q'] == variable_get('site_frontpage', 'node')) {
return array('type' => 'page', 'ids' => array(''));
}
switch (arg(0)) {
case 'node':
// Node paths: node/$nid
if (is_numeric(arg(1))) {
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));
}
else if (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));
}
else if (is_numeric(arg(2))) {
return array('type' => 'term', 'ids' => arg(2));
}
break;
case 'taxonomy_menu':
// Taxonomy menu paths: taxonomy_menu/$tid
if (is_numeric(arg(1))) {
return array('type' => 'term', 'ids' => arg(1));
}
break;
}
if (module_exists('panels_page')) {
if ($result = db_fetch_array(db_query("SELECT * FROM {panels_page} WHERE path = '%s'", $_GET['q']))) {
return array('type' => 'panels_page', 'ids' => array($result['pid']));
}
}
if (module_exists('views')) {
if (($result = db_fetch_array(db_query("SELECT * FROM {view_view} WHERE url LIKE '%s%%' AND page = 1", $_GET['q'])))) {
return array('type' => 'views', 'ids' => array($result['vid']));
}
}
return array('type' => 'none', 'ids' => array());
}