/[drupal]/contributions/modules/nodewords/nodewords.module
ViewVC logotype

Contents of /contributions/modules/nodewords/nodewords.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.66 - (show annotations) (download) (as text)
Sun Oct 25 11:29:21 2009 UTC (4 weeks, 6 days ago) by kiam
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-6--3
Changes since 1.65: +22 -9 lines
File MIME type: text/x-php
By KiamLaLuno: Added a warning shown when "Meta Tags by path" is enabled.
1 <?php
2 // $Id: nodewords.module,v 1.65 2009/10/25 08:21:19 kiam Exp $
3
4 /**
5 * @file
6 * Assigns META tags to nodes, vocabularies, terms and pages.
7 */
8
9 /**
10 * @addtogroup nodewords
11 * @{
12 */
13
14 /*****************************************************************************
15 * Public constants.
16 ****************************************************************************/
17
18 /**
19 * The type of objects to which the meta tags are associated.
20 */
21 define('NODEWORDS_MT_TYPE_DEFAULT', 1);
22 define('NODEWORDS_MT_TYPE_ERRORPAGE', 2);
23 define('NODEWORDS_MT_TYPE_FRONTPAGE', 3);
24 define('NODEWORDS_MT_TYPE_PAGER', 4);
25 define('NODEWORDS_MT_TYPE_NODE', 5);
26 define('NODEWORDS_MT_TYPE_TERM', 6);
27 define('NODEWORDS_MT_TYPE_TRACKER', 7);
28 define('NODEWORDS_MT_TYPE_USER', 8);
29 define('NODEWORDS_MT_TYPE_VOCABULARY', 9);
30
31 /**
32 * The types of meta tags the module is able to handle.
33 */
34 define('NODEWORDS_META', 0);
35 define('NODEWORDS_HTTP_EQUIV', 1);
36 define('NODEWORDS_LINK_REL', 2);
37 define('NODEWORDS_LINK_REV', 3);
38
39 /**
40 * The current API version implemented.
41 */
42 define('NODEWORDS_API_VERSION', 1.1);
43
44 /*****************************************************************************
45 * Drupal hooks.
46 ****************************************************************************/
47
48 /**
49 * Implementation of hook_content_extra_fields().
50 * Allow the meta tags fields to be sorted in the node edit forms.
51 */
52 function nodewords_content_extra_fields() {
53 $extras = array();
54
55 $extras['nodewords'] = array(
56 'label' => t('Meta tags'),
57 'description' => t('Meta tags fieldset.'),
58 'weight' => 10,
59 );
60
61 return $extras;
62 }
63
64 /**
65 * Implementation of hook_form_alter().
66 */
67 function nodewords_form_alter(&$form, &$form_state, $form_id) {
68 $bool = (
69 isset($form['type']) &&
70 isset($form['type']['#value']) &&
71 $form_id == $form['type']['#value'] . '_node_form' &&
72 variable_get('nodewords_edit_metatags_' . $form['type']['#value'], TRUE)
73 );
74
75 if ($bool) {
76 $id = $form['nid']['#value'];
77 if (!empty($form_state['values']['nodewords'])) {
78 $tags = $form_state['values']['nodewords'];
79 }
80 elseif (isset($id) && is_numeric($id)) {
81 $tags = nodewords_load_tags(NODEWORDS_MT_TYPE_NODE, $id);
82 }
83 else {
84 $tags = array();
85 }
86
87 $form['nodewords'] = nodewords_form(
88 NODEWORDS_MT_TYPE_NODE,
89 $tags,
90 array(
91 'page:permissions:additional' => 'administer nodes',
92 'tag_options' => array('node_type' => $form['type']['#value']),
93 )
94 );
95 }
96 }
97
98 /**
99 * Implementation of hook_form_FORM_ID_alter().
100 */
101 function nodewords_form_node_type_form_alter(&$form, &$form_state) {
102 if (isset($form['#node_type'])) {
103
104 $form['nodewords'] = array(
105 '#type' => 'fieldset',
106 '#title' => t('Meta tags settings'),
107 '#collapsible' => TRUE,
108 '#collapsed' => TRUE,
109 );
110
111 $form['nodewords']['nodewords_edit_metatags'] = array(
112 '#type' => 'checkbox',
113 '#title' => t('Allow editing of meta tags'),
114 '#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.'),
115 '#default_value' => variable_get('nodewords_edit_metatags_' . $form['#node_type']->type, TRUE),
116 );
117
118 foreach (nodewords_get_possible_tags() as $name => $info) {
119 $function = $info['tag:function:prefix'] . '_settings_form';
120 $options = array(
121 'parameters' => !empty($info['tag:function:parameters']) ? $info['tag:function:parameters'] : array(),
122 );
123
124 if (function_exists($function)) {
125 $function($form, 'node_type_form', $options);
126 }
127 }
128 }
129 }
130
131 /**
132 * Implementation of hook_form_FORM_ID_alter().
133 */
134 function nodewords_form_taxonomy_form_term_alter(&$form, &$form_state) {
135 $bool = (isset($form['tid']['#value']) &&
136 !isset($form_state['confirm_delete']) &&
137 !isset($form_state['confirm_parents'])
138 );
139
140 if ($bool) {
141 $id = $form['tid']['#value'];
142
143 if (!empty($form_state['values']['nodewords'])) {
144 $tags = $form_state['values']['nodewords'];
145 }
146 elseif (isset($id) && is_numeric($id)) {
147 $tags = nodewords_load_tags(NODEWORDS_MT_TYPE_TERM, $id);
148 }
149 else {
150 $tags = array();
151 }
152
153 $form['nodewords'] = nodewords_form(
154 NODEWORDS_MT_TYPE_TERM,
155 $tags,
156 array()
157 );
158
159 $form['submit']['#weight'] = 45;
160 $form['delete']['#weight'] = 50;
161 }
162 }
163
164 /**
165 * Implementation of hook_form_FORM_ID_alter().
166 */
167 function nodewords_form_taxonomy_form_vocabulary_alter(&$form, &$form_state) {
168 if (isset($form['vid']['#value'])) {
169 $id = $form['vid']['#value'];
170
171 if (!empty($form_state['values']['nodewords'])) {
172 $tags = $form_state['values']['nodewords'];
173 }
174 elseif (isset($id) && is_numeric($id)) {
175 $tags = nodewords_load_tags(NODEWORDS_MT_TYPE_VOCABULARY, $id);
176 }
177 else {
178 $tags = array();
179 }
180
181 $form['nodewords'] = nodewords_form(
182 NODEWORDS_MT_TYPE_VOCABULARY,
183 $tags,
184 array()
185 );
186
187 $form['submit']['#weight'] = 45;
188 $form['delete']['#weight'] = 50;
189 }
190 }
191
192 /**
193 * Implemenation of hook_help().
194 */
195 function nodewords_help($path, $arg) {
196 switch ($path) {
197 case 'admin/content/nodewords/meta-tags':
198 $output = '<p>' . t('On this page you can enter the default values for the meta tags of your site.') . '</p>';
199 break;
200
201 case 'admin/content/nodewords/meta-tags/errorpage_403':
202 $output = '<p>' . t('On this page you can enter the meta tags for the <q>access denied</q> error page of your site.') . '</p>';
203 break;
204
205 case 'admin/content/nodewords/meta-tags/errorpage_404':
206 $output = '<p>' . t('On this page you can enter the meta tags for the <q>page not found</q> error page of your site.') . '</p>';
207 break;
208
209 case 'admin/content/nodewords/meta-tags/frontpage':
210 $output = '<p>' . t('On this page you can enter the meta tags for the front page of your site.') . '</p>';
211 break;
212
213 case 'admin/content/nodewords/meta-tags/other':
214 $output = '<p>' . t('On this page you can enter the meta tags for other pages of your site. <a href="@add_metatags">Add meta tags</a> for a new page.', array('@add_metatags' => url('admin/content/nodewords/meta-tags/other/add'))) . '</p>';
215 break;
216
217 case 'admin/content/nodewords/meta-tags/pager':
218 $output = '<p>' . t('On this page you can enter the meta tags for pages that are part of a pager. This values will be used only when the option <q>%option</q> in the <a href="@settings_page">settings page</a> is not selected.', array('%option' => t('Repeat meta tags for lists'), '@settings_page' => url('admin/content/nodewords/settings'))) . '</p>';
219 break;
220
221 case 'admin/content/nodewords/meta-tags/tracker':
222 $output = '<p>' . t('On this page you can enter the meta tags for tracker pages of your site.') . '</p>';
223 break;
224
225 default:
226 $output = '';
227 break;
228 }
229
230 return $output;
231 }
232
233 /**
234 * Implementation of hook_menu().
235 */
236 function nodewords_menu() {
237 $admin_access = array('administer meta tags');
238 $items = array();
239
240 $items['admin/content/nodewords'] = array(
241 'title' => 'Meta tags',
242 'page callback' => 'drupal_get_form',
243 'page arguments' => array('nodewords_settings_form'),
244 'description' => 'Configure HTML meta tags for all the content.',
245 'access arguments' => $admin_access,
246 'type' => MENU_NORMAL_ITEM,
247 'file' => 'nodewords.admin.inc',
248 );
249
250 $items['admin/content/nodewords/settings'] = array(
251 'title' => 'Settings',
252 'access arguments' => $admin_access,
253 'type' => MENU_DEFAULT_LOCAL_TASK,
254 'weight' => -1,
255 'file' => 'nodewords.admin.inc',
256 );
257
258 $items['admin/content/nodewords/meta-tags'] = array(
259 'title' => 'Default and specific meta tags',
260 'page callback' => 'drupal_get_form',
261 'page arguments' => array('nodewords_tags_form'),
262 'access arguments' => $admin_access,
263 'type' => MENU_LOCAL_TASK,
264 'file' => 'nodewords.admin.inc',
265 );
266
267 $items['admin/content/nodewords/meta-tags/default'] = array(
268 'title' => 'Default values',
269 'access arguments' => $admin_access,
270 'type' => MENU_DEFAULT_LOCAL_TASK,
271 'file' => 'nodewords.admin.inc',
272 );
273
274 $items['admin/content/nodewords/meta-tags/errorpage_403'] = array(
275 'title' => 'Error 403 page',
276 'page callback' => 'drupal_get_form',
277 'page arguments' => array('nodewords_tags_form', NODEWORDS_MT_TYPE_ERRORPAGE, '403'),
278 'access arguments' => $admin_access,
279 'type' => MENU_LOCAL_TASK,
280 'file' => 'nodewords.admin.inc',
281 );
282
283 $items['admin/content/nodewords/meta-tags/errorpage_404'] = array(
284 'title' => 'Error 404 page',
285 'page callback' => 'drupal_get_form',
286 'page arguments' => array('nodewords_tags_form', NODEWORDS_MT_TYPE_ERRORPAGE, '404'),
287 'access arguments' => $admin_access,
288 'type' => MENU_LOCAL_TASK,
289 'file' => 'nodewords.admin.inc',
290 );
291
292 $items['admin/content/nodewords/meta-tags/frontpage'] = array(
293 'title' => 'Front page',
294 'page callback' => 'drupal_get_form',
295 'page arguments' => array('nodewords_tags_form', NODEWORDS_MT_TYPE_FRONTPAGE),
296 'access arguments' => $admin_access,
297 'type' => MENU_LOCAL_TASK,
298 'file' => 'nodewords.admin.inc',
299 );
300
301 $items['admin/content/nodewords/meta-tags/pager'] = array(
302 'title' => 'Pager',
303 'page callback' => 'drupal_get_form',
304 'page arguments' => array('nodewords_tags_form', NODEWORDS_MT_TYPE_PAGER),
305 'access arguments' => $admin_access,
306 'type' => MENU_LOCAL_TASK,
307 'file' => 'nodewords.admin.inc',
308 );
309
310 $items['admin/content/nodewords/meta-tags/tracker'] = array(
311 'title' => 'Tracker pages',
312 'page callback' => 'drupal_get_form',
313 'page arguments' => array('nodewords_tags_form', NODEWORDS_MT_TYPE_TRACKER),
314 'access arguments' => $admin_access,
315 'type' => MENU_LOCAL_TASK,
316 'file' => 'nodewords.admin.inc',
317 );
318
319 $items['admin/content/nodewords/meta-tags/other'] = array(
320 'title' => 'Other pages',
321 'page callback' => 'drupal_get_form',
322 'page arguments' => array('nodewords_pages_overview'),
323 'access arguments' => $admin_access,
324 'type' => MENU_LOCAL_TASK,
325 'weight' => 5,
326 'file' => 'nodewords.admin.inc',
327 );
328
329 $items['admin/content/nodewords/meta-tags/other/add'] = array(
330 'title' => 'Add page meta tags',
331 'page callback' => 'drupal_get_form',
332 'page arguments' => array('nodewords_pages_edit'),
333 'access arguments' => $admin_access,
334 'type' => MENU_CALLBACK,
335 'file' => 'nodewords.admin.inc',
336 );
337
338 $items['admin/content/nodewords/meta-tags/other/delete/%nodewords_page'] = array(
339 'title' => 'Delete page meta tags',
340 'page callback' => 'drupal_get_form',
341 'page arguments' => array('nodewords_pages_confirm_delete', 6),
342 'access arguments' => $admin_access,
343 'parent' => 'admin/content/nodewords/meta-tags/other',
344 'type' => MENU_CALLBACK,
345 'file' => 'nodewords.admin.inc',
346 );
347
348 $items['admin/content/nodewords/meta-tags/other/edit/%nodewords_page'] = array(
349 'title' => 'Edit page meta tags',
350 'page callback' => 'drupal_get_form',
351 'page arguments' => array('nodewords_pages_edit', 6),
352 'access arguments' => $admin_access,
353 'parent' => 'admin/content/nodewords/meta-tags/other',
354 'type' => MENU_CALLBACK,
355 'file' => 'nodewords.admin.inc',
356 );
357
358 return $items;
359 }
360
361 /**
362 * Implementation of hook_node_operations().
363 */
364 function nodewords_node_operations() {
365 $operations = array(
366 'delete_metatags' => array(
367 'label' => t('Delete meta tags'),
368 'callback' => 'nodewords_mass_delete_tags',
369 'callback arguments' => array('type' => NODEWORDS_MT_TYPE_NODE),
370 ),
371 );
372
373 return $operations;
374 }
375
376 /**
377 * Implementation of hook_nodeapi().
378 */
379 function nodewords_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
380 switch ($op) {
381 case 'delete':
382 nodewords_delete_tags(NODEWORDS_MT_TYPE_NODE, $node->nid);
383 break;
384
385 case 'insert':
386 case 'update':
387 if (isset($node->nodewords)) {
388 nodewords_save_tags(NODEWORDS_MT_TYPE_NODE, $node->nid, $node->nodewords, TRUE);
389 }
390 break;
391
392 case 'update index':
393 $output = '';
394
395 if (isset($node->nodewords)) {
396 $tags_info = nodewords_get_possible_tags();
397
398 foreach ($node->nodewords as $name => $content) {
399 // Avoid the disabled meta tags.
400 if (!isset($info[$name])) {
401 continue;
402 }
403
404 if (!empty($tags_info[$name]['tag:template:index'])) {
405 $output .= strtr($tags_info[$name]['tag:template:index'], '%content', $content);
406 }
407 }
408 }
409
410 return $output;
411
412 case 'load':
413 return array(
414 'nodewords' => nodewords_load_tags(NODEWORDS_MT_TYPE_NODE, $node->nid),
415 );
416 }
417 }
418
419 /**
420 * Implementation of hook_perm().
421 */
422 function nodewords_perm() {
423 return array('administer meta tags');
424 }
425
426 /**
427 * Implementation of hook_preprocess_page().
428 */
429 function nodewords_preprocess_page(&$variables) {
430 list($type, $ids) = _nodewords_detect_type_and_ids();
431 drupal_set_html_head(nodewords_output_tags(nodewords_get_tags($type, $ids)));
432
433 $variables['head'] = drupal_get_html_head();
434 }
435
436 /**
437 * Implementation of hook_taxonomy().
438 */
439 function nodewords_taxonomy($op, $type, $object = NULL) {
440 if ($type == 'term') {
441 $id = $object['tid'];
442 $type = NODEWORDS_MT_TYPE_TERM;
443 }
444 elseif ($type == 'vocabulary') {
445 $id = $object['vid'];
446 $type = NODEWORDS_MT_TYPE_VOCABULARY;
447 }
448 else {
449 return;
450 }
451
452 switch ($op) {
453 case 'delete':
454 nodewords_delete_tags($type, $id);
455 break;
456
457 case 'insert':
458 case 'update':
459 if (isset($object['nodewords'])) {
460 nodewords_save_tags($type, $id, $object['nodewords'], TRUE);
461 }
462 break;
463 }
464 }
465
466 /**
467 * Implementation of hook_theme().
468 */
469 function nodewords_theme() {
470 return array(
471 'nodewords_pages_overview' => array(
472 'arguments' => array('form' => array()),
473 ),
474 );
475 }
476
477 /**
478 * Implementation of hook_user().
479 */
480 function nodewords_user($op, &$edit, &$account, $category = NULL) {
481 switch ($op) {
482 case 'load':
483 if (variable_get('nodewords_enable_user_metatags', TRUE)) {
484 $account->nodewords = nodewords_load_tags(NODEWORDS_MT_TYPE_USER, $account->uid);
485 }
486 break;
487
488 case 'delete':
489 nodewords_delete_tags(NODEWORDS_MT_TYPE_USER, $account->uid);
490 break;
491
492 case 'insert':
493 case 'update':
494 if (isset($edit['nodewords'])) {
495 nodewords_save_tags(NODEWORDS_MT_TYPE_USER, $account->uid, $edit['nodewords'], TRUE);
496 }
497 break;
498
499 case 'form':
500 if ($category == 'account') {
501 if (variable_get('nodewords_enable_user_metatags', TRUE)) {
502 $tags = nodewords_load_tags(NODEWORDS_MT_TYPE_USER, $account->uid);
503 $form['nodewords'] = nodewords_form(
504 NODEWORDS_MT_TYPE_USER,
505 $tags,
506 array(
507 'page:permissions:additional' => 'administer users',
508 )
509 );
510
511 return $form;
512 }
513 }
514 break;
515 }
516 }
517
518 /**
519 * Implementation of hook_user_operations().
520 */
521 function nodewords_user_operations() {
522 $operations = array(
523 'delete_metatags' => array(
524 'label' => t('Delete meta tags'),
525 'callback' => 'nodewords_mass_delete_tags',
526 'callback arguments' => array('type' => NODEWORDS_MT_TYPE_USER),
527 ),
528 );
529
530 return $operations;
531 }
532
533 /*****************************************************************************
534 * Public functions.
535 ****************************************************************************/
536
537 /**
538 * Delete tags from table.
539 */
540 function nodewords_delete_tags($type, $id) {
541 db_query("DELETE FROM {nodewords} WHERE type = %d AND id = '%s'", $type, $id);
542
543 if ($type == NODEWORDS_MT_TYPE_PAGE) {
544 db_query("DELETE FROM {nodewords_custom} WHERE path = '%s'", $id);
545 }
546 }
547
548 /**
549 * Return the form used to set the meta tags values.
550 *
551 * @param $type
552 * The object to which the meta tags are associated (node, user, taxonomy
553 * term, etc...).
554 * @param $tags
555 * The meta tags array as returned by nodewords_load_tags().
556 *
557 * @return
558 * An array as requested by the form API.
559 */
560 function nodewords_form($type, $tags, $options = array()) {
561 $default_options = array(
562 'fieldset' => TRUE,
563 'fieldset:title' => t('Meta tags'),
564 'fieldset:weight' => 20,
565 );
566 $edit_tags = variable_get('nodewords_edit', array());
567 $form = array();
568 $options += $default_options;
569 $tag_options = array(
570 'default' => nodewords_load_tags(),
571 'type' => $type,
572 );
573 $tags_info = nodewords_get_possible_tags();
574 $token_module_enabled = module_exists('token');
575 $tokens_support = FALSE;
576
577 if (isset($options['tag_options'])) {
578 $tag_options = array_merge($options['tag_options'], $tag_options);
579 }
580
581 foreach ($tags_info as $name => $info) {
582 if (empty($edit_tags[$name])) {
583 continue;
584 }
585
586 if (!empty($info['tag:context:allowed'])) {
587 $bool = (
588 in_array('<none>', $info['tag:context:allowed']) ||
589 !in_array($type, $info['tag:context:allowed'])
590 );
591
592 if ($bool) {
593 continue;
594 }
595 }
596 elseif (!empty($info['tag:context:denied'])) {
597 if (in_array($type, $info['tag:context:denied'])) {
598 continue;
599 }
600 }
601
602 $bool = (
603 (
604 !empty($options['page:permissions:additional']) &&
605 user_access($options['page:permissions:additional'])
606 ) ||
607 (
608 !empty($info['widget:permission']) &&
609 user_access($info['widget:permission'])
610 )
611 );
612
613 if ($bool) {
614 if (function_exists($function = $info['tag:function:prefix'] . '_form')) {
615 $tag_options['parameters'] = !empty($info['tag:function:parameters']) ? $info['tag:function:parameters'] : array();
616
617 $function($form, isset($tags[$name]) ? $tags[$name] : array(), $tag_options);
618 }
619 }
620 }
621
622 if (!empty($form)) {
623 _nodewords_cmp_form_fields($form, 0, TRUE);
624 uksort($form, '_nodewords_cmp_form_fields');
625
626 if ($options['fieldset']) {
627 $collapsed = FALSE;
628
629 foreach (element_children($form) as $id) {
630 if ($form[$id]['#type'] == 'fieldset') {
631 if (!empty($form[$id]['#collapsed'])) {
632 $collapsed = TRUE;
633 }
634
635 if ($token_module_enabled && !empty($tags_info[$id]['tag:tokens'])) {
636 $tokens_support = TRUE;
637 $tokens_support_str = t('This meta tag supports tokens.');
638 if (empty($form[$id]['#description'])) {
639 $form[$id]['#description'] = $tokens_support_str;
640 }
641 else {
642 $form[$id]['#description'] .= ' ' . $tokens_support_str;
643 }
644 }
645 }
646 }
647
648 if ($tokens_support && type != NODEWORDS_MT_TYPE_DEFAULT) {
649 switch ($type) {
650 case NODEWORDS_MT_TYPE_ERRORPAGE:
651 case NODEWORDS_MT_TYPE_FRONTPAGE:
652 case NODEWORDS_MT_TYPE_PAGER:
653 case NODEWORDS_INSTALL_MT_TYPE_TRACKER:
654 $token_type = 'global';
655 break;
656
657 case NODEWORDS_MT_TYPE_NODE:
658 $token_type = 'node';
659 break;
660
661 case NODEWORDS_MT_TYPE_USER:
662 $token_type = 'user';
663 break;
664
665 default:
666 $token_type = '';
667 break;
668 }
669
670 if (!empty($token_type)) {
671 $form['nodewords_token_help'] = array(
672 '#title' => t('Replacement patterns'),
673 '#type' => 'fieldset',
674 '#collapsible' => TRUE,
675 '#collapsed' => TRUE,
676 '#description' => t('Prefer raw-text replacements for text to avoid problems with HTML entities!'),
677 );
678
679 $form['nodewords_token_help']['help'] = array(
680 '#value' => theme('token_help', 'node'),
681 );
682 }
683 }
684
685 $form['#type'] = 'fieldset';
686 $form['#title'] = $options['fieldset:title'];
687 $form['#tree'] = TRUE;
688 $form['#collapsible'] = TRUE;
689 $form['#collapsed'] = variable_get('nodewords_collapse_fieldset', FALSE) || $collapsed;
690 $form['#weight'] = $options['fieldset:weight'];
691 }
692 }
693
694 return $form;
695 }
696
697 /**
698 * Return a list of possible output tags.
699 *
700 * @param $remove_disabled
701 * If TRUE, disabled meta tags will not be returned.
702 *
703 * @return
704 * An array containing the list of meta tags.
705 */
706 function nodewords_get_possible_tags() {
707 static $tags_info;
708
709 if (!isset($tags_info)) {
710 $tags_info = array();
711
712 // Allow third-party modules to alter the meta tags list, or to add new
713 // meta tags.
714 foreach (module_implements('nodewords_tags_info') as $module) {
715 if (module_hook($module, 'nodewords_api')) {
716 $result = module_invoke($module, 'nodewords_api');
717
718 if (isset($result)) {
719 if (is_numeric($result)) {
720 if ($result <> NODEWORDS_API_VERSION) {
721 continue;
722 }
723 }
724 elseif (is_array($result) && isset($result['version'])) {
725 if ($result['version'] <> NODEWORDS_API_VERSION) {
726 continue;
727 }
728 }
729 else {
730 continue;
731 }
732 }
733 else {
734 continue;
735 }
736
737 $result = module_invoke($module, 'nodewords_tags_info');
738
739 if (isset($result) && is_array($result)) {
740 $tags_info = array_merge($tags_info, $result);
741 }
742 }
743 }
744 }
745
746 return $tags_info;
747 }
748
749 /**
750 * Get the defined meta tags for $type / $id.
751 *
752 * @param $type
753 * Realm of the object the meta tags are associated with.
754 * This is one of the following: NODEWORDS_MT_TYPE_ERRORPAGE,
755 NODEWORDS_MT_TYPE_FRONTPAGE, NODEWORDS_MT_TYPE_NODE,
756 * NODEWORDS_MT_TYPE_PAGE, NODEWORDS_MT_TYPE_PAGER, NODEWORDS_MT_TYPE_TERM,
757 NODEWORDS_MT_TYPE_TRACKER, NODEWORDS_MT_TYPE_VOCABULARY.
758 * @param $ids
759 * ID of the object to get the meta tags from.
760 * This is one of the following:
761 * - NODEWORDS_MT_TYPE_ERRORPAGE => an array containing the HTML error code
762 (403 or 404).
763 * - NODEWORDS_MT_TYPE_FRONTPAGE => none.
764 * - NODEWORDS_MT_TYPE_NODE => an array containing the node ID.
765 * - NODEWORDS_MT_TYPE_PAGE => an array containing the page path.
766 * - NODEWORDS_MT_TYPE_TERM => an array of term IDs.
767 * - NODEWORDS_MT_TYPE_USER => an array containing the user ID.
768 * - NODEWORDS_MT_TYPE_VOCABULARY => an array of vocabulary IDs.
769 * @param $filtered
770 * If TRUE, only the meta tags that the user configured for
771 * output will be returned.
772 * If FALSE, all meta tags will be returned.
773 *
774 * @return
775 * An associative array of the defined meta tags.
776 */
777 function nodewords_get_tags($type, $ids = array(''), $filtered = TRUE) {
778 $output_tags = array();
779 $tag_options = array(
780 'default' => nodewords_load_tags(),
781 'ids' => $ids,
782 'type' => $type,
783 );
784
785 if (!is_array($ids)) {
786 $ids = array($ids);
787 }
788
789 if ($type == NODEWORDS_MT_TYPE_PAGER) {
790 foreach (nodewords_get_possible_tags() as $name => $info) {
791 if (!empty($info['tag:pager']) && function_exists($function = $info['tag:function:prefix'] . '_prepare')) {
792 $tag_options['parameters'] = !empty($info['tag:function:parameters']) ? $info['tag:function:parameters'] : array();
793
794 $function($output_tags, array(), $tag_options);
795 }
796 }
797 }
798 else {
799 // User profiles metatags are not enabled.
800 if ($type == NODEWORDS_MT_TYPE_USER && !variable_get('nodewords_enable_user_metatags', TRUE)) {
801 $tags = array();
802 }
803 // Load the values from the database
804 elseif (count($ids) == 1 && ($type != NODEWORDS_MT_TYPE_NODE || node_access('view', node_load($ids[0])))) {
805 $tags = nodewords_load_tags($type, $ids[0]);
806 }
807 else {
808 $tags = array();
809 }
810
811 // Prepare the tags.
812 foreach (nodewords_get_possible_tags() as $name => $info) {
813 if (function_exists($function = $info['tag:function:prefix'] . '_prepare')) {
814 $tag_options['parameters'] = !empty($info['tag:function:parameters']) ? $info['tag:function:parameters'] : array();
815
816 $function($output_tags, isset($tags[$name]) ? $tags[$name] : array(), $tag_options);
817 }
818 }
819 }
820
821 // Filter out tags the user has chosen not to see
822 if ($filtered) {
823 foreach (variable_get('nodewords_head', array()) as $name => $viewable) {
824 if (!$viewable) {
825 unset($output_tags[$name]);
826 }
827 }
828 }
829
830 return $output_tags;
831 }
832
833 /**
834 * Return the term object matching a term ID. This is a modified version of
835 * taxonomy_get_term() which uses db_rewrite_sql().
836 *
837 * @param $tid
838 * A term's ID.
839 *
840 * @return
841 * A term object. Results are statically cached.
842 */
843 function nodewords_get_term($tid) {
844 global $user;
845 static $previous_uid = -1, $terms = array();
846
847 if ($user->uid <> $previous_uid) {
848 $terms = array();
849 $previous_uid = $user->uid;
850 }
851
852 if (!isset($terms[$tid])) {
853 $terms[$tid] = db_fetch_object(
854 db_query(
855 db_rewrite_sql('SELECT * FROM {term_data} t WHERE t.tid = %d', 't', 'tid'),
856 $tid
857 )
858 );
859 }
860
861 return $terms[$tid];
862 }
863
864 /**
865 * Load tags from table.
866 */
867 function nodewords_load_tags($type = NODEWORDS_MT_TYPE_DEFAULT, $id = '') {
868 $result = db_query("SELECT * FROM {nodewords} WHERE type = %d AND id = '%s'", $type, $id);
869 $tags = array();
870 $tags_info = nodewords_get_possible_tags();
871
872 while ($row = db_fetch_object($result)) {
873 if (isset($tags_info[$row->name])) {
874 $tags[$row->name] = $tags_info[$row->name]['tag:db:type'] != 'string' ? unserialize($row->content) : array('value' => $row->content);
875 }
876 }
877
878 if (empty($tags) && $type == NODEWORDS_MT_TYPE_TERM) {
879 return nodewords_load_tags(NODEWORDS_MT_TYPE_VOCABULARY, db_result(db_query('SELECT vid FROM {term_data} WHERE tid = %d', $id)));
880 }
881
882 return $tags;
883 }
884
885 /**
886 * Delete the nodes meta tags.
887 *
888 * @param $ids
889 * An array of IDs.
890 * @param $type
891 * The type of the object associated with the IDs (NODEWORDS_MT_TYPE_NODE,
892 * NODEWORDS_MT_TYPE_USER, ...).
893 */
894 function nodewords_mass_delete_tags($ids, $type) {
895 db_query("DELETE FROM {nodewords} WHERE id IN (" . db_placeholders($ids, 'varchar') . ") AND type = %d",
896 array_merge($ids, array($type))
897 );
898
899 if ($type == NODEWORDS_MT_TYPE_PAGE) {
900 db_query("DELETE FROM {nodewords_custom} WHERE path IN (" . db_placeholders($ids, 'varchar') . ")",
901 $ids
902 );
903 }
904
905 drupal_set_message(t('The update has been performed.'));
906 }
907
908 /**
909 * Create the content of a meta tag from a node teaser.
910 *
911 * @param $body
912 * The node body.
913 * @param $format
914 * The format set for the node.
915 * @param $alt_attribute
916 * If TRUE, any tag img will be replaced with its attribute alt.
917 * @param $size
918 * The maximum allowed size; if it is zero, then the function will use the
919 * value of the Drupal variable nodewords_max_size.
920 *
921 * @return
922 * The string to use to populate the meta tag.
923 */
924 function nodewords_metatag_from_teaser($body, $format, $alt_attribute, $size = 0) {
925 if (!$size) {
926 $size = variable_get('nodewords_max_size', 350);
927 }
928
929 // We check for the presence of the PHP evaluator filter in the current
930 // format. If the body contains PHP code, we do not split it up to prevent
931 // parse errors.
932 if (isset($format)) {
933 $filters = filter_list_format($format);
934 if (isset($filters['php/0']) && strpos($body, '<?') !== FALSE) {
935 return '';
936 }
937 }
938
939 // If a valid delimiter has been specified, use it to chop off the teaser.
940 if (($delimiter = strpos($body, '<!--break-->')) !== FALSE) {
941 $body = substr($body, 0, $delimiter);
942 }
943
944 // Initialize the helper function used for preg_replace_callback.
945 _nodewords_teaser_match_callback($alt_attribute, TRUE);
946
947 // Replace the meta tag img with the attribute alt, and strip off all the
948 // HTML tags.
949 $body = strip_tags(
950 preg_replace_callback('/<img\s[^>]*alt=["\']([^"\']*)["\'][^>]*>/i',
951 '_nodewords_teaser_match_callback',
952 $body
953 )
954 );
955
956 // Truncate the string at last space within the given size.
957 return truncate_utf8($body, $size, TRUE);
958 }
959
960 /**
961 * Render the meta tag values as HTML.
962 *
963 * @param $tags
964 * An array of tags (as returned by nodewords_get_tags()).
965 *
966 * @return
967 * A string containing the HTML output for the META tag.
968 */
969 function nodewords_output_tags($tags) {
970 $output = array();
971 $tags_info = nodewords_get_possible_tags();
972 $weights = array();
973
974 foreach ($tags as $name => $content) {
975 if (empty($content)) {
976 continue;
977 }
978
979 $parts = explode(':', $name);
980
981 if (!isset($parts[1])) {
982 $parts[1] = $parts[0];
983 }
984
985 $bool = (
986 isset($tags_info[$parts[0]]['tag:template'][$parts[1]]) &&
987 ($meta_name = check_plain(strip_tags(decode_entities($parts[1])))) &&
988 ($meta_content = check_plain(strip_tags(decode_entities($content))))
989 );
990
991 if ($bool) {
992 $replace = array(
993 '%name' => $meta_name,
994 '%content' => $meta_content,
995 '%attributes' => empty($tags_info[$parts[0]]['tag:attributes'][$parts[1]]) ? '' : drupal_attributes($tags_info[$parts[0]]['tag:attributes'][$parts[1]]),
996 );
997 $template = $tags_info[$parts[0]]['tag:template'][$parts[1]];
998 $weight = isset($tags_info[$parts[0]]['tag:weight'][$parts[1]]) ? $tags_info[$parts[0]]['tag:weight'][$parts[1]] : 0;
999
1000 switch ($template) {
1001 case NODEWORDS_META:
1002 $template = '<meta name="%name" content="%content"%attributes />';
1003 break;
1004
1005 case NODEWORDS_HTTP_EQUIV:
1006 $template = '<meta http-equiv="%name" content="%content"%attributes />';
1007 break;
1008
1009 case NODEWORDS_LINK_REL:
1010 $template = '<link rel="%name" href="%content"%attributes />';
1011 break;
1012
1013 case NODEWORDS_LINK_REV:
1014 $template = '<link rev="%name" href="%content"%attributes />';
1015 break;
1016
1017 default:
1018 if (!is_string($template)) {
1019 $template = '';
1020 }
1021 break;
1022 }
1023
1024 if (!empty($template)) {
1025 $output[] = strtr($template, $replace);
1026 $weights[] = $weight;
1027 }
1028 }
1029 }
1030
1031 array_multisort($weights, $output);
1032
1033 return implode("\n", $output);
1034 }
1035
1036 /**
1037 * This function is used from the menu system when a menu callback path contains
1038 * %nodewords_page_load.
1039 */
1040 function nodewords_page_load($pid) {
1041 return _nodewords_get_pages_data($pid);
1042 }
1043
1044 function nodewords_replace_tokens($type, $id, $content) {
1045 if (empty($content) || !module_exists('token')) {
1046 return $content;
1047 }
1048
1049 switch ($type) {
1050 case NODEWORDS_MT_TYPE_ERRORPAGE:
1051 case NODEWORDS_MT_TYPE_FRONTPAGE:
1052 case NODEWORDS_MT_TYPE_PAGER:
1053 case NODEWORDS_INSTALL_MT_TYPE_TRACKER:
1054 $token_type = 'global';
1055 $token_object = NULL;
1056 break;
1057
1058 case NODEWORDS_MT_TYPE_NODE:
1059 if (is_array($id) && isset($id[0])) {
1060 $id = $id[0];
1061 }
1062
1063 $token_type = 'node';
1064 $token_object = node_load(array('vid' => $id));
1065
1066 break;
1067
1068 case NODEWORDS_MT_TYPE_USER:
1069 if (is_array($id) && isset($id[0])) {
1070 $id = $id[0];
1071 }
1072
1073 $token_type = 'user';
1074 $token_object = user_load($id);
1075 break;
1076
1077 default:
1078 return $content;
1079 }
1080
1081 return token_replace($content, $token_type, $token_object);
1082 }
1083
1084 /**
1085 * Update or insert tags in the table.
1086 */
1087 function nodewords_save_tags($type, $id, $tags, $log_message = FALSE) {
1088 global $user;
1089 static $tags_info;
1090
1091 if (!isset($tags_info)) {
1092 $tags_info = nodewords_get_possible_tags();
1093 }
1094
1095 $done = FALSE;
1096
1097 foreach ($tags as $name => $content) {
1098 if (isset($tags_info[$name])) {
1099 if ($tags_info[$name]['tag:db:type'] != 'string') {
1100 $content = serialize($content);
1101 }
1102 else {
1103 $content = $content['value'];
1104 }
1105
1106 $result = db_fetch_object(db_query("SELECT * FROM {nodewords} WHERE type = %d AND id = '%s' AND name = '%s'", $type, $id, $name));
1107
1108 if ($result === FALSE) {
1109 $row = new stdClass();
1110 $row->type = $type;
1111 $row->id = $id;
1112 $row->name = $name;
1113 }
1114 else {
1115 $row = $result;
1116 }
1117
1118 $row->content = $content;
1119
1120 drupal_write_record('nodewords', $row, $result !== FALSE ? 'mtid' : NULL);
1121
1122 if (!$done) {
1123 watchdog('nodewords', '%name changed the meta tags for %type (%id).', array('%name' => $user->name, '%type' => $type, '%id' => $id));
1124 $done = TRUE;
1125 }
1126 }
1127 }
1128 }
1129
1130 /*
1131 * Remove the duplicates from a list of items separated from the separator,
1132 * preserving the order in which they appear.
1133 * @param $text
1134 * The string containing the list of items concatenated using $separator.
1135 * @param $separator
1136 * The string used to split the string into an array. A space will be appended
1137 * to the string before it is used to create the string from the array of
1138 * unique items found in the string passed as argument.
1139 * @param $max_items
1140 * The maximum number of items accepted in the returned array; the default
1141 * value is -1, which means no limit.
1142 *
1143 * @return
1144 * A string containing only unique items present in the string of concatenated
1145 * items.
1146 */
1147 function nodewords_unique($text, $separator = ',', $max_items = -1) {
1148 $lc_values = array();
1149 $unique_values = array();
1150
1151 if (empty($text)) {
1152 return '';
1153 }
1154
1155 foreach (array_filter(array_map('trim', explode($separator, $text))) as $item) {
1156 $lowercase = drupal_strtolower($item);
1157
1158 if (!in_array($lowercase, $lc_values)) {
1159 $lc_values[] = $lowercase;
1160 $unique_values[] = $item;
1161 }
1162 }
1163
1164 if ($max_items > 0) {
1165 $unique_values = array_slice($uniq_values, 0, $max_items);
1166 }
1167
1168 return implode("$separator ", $unique_values);
1169 }
1170
1171 /*****************************************************************************
1172 * Private functions.
1173 ****************************************************************************/
1174
1175 /**
1176 * Show the requirement errors reported from nodewords_requirements().
1177 * The function is an adaption of drupal_check_module(), and it is called in
1178 * the module settings pages.
1179 */
1180 function _nodewords_check_enabled_modules() {
1181 function _nodewords_check_enabled_modules() {
1182 if (user_access('administer site configuration')) {
1183 if (!count(module_implements('nodewords_api'))) {
1184 drupal_set_message(
1185 t(
1186 'Nodewords does not create meta tags anymore; it is just a module that implements a public API used from the modules that create meta tags. You need to <a href="@url">enable</a> at least one module between nodewords_basic.module, nodewords_extra.module, and nodewords_verification_tags.module that are listed under <em>Meta tags</em> in the <a href="@url">modules page</a>.',
1187 array('@url' => url('admin/build/modules'))
1188 ),
1189 'error'
1190 );
1191 }
1192
1193 if (module_exists('nodewords_bypath')) {
1194 drupal_set_message(
1195 t(
1196 'The feature implemented in <q>Meta Tags by Path</q> is now included in Nodewords; there is not need to use <q>Meta Tags by Path</q>, and the module should be disabled to avoid possible conflicts. Disable the module in the<a href="@url">modules page</a>.'),
1197 array('@url' => url('admin/build/modules')
1198 )
1199 );
1200 }
1201 }
1202 }
1203 }
1204
1205 /**
1206 * Helper function for uksort().
1207 * Sort the form fields basing on their titles.
1208 */
1209 function _nodewords_cmp_form_fields($a, $b, $init = FALSE) {
1210 static $form;
1211
1212 if ($init) {
1213 $form = $a;
1214 }
1215 else {
1216 return strnatcmp($form[$a]['#title'], $form[$b]['#title']);
1217 }
1218 }
1219
1220 /**
1221 * Try to guess the $type and $ids by looking at $_GET['q'].
1222 */
1223 function _nodewords_detect_type_and_ids() {
1224 $arg = arg();
1225
1226 $bool = (
1227 !variable_get('nodewords_list_repeat', FALSE) &&
1228 isset($_REQUEST['page'])
1229 && intval($_REQUEST['page']) > 0
1230 );
1231
1232 if ($bool) {
1233 return array(NODEWORDS_MT_TYPE_PAGER, array(0));
1234 }
1235
1236 if (variable_get('site_offline', 0) && !user_access('administer site configuration')) {
1237 return array('none', array());
1238 }
1239
1240 if (drupal_is_front_page() && variable_get('nodewords_use_frontpage_tags', TRUE)) {
1241 return array(NODEWORDS_MT_TYPE_FRONTPAGE, array(0));
1242 }
1243
1244 $headers = drupal_get_headers();
1245
1246 if (preg_match('@HTTP/1\.[01]\x20+403@', $headers)) {
1247 return array(NODEWORDS_MT_TYPE_ERRORPAGE, array(403));
1248 }
1249
1250