Issue #971428 by DamienMcKenna: Filter the node summary using check_markup() prior...
[project/nodewords.git] / nodewords.module
1 <?php
2 /**
3 * @file
4 * Implement an API that other modules can use to implement meta tags.
5 */
6
7 /**
8 * The minimum API version supported.
9 */
10 define('NODEWORDS_MINIMUM_API_VERSION', '1.12');
11
12 /**
13 * The current API version implemented.
14 */
15 define('NODEWORDS_API_VERSION', '1.14');
16
17 /**
18 * The type of objects to which the meta tags are associated.
19 */
20 define('NODEWORDS_TYPE_BLOG', 13);
21 define('NODEWORDS_TYPE_DEFAULT', 1);
22 define('NODEWORDS_TYPE_ERRORPAGE', 2);
23 define('NODEWORDS_TYPE_FORUM', 12);
24 define('NODEWORDS_TYPE_FRONTPAGE', 3);
25 define('NODEWORDS_TYPE_NONE', 0);
26 define('NODEWORDS_TYPE_NODE', 5);
27 define('NODEWORDS_TYPE_OFFLINE', 11);
28 define('NODEWORDS_TYPE_PAGE', 10);
29 define('NODEWORDS_TYPE_PAGER', 4);
30 define('NODEWORDS_TYPE_TERM', 6);
31 define('NODEWORDS_TYPE_TRACKER', 7);
32 define('NODEWORDS_TYPE_USER', 8);
33 define('NODEWORDS_TYPE_VOCABULARY', 9);
34
35 /**
36 * The types of meta tags the module is able to handle.
37 */
38 define('NODEWORDS_META', 0);
39 define('NODEWORDS_HTTP_EQUIV', 1);
40 define('NODEWORDS_LINK_REL', 2);
41 define('NODEWORDS_LINK_REV', 3);
42 // This requires theme customizations, see README.txt for further details.
43 define('NODEWORDS_META_PROPERTY', 4);
44
45 define('NODEWORDS_GENERATION_NEVER', 0);
46 define('NODEWORDS_GENERATION_WHEN_EMPTY', 1);
47 define('NODEWORDS_GENERATION_ALWAYS', 2);
48
49 define('NODEWORDS_GENERATION_BODY', 1);
50 define('NODEWORDS_GENERATION_TEASER', 2);
51 define('NODEWORDS_GENERATION_TEASER_BODY', 3);
52
53 /**
54 * Implements hook_content_extra_fields().
55 * Allow the meta tags fields to be sorted in the node edit forms.
56 */
57 function nodewords_content_extra_fields() {
58 $extras = array();
59
60 $extras['nodewords'] = array(
61 'label' => t('Meta tags'),
62 'description' => t('Meta tags fieldset.'),
63 'weight' => 10,
64 );
65
66 return $extras;
67 }
68
69 /**
70 * Implements hook_form_alter().
71 */
72 function nodewords_form_alter(&$form, &$form_state, $form_id) {
73 $bool = (
74 isset($form['type']) &&
75 isset($form['#node']) &&
76 $form_id == $form['type']['#value'] . '_node_form' &&
77 variable_get('nodewords_edit_metatags_' . $form['type']['#value'], TRUE)
78 );
79
80 if ($bool) {
81 $node = $form['#node'];
82
83 $form['nodewords'] = nodewords_form(
84 NODEWORDS_TYPE_NODE,
85 !empty($node->nodewords) ? $node->nodewords : array(),
86 array(
87 'tag_options' => array('node_type' => $form['type']['#value']),
88 )
89 );
90 }
91 }
92
93 /**
94 * Implements hook_form_FORM_ID_alter().
95 */
96 function nodewords_form_node_type_form_alter(&$form, &$form_state) {
97 if (isset($form['#node_type'])) {
98
99 $form['nodewords'] = array(
100 '#type' => 'fieldset',
101 '#title' => t('Meta tags settings'),
102 '#collapsible' => TRUE,
103 '#collapsed' => TRUE,
104 '#group' => 'additional_settings',
105 );
106
107 $form['nodewords']['nodewords_edit_metatags'] = array(
108 '#type' => 'checkbox',
109 '#title' => t('Allow editing of meta tags'),
110 '#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.'),
111 '#default_value' => variable_get('nodewords_edit_metatags_' . $form['#node_type']->type, TRUE),
112 );
113
114 $form['nodewords']['metatags_generation'] = array(
115 '#type' => 'fieldset',
116 '#title' => t('Node meta tags generation options'),
117 '#description' => t('These options change how a meta tag content is generated from the node content. These settings apply to specific meta tags.'),
118 '#collapsible' => TRUE,
119 );
120
121 $options = array(
122 NODEWORDS_GENERATION_NEVER => t('Do not generate meta tags content'),
123 NODEWORDS_GENERATION_WHEN_EMPTY => t('Generate meta tag content when the meta tag content is empty (default)'),
124 NODEWORDS_GENERATION_ALWAYS => t('Always generate the meta tag content'),
125 );
126
127 $form['nodewords']['metatags_generation']['nodewords_metatags_generation_method'] = array(
128 '#type' => 'radios',
129 '#options' => $options,
130 '#default_value' => variable_get(
131 'nodewords_metatags_generation_method_' . $form['#node_type']->type,
132 NODEWORDS_GENERATION_WHEN_EMPTY
133 ),
134 );
135
136 $options = array(
137 NODEWORDS_GENERATION_BODY => t('Generate meta tags content from the node body'),
138 NODEWORDS_GENERATION_TEASER => t('Generate meta tags content from the node teaser (default)'),
139 NODEWORDS_GENERATION_TEASER_BODY => t('Generate meta tags content from the node teaser, or the node body when the node teaser is empty'),
140 );
141
142 $form['nodewords']['metatags_generation']['nodewords_metatags_generation_source'] = array(
143 '#type' => 'radios',
144 '#title' => t('Generation source'),
145 '#options' => $options,
146 '#default_value' => variable_get(
147 'nodewords_metatags_generation_source_' . $form['#node_type']->type,
148 NODEWORDS_GENERATION_TEASER
149 ),
150 );
151
152 $form['nodewords']['metatags_generation']['nodewords_use_alt_attribute'] = array(
153 '#type' => 'checkbox',
154 '#title' => t('Replace the tag IMG content with the attribute ALT'),
155 '#default_value' => variable_get(
156 'nodewords_use_alt_attribute_' . $form['#node_type']->type,
157 TRUE
158 ),
159 );
160
161 $options = array(
162 'imagebrowser' => 'imagebrowser.module',
163 'img_assist' => 'img_assist.module',
164 );
165
166 $form['nodewords']['metatags_generation']['nodewords_filter_modules_output'] = array(
167 '#type' => 'checkboxes',
168 '#title' => t('Filter the text added by third-party modules in the node teaser'),
169 '#options' => $options,
170 '#default_value' => variable_get(
171 'nodewords_filter_modules_output_' . $form['#node_type']->type,
172 array()
173 ),
174 '#checkall' => TRUE,
175 );
176
177 $form['nodewords']['metatags_generation']['nodewords_filter_regexp'] = array(
178 '#type' => 'textfield',
179 '#title' => t('Custom regular expression'),
180 '#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 <a href="http://www.php.net/manual/en/pcre.pattern.php">Perl compatible</a> syntax. Slashes are assumed as delimiters and all expressions are case-sensitive.'),
181 '#element_validate' => array('nodewords_filter_regex_validate'),
182 '#default_value' => variable_get(
183 'nodewords_filter_regexp_' . $form['#node_type']->type, ''
184 ),
185 '#field_prefix' => '/',
186 '#field_suffix' => '/',
187 '#size' => 60,
188 );
189
190 foreach (nodewords_get_possible_tags() as $name => $info) {
191 $function = $info['callback'] . '_settings_form';
192 $options = array();
193
194 if (function_exists($function)) {
195 $function($form, 'node_type_form', $options);
196 }
197 }
198 }
199 }
200
201 /**
202 * Implements hook_form_FORM_ID_alter().
203 */
204 function nodewords_form_taxonomy_form_term_alter(&$form, &$form_state) {
205 $bool = (isset($form['tid']['#value']) &&
206 !isset($form_state['confirm_delete']) &&
207 !isset($form_state['confirm_parents'])
208 );
209
210 if ($bool) {
211 $id = $form['tid']['#value'];
212
213 if (!empty($form_state['values']['nodewords'])) {
214 $tags = $form_state['values']['nodewords'];
215 }
216 elseif (isset($id) && is_numeric($id)) {
217 $tags = nodewords_load_tags(NODEWORDS_TYPE_TERM, $id);
218 }
219 else {
220 $tags = array();
221 }
222
223 $form['nodewords'] = nodewords_form(
224 NODEWORDS_TYPE_TERM,
225 $tags
226 );
227
228 // Ensure the submit & delete buttons are at the bottom. Hopefully.
229 $form['submit']['#weight'] = 1000;
230 $form['delete']['#weight'] = 1001;
231 }
232 }
233
234 /**
235 * Implements hook_form_FORM_ID_alter().
236 */
237 function nodewords_form_taxonomy_form_vocabulary_alter(&$form, &$form_state) {
238 if (isset($form['vid']['#value'])) {
239 $id = $form['vid']['#value'];
240
241 if (!empty($form_state['values']['nodewords'])) {
242 $tags = $form_state['values']['nodewords'];
243 }
244 elseif (isset($id) && is_numeric($id)) {
245 $tags = nodewords_load_tags(NODEWORDS_TYPE_VOCABULARY, $id);
246 }
247 else {
248 $tags = array();
249 }
250
251 $form['nodewords'] = nodewords_form(
252 NODEWORDS_TYPE_VOCABULARY,
253 $tags
254 );
255
256 // Ensure the submit & delete buttons are at the bottom. Hopefully.
257 $form['submit']['#weight'] = 1000;
258 $form['delete']['#weight'] = 1001;
259 }
260 }
261
262 /**
263 * Implemenation of hook_help().
264 */
265 function nodewords_help($path, $arg) {
266 switch ($path) {
267 case 'admin/content/nodewords/meta-tags':
268 $output = '<p>' . t('On this page you can enter the default values for the meta tags of your site.') . '</p>';
269 break;
270
271 case 'admin/content/nodewords/meta-tags/errorpage_403':
272 $output = '<p>' . t('On this page you can enter the meta tags for the <q>access denied</q> error page of your site.') . '</p>';
273 break;
274
275 case 'admin/content/nodewords/meta-tags/errorpage_404':
276 $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>';
277 break;
278
279 case 'admin/content/nodewords/meta-tags/frontpage':
280 $output = '<p>' . t('On this page you can enter the meta tags for the front page of your site.') . '</p>';
281 break;
282
283 case 'admin/content/nodewords/meta-tags/custom':
284 $output = '<p>' . 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.') . '</p>';
285 break;
286
287 default:
288 $output = '';
289 break;
290 }
291
292 return $output;
293 }
294
295 /**
296 * Implements hook_menu().
297 */
298 function nodewords_menu() {
299 $admin_access = array('administer meta tags');
300 $items = array();
301
302 $items['admin/content/nodewords'] = array(
303 'title' => 'Meta tags',
304 'page callback' => 'drupal_get_form',
305 'page arguments' => array('nodewords_settings_form'),
306 'description' => 'Configure HTML meta tags for all the content.',
307 'access arguments' => $admin_access,
308 'type' => MENU_NORMAL_ITEM,
309 'file' => 'nodewords.admin.inc',
310 );
311
312 $items['admin/content/nodewords/settings'] = array(
313 'title' => 'Settings',
314 'access arguments' => $admin_access,
315 'type' => MENU_DEFAULT_LOCAL_TASK,
316 'weight' => -10,
317 'file' => 'nodewords.admin.inc',
318 );
319
320 $items['admin/content/nodewords/meta-tags'] = array(
321 'title' => 'Default and specific meta tags',
322 'page callback' => 'drupal_get_form',
323 'page arguments' => array('nodewords_tags_form'),
324 'access arguments' => $admin_access,
325 'type' => MENU_LOCAL_TASK,
326 'file' => 'nodewords.admin.inc',
327 'weight' => -9,
328 );
329
330 $items['admin/content/nodewords/meta-tags/default'] = array(
331 'title' => 'Default values',
332 'access arguments' => $admin_access,
333 'type' => MENU_DEFAULT_LOCAL_TASK,
334 'file' => 'nodewords.admin.inc',
335 'weight' => -10,
336 );
337
338 $items['admin/content/nodewords/meta-tags/frontpage'] = array(
339 'title' => 'Front page',
340 'page callback' => 'drupal_get_form',
341 'page arguments' => array('nodewords_tags_form', (string) NODEWORDS_TYPE_FRONTPAGE),
342 'access arguments' => $admin_access,
343 'type' => MENU_LOCAL_TASK,
344 'file' => 'nodewords.admin.inc',
345 'weight' => -9,
346 );
347
348 $items['admin/content/nodewords/meta-tags/offline'] = array(
349 'title' => 'Site off-line page',
350 'page callback' => 'drupal_get_form',
351 'page arguments' => array('nodewords_tags_form', (string) NODEWORDS_TYPE_OFFLINE),
352 'access arguments' => $admin_access,
353 'type' => MENU_LOCAL_TASK,
354 'file' => 'nodewords.admin.inc',
355 'weight' => -8,
356 );
357
358 $items['admin/content/nodewords/meta-tags/errorpage_403'] = array(
359 'title' => 'Error 403 page',
360 'page callback' => 'drupal_get_form',
361 'page arguments' => array('nodewords_tags_form', (string) NODEWORDS_TYPE_ERRORPAGE, '403'),
362 'access arguments' => $admin_access,
363 'type' => MENU_LOCAL_TASK,
364 'file' => 'nodewords.admin.inc',
365 'weight' => -7,
366 );
367
368 $items['admin/content/nodewords/meta-tags/errorpage_404'] = array(
369 'title' => 'Error 404 page',
370 'page callback' => 'drupal_get_form',
371 'page arguments' => array('nodewords_tags_form', (string) NODEWORDS_TYPE_ERRORPAGE, '404'),
372 'access arguments' => $admin_access,
373 'type' => MENU_LOCAL_TASK,
374 'file' => 'nodewords.admin.inc',
375 'weight' => -6,
376 );
377
378 // Only show the forum settings page if the core Blog module is enabled.
379 if (module_exists('blog')) {
380 $items['admin/content/nodewords/meta-tags/blog'] = array(
381 'title' => 'Blog main page',
382 'page callback' => 'drupal_get_form',
383 'page arguments' => array('nodewords_tags_form', (string) NODEWORDS_TYPE_BLOG),
384 'access arguments' => $admin_access,
385 'type' => MENU_LOCAL_TASK,
386 'file' => 'nodewords.admin.inc',
387 'weight' => -5,
388 );
389 }
390
391 // Only show the forum settings page if the core Forum module is enabled.
392 if (module_exists('forum')) {
393 $items['admin/content/nodewords/meta-tags/forum'] = array(
394 'title' => 'Forum main page',
395 'page callback' => 'drupal_get_form',
396 'page arguments' => array('nodewords_tags_form', (string) NODEWORDS_TYPE_FORUM),
397 'access arguments' => $admin_access,
398 'type' => MENU_LOCAL_TASK,
399 'file' => 'nodewords.admin.inc',
400 'weight' => -4,
401 );
402 }
403
404 $items['admin/content/nodewords/meta-tags/custom'] = array(
405 'title' => 'Custom pages',
406 'page callback' => 'drupal_get_form',
407 'page arguments' => array('nodewords_custom_pages_overview'),
408 'access arguments' => $admin_access,
409 'type' => MENU_LOCAL_TASK,
410 'weight' => 20,
411 'file' => 'nodewords.admin.inc',
412 );
413
414 $items['admin/content/nodewords/meta-tags/custom/add'] = array(
415 'title' => 'Add custom pages meta tags',
416 'page callback' => 'drupal_get_form',
417 'page arguments' => array('nodewords_custom_pages_edit'),
418 'access arguments' => $admin_access,
419 'type' => MENU_CALLBACK,
420 'file' => 'nodewords.admin.inc',
421 );
422
423 $items['admin/content/nodewords/meta-tags/custom/%nodewords_page/delete'] = array(
424 'title' => 'Delete custom pages meta tags',
425 'page callback' => 'drupal_get_form',
426 'page arguments' => array('nodewords_custom_pages_confirm_delete', 5),
427 'access arguments' => $admin_access,
428 'parent' => 'admin/content/nodewords/meta-tags/custom',
429 'type' => MENU_CALLBACK,
430 'file' => 'nodewords.admin.inc',
431 );
432
433 $items['admin/content/nodewords/meta-tags/custom/%nodewords_page/edit'] = array(
434 'title' => 'Edit custom pages meta tags',
435 'page callback' => 'drupal_get_form',
436 'page arguments' => array('nodewords_custom_pages_edit', 5),
437 'access arguments' => $admin_access,
438 'parent' => 'admin/content/nodewords/meta-tags/custom',
439 'type' => MENU_CALLBACK,
440 'file' => 'nodewords.admin.inc',
441 );
442
443 return $items;
444 }
445
446 /**
447 * Implements hook_node_operations().
448 */
449 function nodewords_node_operations() {
450 $operations = array(
451 'delete_metatags' => array(
452 'label' => t('Delete meta tags'),
453 'callback' => 'nodewords_mass_update',
454 'callback arguments' => array('type' => NODEWORDS_TYPE_NODE, 'operation' => 'delete'),
455 ),
456 );
457
458 return $operations;
459 }
460
461 /**
462 * Implements hook_node_type().
463 */
464 function nodewords_node_type($op, $info) {
465 if ($op == 'delete') {
466 $variables = array(
467 'nodewords_metatags_generation_method_',
468 'nodewords_metatags_generation_source_',
469 'nodewords_edit_metatags_',
470 'nodewords_filter_modules_output_',
471 'nodewords_filter_regexp_',
472 'nodewords_use_alt_attr_',
473 'nodewords_use_teaser_',
474 );
475
476 foreach ($variables as $variable) {
477 variable_del($variable . $info->type);
478 }
479 }
480 }
481
482 /**
483 * Implements hook_nodeapi().
484 */
485 function nodewords_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
486 switch ($op) {
487 case 'delete':
488 nodewords_delete_tags(NODEWORDS_TYPE_NODE, $node->nid);
489 break;
490
491 case 'insert':
492 case 'update':
493 if (isset($node->nodewords)) {
494 nodewords_save_tags(NODEWORDS_TYPE_NODE, $node->nid, $node->nodewords, TRUE);
495 }
496 break;
497
498 case 'load':
499 return array(
500 'nodewords' => nodewords_load_tags(NODEWORDS_TYPE_NODE, $node->nid),
501 );
502
503 case 'prepare translation':
504 if (isset($node->translation_source->nodewords)) {
505 $node->nodewords = $node->translation_source->nodewords;
506 }
507 break;
508
509 case 'update index':
510 $output_tags = array();
511 $tag_options = array(
512 'type' => NODEWORDS_TYPE_NODE,
513 'id' => $node->nid,
514 'output' => 'update index',
515 );
516
517 if (isset($node->nodewords)) {
518 // Prepare the tags.
519 foreach (nodewords_get_possible_tags() as $name => $info) {
520 $bool = (
521 !empty($info['templates']['search index']) &&
522 function_exists($function = $info['callback'] . '_prepare')
523 );
524
525 if ($bool) {
526 $function(
527 $output_tags,
528 isset($node->nodewords[$name]) ? $node->nodewords[$name] : array(),
529 $tag_options
530 );
531 }
532 }
533
534 drupal_alter('nodewords_tags', $output_tags, $tag_options);
535 $output = _nodewords_output_tags($output_tags, 'update index');
536 drupal_alter('nodewords_tags_output', $output, $tag_options);
537
538 return $output;
539 }
540
541 return '';
542 }
543 }
544
545 /**
546 * Implements hook_perm().
547 */
548 function nodewords_perm() {
549 return array('administer meta tags');
550 }
551
552 /**
553 * Implements hook_preprocess_page().
554 */
555 function nodewords_preprocess_page(&$variables) {
556 // Do not do anything when running in install or update mode.
557 if (defined('MAINTENANCE_MODE')) {
558 return;
559 }
560
561 $options = _nodewords_detect_type_and_id();
562 $output_tags = array();
563
564 $options += array(
565 'default' => nodewords_load_tags(),
566 'output' => 'head',
567 );
568
569 if ($options['type'] == NODEWORDS_TYPE_PAGER) {
570 foreach (nodewords_get_possible_tags() as $name => $info) {
571 $bool = (
572 !empty($info['context']['allowed']) &&
573 in_array(NODEWORDS_TYPE_PAGER, $info['context']['allowed']) &&
574 function_exists($function = $info['callback'] . '_prepare')
575 );
576
577 if ($bool) {
578 $function($output_tags, array(), $options);
579 }
580 }
581 }
582 else {
583 // User profiles meta tags are not enabled.
584 if ($options['type'] == NODEWORDS_TYPE_USER && !variable_get('nodewords_enable_user_metatags', TRUE)) {
585 return;
586 }
587 // If the visitor doesn't have access to this node, don't load anything.
588 elseif ($options['type'] == NODEWORDS_TYPE_NODE && !node_access('view', node_load($options['id']))) {
589 $tags = array();
590 }
591 // Otherwise, load the tags for this page.
592 else {
593 $tags = nodewords_load_tags($options['type'], $options['id']);
594 }
595
596 // Prepare the tags.
597 foreach (nodewords_get_possible_tags() as $name => $info) {
598 if (function_exists($function = $info['callback'] . '_prepare')) {
599 $function($output_tags, isset($tags[$name]) ? $tags[$name] : array(), $options);
600 }
601 }
602 }
603
604 drupal_alter('nodewords_tags', $output_tags, $options);
605
606 // Title tag has its own page preprocess variable and must be handled
607 // separately.
608 if (isset($output_tags['page_title'])) {
609 if (!empty($output_tags['page_title'])) {
610 drupal_set_title($output_tags['page_title']);
611 $variables['head_title'] = strip_tags(drupal_get_title());
612 }
613 unset($output_tags['page_title']);
614 }
615
616 $output = _nodewords_output_tags($output_tags);
617 drupal_alter('nodewords_tags_output', $output, $options);
618
619 // Output the tags to the header.
620 drupal_set_html_head($output);
621 $variables['head'] = drupal_get_html_head();
622 }
623
624 /**
625 * Implements hook_preprocess_maintenance_page().
626 */
627 function nodewords_preprocess_maintenance_page(&$variables) {
628 nodewords_preprocess_page($variables);
629 }
630
631 /**
632 * Implements hook_taxonomy().
633 */
634 function nodewords_taxonomy($op, $type, $array = NULL) {
635 if (isset($array)) {
636 if ($type == 'term') {
637 $type = NODEWORDS_TYPE_TERM;
638 $id = $array['tid'];
639 }
640 elseif ($type == 'vocabulary') {
641 $type = NODEWORDS_TYPE_VOCABULARY;
642 $id = $array['vid'];
643 }
644 else {
645 return;
646 }
647
648 switch ($op) {
649 case 'delete':
650 nodewords_delete_tags($type, $id);
651 break;
652
653 case 'insert':
654 case 'update':
655 if (isset($array['nodewords'])) {
656 nodewords_save_tags($type, $id, $array['nodewords'], TRUE);
657 }
658 break;
659 }
660 }
661 }
662
663 /**
664 * Implements hook_theme().
665 */
666 function nodewords_theme() {
667 return array(
668 'nodewords_custom_pages_overview' => array(
669 'arguments' => array('form' => array()),
670 'file' => 'nodewords.admin.inc',
671 ),
672 );
673 }
674
675 /**
676 * Implements hook_user().
677 */
678 function nodewords_user($op, &$edit, &$account, $category = NULL) {
679 global $user;
680
681 switch ($op) {
682 case 'load':
683 if (arg(0) == 'user' && !empty($account->uid) && variable_get('nodewords_enable_user_metatags', TRUE)) {
684 $account->nodewords = nodewords_load_tags(NODEWORDS_TYPE_USER, $account->uid);
685 }
686 break;
687
688 case 'delete':
689 nodewords_delete_tags(NODEWORDS_TYPE_USER, $account->uid);
690 break;
691
692 case 'insert':
693 case 'update':
694 if (isset($edit['nodewords'])) {
695 nodewords_save_tags(NODEWORDS_TYPE_USER, $account->uid, $edit['nodewords'], TRUE);
696 }
697
698 $edit['nodewords'] = NULL;
699 break;
700
701 case 'form':
702 $bool = (
703 (
704 user_access('administer meta tags') ||
705 $user->uid == $account->uid
706 ) && variable_get('nodewords_enable_user_metatags', TRUE) &&
707 $category == 'account'
708 );
709
710 if ($bool) {
711 $tags = nodewords_load_tags(NODEWORDS_TYPE_USER, $account->uid);
712 $form['nodewords'] = nodewords_form(
713 NODEWORDS_TYPE_USER,
714 $tags
715 );
716
717 return $form;
718 }
719 return array();
720 }
721 }
722
723 /**
724 * Implements hook_user_operations().
725 */
726 function nodewords_user_operations() {
727 $operations = array(
728 'delete_metatags' => array(
729 'label' => t('Delete meta tags'),
730 'callback' => 'nodewords_mass_update',
731 'callback arguments' => array('type' => NODEWORDS_TYPE_USER, 'operation' => 'delete'),
732 ),
733 );
734
735 return $operations;
736 }
737
738 /**
739 * Delete tags from table.
740 */
741 function nodewords_delete_tags($type, $id) {
742 db_query("DELETE FROM {nodewords} WHERE type = %d AND id = %d", $type, $id);
743
744 if ($type == NODEWORDS_TYPE_PAGE) {
745 db_query("DELETE FROM {nodewords_custom} WHERE pid = %d", $id);
746 }
747 }
748
749 /**
750 * Return the form used to set the meta tags values.
751 *
752 * @param $type
753 * The object to which the meta tags are associated (node, user, taxonomy
754 * term, etc...).
755 * @param $tags
756 * The meta tags array as returned by nodewords_load_tags().
757 *
758 * @return
759 * An array as requested by the form API.
760 */
761 function nodewords_form($type, $tags, $options = array()) {
762 $default_options = array(
763 'fieldset' => TRUE,
764 'fieldset:title' => t('Meta tags'),
765 'fieldset:weight' => 20,
766 );
767 $default_tag_options = array(
768 'default' => nodewords_load_tags(),
769 'type' => $type,
770 );
771 $edit_tags = variable_get('nodewords_edit', array());
772 $form = array();
773 $options += $default_options;
774
775 if (isset($options['tag_options']) && is_array($options['tag_options'])) {
776 $tag_options = $options['tag_options'] + $default_tag_options;
777 }
778 else {
779 $tag_options = $default_tag_options;
780 }
781
782 $tags_info = nodewords_get_possible_tags();
783
784 foreach ($tags_info as $name => $info) {
785 $bool = (
786 (
787 user_access('administer meta tags')
788 ) ||
789 !empty($edit_tags[$name])
790 );
791
792 if ($bool) {
793 $bool = (
794 (
795 !empty($info['context']['allowed']) &&
796 is_array($info['context']['allowed']) &&
797 !in_array($type, $info['context']['allowed'])
798 ) ||
799 (
800 !empty($info['context']['denied']) &&
801 is_array($info['context']['denied']) &&
802 in_array($type, $info['context']['denied'])
803 )
804 );
805
806 if ($bool) {
807 continue;
808 }
809
810 $bool = (
811 (
812 user_access('administer meta tags')
813 ) ||
814 (
815 !empty($info['permission']) &&
816 user_access($info['permission'])
817 )
818 );
819
820 if ($bool) {
821 if (function_exists($function = $info['callback'] . '_form')) {
822 $function($form, isset($tags[$name]) ? $tags[$name] : array(), $tag_options);
823 }
824 }
825 }
826 }
827
828 if (!empty($form) && $options['fieldset']) {
829 $form['#type'] = 'fieldset';
830 $form['#title'] = $options['fieldset:title'];
831 $form['#tree'] = TRUE;
832 $form['#collapsible'] = TRUE;
833 $form['#collapsed'] = TRUE;
834 $form['#weight'] = $options['fieldset:weight'];
835 $form['#group'] = 'additional_settings';
836 }
837
838 return $form;
839 }
840
841 /**
842 * Query all the modules implementing meta tags and return the list of meta tags.
843 *
844 * @param $load
845 * If TRUE, the file containing the code implementing the meta tags will be loaded.
846 *
847 * @return
848 * An array containing the list of meta tags definitions.
849 */
850 function nodewords_get_possible_tags($load = FALSE) {
851 static $tags_info = array();
852
853 if ($load) {
854 $tags_info = array();
855 }
856
857 if (empty($tags_info)) {
858 // Allow third-party modules to alter the meta tags list, or to add new
859 // meta tags.
860 foreach (module_implements('nodewords_tags_info') as $module) {
861 if (module_hook($module, 'nodewords_api')) {
862 $info = module_invoke($module, 'nodewords_api');
863 $version = '0.0';
864
865 if (isset($info)) {
866 if (is_string($info)) {
867 $version = $info;
868 }
869 elseif (is_array($info) && isset($info['version'])) {
870 $version = $info['version'];
871
872 if ($load && !empty($info['file'])) {
873 $include_file = $info['file'];
874
875 if (isset($info['path'])) {
876 $include_file = $info['path'] . '/' . $include_file;
877 }
878
879 include_once $include_file;
880 }
881 }
882 }
883
884 $bool = (
885 version_compare($version, NODEWORDS_MINIMUM_API_VERSION, '<') ||
886 version_compare($version, NODEWORDS_API_VERSION, '>')
887 );
888
889 if ($bool) {
890 continue;
891 }
892
893 $result = module_invoke($module, 'nodewords_tags_info');
894
895 if (isset($result) && is_array($result)) {
896 $tags_info = array_merge($tags_info, $result);
897 }
898 }
899 }
900 }
901
902 return $tags_info;
903 }
904
905 /**
906 * Return the term object matching a term ID. This is a modified version of
907 * taxonomy_get_term() which uses db_rewrite_sql().
908 *
909 * @param $tid
910 * A term's ID.
911 * @param $uid
912 * The user ID; if not passed, the function will use the global user ID.
913 *
914 * @return
915 * A term object, or FALSE. Results are statically cached.
916 */
917 function nodewords_get_term($tid, $uid = NULL) {
918 global $user;
919 static $terms = array();
920
921 if (!isset($uid)) {
922 $uid = $user->uid;
923 }
924
925 if (!isset($terms[$uid][$tid])) {
926 $terms[$uid][$tid] = db_fetch_object(
927 db_query(
928 db_rewrite_sql('SELECT * FROM {term_data} t WHERE t.tid = %d', 't', 'tid'),
929 $tid
930 )
931 );
932 }
933
934 return !empty($terms[$uid][$tid]) ? $terms[$uid][$tid] : FALSE;
935 }
936
937 /**
938 * Load tags from table.
939 */
940 function nodewords_load_tags($type = NODEWORDS_TYPE_DEFAULT, $id = 0) {
941 static $queries = array();
942
943 // If the metatags haven't been loaded before, load them.
944 if (!isset($queries[$type][$id])) {
945 $result = db_query("SELECT * FROM {nodewords} WHERE type = %d AND id = %d", $type, $id);
946 $tags = array();
947 $tags_info = nodewords_get_possible_tags();
948
949 while ($row = db_fetch_object($result)) {
950 if (isset($tags_info[$row->name])) {
951 $tags[$row->name] = unserialize($row->content);
952 }
953 }
954
955 // If no metatags are found for this term, try loading the vocabulary's.
956 if (empty($tags) && $type == NODEWORDS_TYPE_TERM) {
957 $tags = nodewords_load_tags(NODEWORDS_TYPE_VOCABULARY, db_result(db_query('SELECT vid FROM {term_data} WHERE tid = %d', $id)));
958 }
959
960 // Cache the metatags for later.
961 $queries[$type][$id] = $tags;
962 }
963
964 return $queries[$type][$id];
965 }
966
967
968 /**
969 * Delete the nodes meta tags.
970 *
971 * @param $ids
972 * An array of IDs.
973 * @param $type
974 * The type of the object associated with the IDs (NODEWORDS_TYPE_NODE, NODEWORDS_TYPE_USER,
975 * NODEWORDS_TYPE_PAGER, NODEWORDS_TYPE_PAGE, ...).
976 * @param $operation
977 * The operation to execute (currently implemented: delete).
978 */
979 function nodewords_mass_update($ids, $type, $operation = 'delete') {
980 if ($operation == 'delete') {
981 if (($count = count($ids))) {
982 if ($count <= 10) {
983 db_query("DELETE FROM {nodewords} WHERE id IN (" . db_placeholders($ids, 'int') . ") AND type = %d",
984 array_merge($ids, array($type))
985 );
986
987 if ($type == NODEWORDS_TYPE_PAGE) {
988 db_query("DELETE FROM {nodewords_custom} WHERE pid IN (" . db_placeholders($ids, 'int') . ")", $ids);
989 }
990
991 drupal_set_message(t('The update has been performed.'));
992 }
993 else {
994 $batch = array(
995 'operations' => array(
996 array('_nodewords_mass_delete_batch_process', array($ids, $type))
997 ),
998 'finished' => '_nodewords_mass_update_batch_finished',
999 'title' => t('Processing'),
1000 'progress_message' => '',
1001 'error_message' => t('The update has encountered an error.'),
1002 'file' => drupal_get_path('module', 'nodewords') .'/nodewords.admin.inc',
1003 );
1004 batch_set($batch);
1005 }
1006 }
1007 }
1008 }
1009
1010 /**
1011 * Create the content of a meta tag from a node teaser.
1012 *
1013 * @param $node
1014 * The node object the meta tag refers to.
1015 * @param $content
1016 * The meta tag content.
1017 * @param $options
1018 * An array of options; currently, the only option used is the maximum allowed
1019 * length.
1020 *
1021 * @return
1022 * The string used as meta tag content.
1023 */
1024 function nodewords_metatag_from_node_content($node, $content, $options = array()) {
1025 // The method used to generate the summary string.
1026 $method = variable_get('nodewords_metatags_generation_method_' . $node->type, NODEWORDS_GENERATION_WHEN_EMPTY);
1027
1028 // If not generating an automatic description, return immediately.
1029 if ($method == NODEWORDS_GENERATION_NEVER
1030 || ($method == NODEWORDS_GENERATION_WHEN_EMPTY && !empty($content))) {
1031 return $content;
1032 }
1033
1034 // Proceed as normal.
1035 $result = '';
1036 $source = variable_get('nodewords_metatags_generation_source_' . $node->type, NODEWORDS_GENERATION_TEASER);
1037
1038 // If generating an automatic description, determine the source.
1039 if (!empty($node->teaser) && ($source == NODEWORDS_GENERATION_TEASER || $source == NODEWORDS_GENERATION_TEASER_BODY)) {
1040 $result = $node->teaser;
1041 }
1042 elseif (!empty($node->body) && ($source == NODEWORDS_GENERATION_BODY || ($source == NODEWORDS_GENERATION_TEASER_BODY && empty($node->teaser)))) {
1043 $result = $node->body;
1044 }
1045
1046 // Clean up the text by running it through applicable filters.
1047 if (!empty($result)) {
1048 // Check for the presence of the PHP evaluator filter in the current format.
1049 // If the text contains PHP code, do not split it up to prevent parse errors.
1050 $filters = filter_list_format($node->format);
1051 if (isset($filters['php/0']) && strpos($result, '<?') !== FALSE) {
1052 $result = '';
1053 }
1054
1055 // Continue as normal.
1056 else {
1057 // Run all of the normal text filters on the summary text.
1058 $result = check_markup($result, $node->format, FALSE);
1059
1060 // Ensure there's a setting for controlling the maximum summary length.
1061 if (!isset($options['size'])) {
1062 $options['size'] = variable_get('nodewords_max_size', 350);
1063 }
1064
1065 // Optionally replace the tag IMG with its ALT attribute.
1066 if (variable_get('nodewords_use_alt_attribute', FALSE)) {
1067 $result = preg_replace("/<img\s[^>]*alt=[\"']([^\"']*)[\"'][^>]*>/i", ' ($1) ', $result);
1068 }
1069
1070 // Strip off all the HTML tags.
1071 $result = strip_tags($result);
1072
1073 // Remove the strings added from third-party modules.
1074 $modules = array_filter(variable_get('nodewords_filter_modules_output_' . $node->type, array()));
1075 $regexps = array(
1076 'imagebrowser' => '/\[ibimage[^\]]*\]/i',
1077 'img_assist' => '/\[img_assist[^\]]*\]/i',
1078 );
1079 foreach ($regexps as $module => $regexp) {
1080 if (isset($modules[$module])) {
1081 $result = preg_replace($regexp, '', $result);
1082 }
1083 }
1084
1085 // Remove the text matching the type-specific regular expression.
1086 if ($regexp = trim(variable_get('nodewords_filter_regexp_' . $node->type, ''))) {
1087 $result = preg_replace('/' . $regexp . '/', '', $result);
1088 }
1089
1090 // Remove line breaks.
1091 $result = preg_replace('/(\r\n?|\n)/', ' ', $result);
1092
1093 // Remove any leading & trailing whitespace.
1094 $result = trim($result);
1095
1096 // Remove excess inline whitespace.
1097 $result = preg_replace('/\s\s+/', ' ', $result);
1098
1099 // Trim the filtered text to the maximum length allowed.
1100 $result = node_teaser($result, $node->format, $options['size']);
1101 }
1102 }
1103
1104 return $result;
1105 }
1106
1107 /**
1108 * This function is used from the menu system when a menu callback path contains
1109 * %nodewords_page_load.
1110 */
1111 function nodewords_page_load($pid) {
1112 return _nodewords_get_custom_pages_data($pid);
1113 }
1114
1115 /**
1116 * Update or insert tags in the table.
1117 */
1118 function nodewords_save_tags($type, $id, $tags, $log_message = FALSE) {
1119 global $user;
1120
1121 $done = FALSE;
1122 $tags_info = nodewords_get_possible_tags();
1123 $types_str = array(
1124 NODEWORDS_TYPE_BLOG => t('blog main page'),
1125 NODEWORDS_TYPE_DEFAULT => t('default'),
1126 NODEWORDS_TYPE_ERRORPAGE => t('HTTP error page'),
1127 NODEWORDS_TYPE_FORUM => t('forum main page'),
1128 NODEWORDS_TYPE_FRONTPAGE => t('front page'),
1129 NODEWORDS_TYPE_NODE => t('node'),
1130 NODEWORDS_TYPE_OFFLINE => t('site offline page'),
1131 NODEWORDS_TYPE_PAGE => t('custom page'),
1132 NODEWORDS_TYPE_PAGER => t('list page'),
1133 NODEWORDS_TYPE_TERM => t('taxonomy term'),
1134 NODEWORDS_TYPE_USER => t('user profile'),
1135 NODEWORDS_TYPE_VOCABULARY => t('vocabulary'),
1136 );
1137
1138 foreach ($tags as $name => $content) {
1139 if (isset($tags_info[$name])) {
1140 $content = serialize($content);
1141 $result = db_fetch_object(
1142 db_query_range(
1143 "SELECT * FROM {nodewords} WHERE type = %d AND id = %d AND name = '%s'",
1144 $type, $id, $name, 0, 1
1145 )
1146 );
1147
1148 if ($result === FALSE) {
1149 $row = new stdClass();
1150 $row->type = $type;
1151 $row->id = $id;
1152 $row->name = $name;
1153 }
1154 else {
1155 $row = $result;
1156 }
1157
1158 $row->content = $content;
1159
1160 drupal_write_record('nodewords', $row, $result !== FALSE ? 'mtid' : array());
1161
1162 if (!$done) {
1163 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));
1164 $done = TRUE;
1165 }
1166 }
1167 }
1168
1169 // Reload the tags so that future API calls get the correct data.
1170 nodewords_load_tags($type, $id);
1171 }
1172
1173 /**
1174 * Remove the duplicates from a list of items separated from the separator,
1175 * preserving the order in which they appear.
1176 * @param $text
1177 * The string containing the list of items concatenated using $separator.
1178 * @param $separator
1179 * The string used to split the string into an array. A space will be appended
1180 * to the string before it is used to create the string from the array of
1181 * unique items found in the string passed as argument.
1182 * @param $max_items
1183 * The maximum number of items accepted in the returned array; the default
1184 * value is -1, which means no limit.
1185 *
1186 * @return
1187 * A string containing only unique items present in the string of concatenated
1188 * items.
1189 */
1190 function nodewords_unique_values($text, $separator = ',', $max_items = -1) {
1191 $lc_values = array();
1192 $unique_values = array();
1193
1194 if (empty($text)) {
1195 return '';
1196 }
1197
1198 foreach (array_filter(array_map('trim', explode($separator, $text))) as $item) {
1199 $lowercase = drupal_strtolower($item);
1200
1201 if (!in_array($lowercase, $lc_values)) {
1202 $lc_values[] = $lowercase;
1203 $unique_values[] = $item;
1204 }
1205 }
1206
1207 if ($max_items > 0) {
1208 $unique_values = array_slice($unique_values, 0, $max_items);
1209 }
1210
1211 return implode("$separator", $unique_values);
1212 }
1213
1214 /**
1215 * Return the absolute URL of a path.
1216 *
1217 * Return the absolute URL of a path built using the base URL saved in the
1218 * Drupal variable nodewords_base_url.
1219 *
1220 * @param $path
1221 * The path for which the absolute must be built.
1222 * @param $options
1223 * An array of options as used by url().
1224 */
1225 function nodewords_url($path, $options = array()) {
1226 // Remove the trailing slash, it will be added in the url() function.
1227 $base_url = rtrim(variable_get('nodewords_base_url', ''), '/');
1228
1229 $options += array(
1230 'alias' => TRUE,
1231 'absolute' => TRUE,
1232 'fragment' => '',
1233 'query' => '',
1234 'prefix' => ''
1235 );
1236
1237 // Identify if the URL alias should be used.
1238 if (variable_get('nodewords_use_path_alias', TRUE)) {
1239 // Note: the url() function's 'alias' argument is to specify that the
1240 // path is *already* an alias, it is not for requesting an alias be loaded.
1241 $options['alias'] = FALSE;
1242 }
1243
1244 $options['base_url'] = empty($base_url) ? NULL : $base_url;
1245
1246 return url($path, $options);
1247 }
1248
1249 /**
1250 * Implementation of hook_migrate_api().
1251 */
1252 function nodewords_migrate_api() {
1253 return array(
1254 'api' => 1,
1255 'integration modules' => array(
1256 'nodewords' => array('status' => FALSE),
1257 ),
1258 );
1259 }
1260
1261
1262 /**
1263 * Internal functions
1264 */
1265
1266
1267 /**
1268 * Try to guess the $type and $id by looking at $_GET['q'].
1269 */
1270 function _nodewords_detect_type_and_id() {
1271 $arg = arg();
1272 $default = array('type' => NODEWORDS_TYPE_PAGE, 'id' => 0);
1273
1274 // Do not do anything when running in install or update mode.
1275 if (defined('MAINTENANCE_MODE')) {
1276 return array('type' => NODEWORDS_TYPE_NONE);
1277 }
1278
1279 if (variable_get('site_offline', 0) && !user_access('administer site configuration')) {
1280 // User will see the site-offline page.
1281 return array('type' => NODEWORDS_TYPE_OFFLINE, 'id' => 0);
1282 }
1283
1284 $headers = drupal_get_headers();
1285
1286 if (preg_match('@HTTP/1\.[01]\x20+403@', $headers)) {
1287 return array('type' => NODEWORDS_TYPE_ERRORPAGE, 'id' => 403);
1288 }
1289
1290 if (preg_match('@HTTP/1\.[01]\x20+404@', $headers)) {
1291 return array('type' => NODEWORDS_TYPE_ERRORPAGE, 'id' => 404);
1292 }
1293
1294 $bool = (
1295 !variable_get('nodewords_list_repeat', FALSE) &&
1296 isset($_REQUEST['page']) &&
1297 intval($_REQUEST['page']) > 0
1298 );
1299
1300 if ($bool) {
1301 return array('type' => NODEWORDS_TYPE_PAGER, 'id' => 0);
1302 }
1303
1304 if (drupal_is_front_page() && variable_get('nodewords_use_frontpage_tags', TRUE)) {
1305 return array('type' => NODEWORDS_TYPE_FRONTPAGE, 'id' => 0);
1306 }
1307
1308 if (module_exists('blog') && $_GET['q'] == 'blog') {
1309 return array('type' => NODEWORDS_TYPE_BLOG, 'id' => 0);
1310 }
1311
1312 if (module_exists('forum') && $_GET['q'] == 'forum') {
1313 return array('type' => NODEWORDS_TYPE_FORUM, 'id' => 0);
1314 }
1315
1316 // Check all of the custom page paths.
1317 foreach (_nodewords_get_pages_paths() as $pid => $path) {
1318 // The path is a system path.
1319 if (drupal_match_path($_GET['q'], $path)) {
1320 return array('type' => NODEWORDS_TYPE_PAGE, 'id' => $pid);
1321 }
1322
1323 // The path is a URL alias.
1324 $alias = drupal_get_path_alias($_GET['q']);
1325 if ($alias != $_GET['q'] && drupal_match_path($alias, $path)) {
1326 return array('type' => NODEWORDS_TYPE_PAGE, 'id' => $pid);
1327 }
1328 }
1329
1330 if (!isset($arg[0])) {
1331 return $default;
1332 }
1333
1334 _nodewords_load_all_includes();
1335
1336 $result = array(
1337 'type' => NODEWORDS_TYPE_NONE,
1338 'id' => 0,
1339 );
1340
1341 foreach (module_implements('nodewords_type_id') as $module) {
1342 $function = $module . '_nodewords_type_id';
1343 $function($result, $arg);
1344
1345 if ($result['type'] != NODEWORDS_TYPE_NONE) {
1346 return $result;
1347 }
1348 }
1349
1350 return $default;
1351 }
1352
1353 /**
1354 * Load the page meta tags data from the cache.
1355 *
1356 * @param $id
1357 * The ID of the page to load; by default the function loads all the custom
1358 * pages data.
1359 */
1360 function _nodewords_get_custom_pages_data($id = NULL) {
1361 static $pages;
1362
1363 if (!isset($pages)) {
1364 $pages = array();
1365 $result = db_query("SELECT * FROM {nodewords_custom} ORDER BY weight ASC");
1366
1367 while ($page = db_fetch_object($result)) {
1368 $page->tags = nodewords_load_tags(NODEWORDS_TYPE_PAGE, $page->pid);
1369 $pages[$page->pid] = $page;
1370 }
1371 }
1372
1373 return isset($id) ? (isset($pages[$id]) ? $pages[$id] : FALSE) : $pages;
1374 }
1375
1376 /**
1377 * Answer a listing of page paths, stored in a static cache.
1378 */
1379 function _nodewords_get_pages_paths() {
1380 static $paths;
1381
1382 if (!isset($paths)) {
1383 $paths = array();
1384 $result = db_query("SELECT pid, path FROM {nodewords_custom} ORDER BY weight ASC");
1385
1386 while ($page = db_fetch_object($result)) {
1387 $paths[$page->pid] = $page->path;
1388 }
1389 }
1390
1391 return $paths;
1392 }
1393
1394 /**
1395 * Load the files in the directory "includes" basing on the enabled modules.
1396 */
1397 function _nodewords_load_all_includes() {
1398 $dir = drupal_get_path('module', 'nodewords') . '/includes';
1399
1400 // Update this array when new files are added.
1401 $includes = array(
1402 'forum',
1403 'image',
1404 'node',
1405 'taxonomy',
1406 'taxonomy_menu',
1407 'uc_catalog',
1408 'user',
1409 );
1410 foreach ($includes as $include) {
1411 if (module_exists($include) && !module_hook($include, 'nodewords_type_id')) {
1412 module_load_include('inc', 'nodewords', 'includes/' . $include);
1413 }
1414 }
1415 }
1416
1417 /**
1418 * Render the meta tag values as HTML.
1419 *
1420 * @param $tags
1421 * An array of tags.
1422 *
1423 * @param $output_type
1424 * The type of output, 'head' (default), or 'update index'.
1425 *
1426 * @return
1427 * A string containing the HTML output for the META tag.
1428 */
1429 function _nodewords_output_tags($tags, $output_type = 'head') {
1430 $output = array();
1431 $tags_info = nodewords_get_possible_tags();
1432 $weights = array();
1433
1434 foreach ($tags as $name => $content) {
1435 if ($content === '') {
1436 continue;
1437 }
1438
1439 // Check if it's a property meta tag (which may contain an XML namespace).
1440 $is_property = (
1441 $output_type == 'head' &&
1442 isset($tags_info[$name]['templates']['head'][$name]) &&
1443 $tags_info[$name]['templates']['head'][$name] == NODEWORDS_META_PROPERTY
1444 );
1445 $parts = $is_property ? array($name, $name) : explode(':', $name);
1446
1447 // Ensure that the previous assigned output template was cleared.
1448 $template = NULL;
1449
1450 if (!isset($parts[1])) {
1451 $parts[1] = $parts[0];
1452 }
1453
1454 if ($output_type == 'update index') {
1455 $bool = (
1456 isset($tags_info[$parts[0]]['templates']['search index'][$parts[1]])
1457 );
1458
1459 if ($bool) {
1460 // The '%content' element will be added later.
1461 $replace = array(
1462 '%attributes' => empty($tags_info[$parts[0]]['attributes'][$parts[1]]) ? '' : drupal_attributes($tags_info[$parts[0]]['attributes'][$parts[1]]),
1463 );
1464 $template = $tags_info[$parts[0]]['templates']['search index'][$parts[1]];
1465 $weight = isset($tags_info[$parts[0]]['weight'][$parts[1]]) ? $tags_info[$parts[0]]['weight'][$parts[1]] : 0;
1466 }
1467 }
1468 else {
1469 $bool = (
1470 isset($tags_info[$parts[0]]['templates']['head'][$parts[1]]) &&
1471 ($meta_name = check_plain(decode_entities(strip_tags($parts[1]))))
1472 );
1473
1474 if ($bool) {
1475 // The '%content' element will be added later.
1476 $replace = array(
1477 '%name' => $meta_name,
1478 '%attributes' => empty($tags_info[$parts[0]]['attributes'][$parts[1]]) ? '' : drupal_attributes($tags_info[$parts[0]]['attributes'][$parts[1]]),
1479 );
1480 $template = $tags_info[$parts[0]]['templates']['head'][$parts[1]];
1481 $weight = isset($tags_info[$parts[0]]['weight'][$parts[1]]) ? $tags_info[$parts[0]]['weight'][$parts[1]] : 0;
1482
1483 if (!is_string($template)) {
1484 switch ($template) {
1485 case NODEWORDS_META:
1486 $template = '<meta name="%name" content="%content"%attributes />';
1487 break;
1488
1489 case NODEWORDS_HTTP_EQUIV:
1490 $template = '<meta http-equiv="%name" content="%content"%attributes />';
1491 break;
1492
1493 case NODEWORDS_LINK_REL:
1494 $template = '<link rel="%name" href="%content"%attributes />';
1495 break;
1496
1497 case NODEWORDS_LINK_REV:
1498 $template = '<link rev="%name" href="%content"%attributes />';
1499 break;
1500
1501 // This requires theme customizations in order for the output to
1502 // remain valid XHTML, see the OpenGraph section of the README.txt
1503 // for further details.
1504 case NODEWORDS_META_PROPERTY:
1505 $template = '<meta property="%name" content="%content"%attributes />';
1506 break;
1507
1508 default:
1509 $template = '';
1510 break;
1511 }
1512 }
1513 }
1514 }
1515
1516 if (!empty($template)) {
1517 // Allow the meta tags to have multiple values.
1518 if (!empty($tags_info[$name]['multiple'])) {
1519 $content = explode("\n", $content);
1520 }
1521
1522 // Simplify the output handling.
1523 if (!is_array($content)) {
1524 $content = array($content);
1525 }
1526
1527 // Now add the content element(s) to the replacement array.
1528 foreach ($content as $content_item) {
1529 $replace['%content'] = str_replace('&#039;', "'", trim(check_plain(decode_entities(strip_tags($content_item)))));
1530 $output[] = strtr($template, $replace);
1531 $weights[] = $weight;
1532 }
1533 }
1534 }
1535
1536 if (count($output)) {
1537 array_multisort($weights, $output);
1538
1539 return implode("\n", $output);
1540 }
1541 else {
1542 return '';
1543 }
1544 }
1545
1546 /**
1547 * Provide a complete path for the current URL, optionally using a value
1548 * manually assigned via a form.
1549 *
1550 * @param $content
1551 * An array of settings for this current field, optionally including the
1552 * manually assigned path.
1553 * @param $options
1554 * An array of options for this current page.
1555 *
1556 * @return
1557 * Fully qualified absolute URL for the current page.
1558 */
1559 function _nodewords_prepare_path($content, $options) {
1560 // Need to compare the value against system base path.
1561 $base_path = base_path();
1562
1563 // Remove the base path from the front of the URL.
1564 if (!empty($content['value']) && strpos($content['value'], $base_path) === 0) {
1565 $content['value'] = drupal_substr($content['value'], drupal_strlen($base_path));
1566 }
1567
1568 // Start with a system path.
1569 if (empty($content['value'])) {
1570 $path = '';
1571
1572 switch ($options['type']) {
1573 case NODEWORDS_TYPE_FRONTPAGE:
1574 $path = '<front>';
1575 break;
1576
1577 case NODEWORDS_TYPE_NODE:
1578 $path = 'node/' . $options['id'];
1579 break;
1580
1581 case NODEWORDS_TYPE_PAGE:
1582 $path = $_GET['q'];
1583 break;
1584
1585 case NODEWORDS_TYPE_TERM:
1586 // Accommodate other modules that change the path via hook_term_path().
1587 $term = taxonomy_get_term($options['id']);
1588 $path = module_invoke('taxonomy', 'term_path', $term);
1589 break;
1590
1591 case NODEWORDS_TYPE_USER:
1592 $path = 'user/' . $options['id'];
1593 break;
1594 }
1595
1596 if (!empty($path)) {
1597 $content['value'] = $path;
1598 }
1599 }
1600
1601 if (!empty($content['value'])) {
1602 return check_url(nodewords_url($content['value'], $options));
1603 }
1604 else {
1605 return '';
1606 }
1607 }
1608
1609 /**
1610 * Extract or generate the description for the current object.
1611 *
1612 * @param $tags
1613 * The array of tags currently generated.
1614 * @param $content
1615 * An array of settings for this current field, optionally including the
1616 * manually assigned path.
1617 * @param $options
1618 * An array of options for this current page.
1619 * @param $tag_name
1620 * The name of the tag being generated.
1621 *
1622 * @return
1623 * The final string to be used for the description tag.
1624 */
1625 function _nodewords_prepare_description(&$tags, $content, $options, $tag_name) {
1626 // Will need this.
1627 if (!isset($content['value'])) {
1628 $content['value'] = '';
1629 }
1630
1631 // If this is a node, try to load it then see if the description can or needs
1632 // to be automatically generated.
1633 if ($options['type'] == NODEWORDS_TYPE_NODE && $node = node_load($options['id'])) {
1634 $autogenerate = variable_get('nodewords_metatags_generation_method_' . $node->type, NODEWORDS_GENERATION_WHEN_EMPTY);
1635
1636 if ($autogenerate == NODEWORDS_GENERATION_ALWAYS
1637 || (empty($content['value']) && $autogenerate == NODEWORDS_GENERATION_WHEN_EMPTY)) {
1638 $content['value'] = nodewords_metatag_from_node_content($node, $content['value']);
1639 }
1640 }
1641
1642 // Load the default if no value is present.
1643 if (empty($content['value']) && !empty($options['default'][$tag_name]['value'])) {
1644 $content['value'] = $options['default'][$tag_name]['value'];
1645 }
1646
1647 $tags[$tag_name] = $content['value'];
1648 }