page_title.module 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  1. <?php
  2. /**
  3. * @file
  4. * Enhanced control over the page title (in the head tag).
  5. *
  6. * This module gives you control over the page title. It gives you the chance
  7. * to provide patterns for how the title should be structured, and on node
  8. * pages, gives you the chance to specify the page title rather than defaulting
  9. * to the node title.
  10. */
  11. /**
  12. * Implement hook_help().
  13. */
  14. function page_title_help($path, $arg) {
  15. $output = NULL;
  16. switch ($path) {
  17. case 'admin/config/search/page-title' :
  18. $output = '<p>' . t('Page Title provides control over the &lt;title> element on a page using token patterns and an optional textfield to override the title of the item (be it a node, term, user or other). The Token Scope column lets you know which tokens are available for this field (Global is always available). Please click on the <strong><em>more help&hellip;</em></strong> link below if you need further assistance.') . '</p>';
  19. $output .= '<p>' . l(t('More Help...'), 'admin/help/page_title') . '</p>';
  20. break;
  21. case 'admin/help#page_title':
  22. $output = '<p>' . t('Drupal\'s default page title follows one of two patterns:') . '</p>';
  23. $items = array(
  24. t('<strong>Default Page</strong>: <samp><em>page title</em> | <em>site name</em></samp>'),
  25. t('<strong>Default Frontpage</strong>: <samp><em>site name</em> | <em>site slogan</em></samp>'),
  26. );
  27. $output .= theme('item_list', $items, NULL, 'ol');
  28. $output .= '<p>' . t('The <strong>Page Title</strong> module lets you change these defaults in two ways. First, you can adjust the patterns below using the placeholders given. This will change the way the default page titles are created. Second, on enabled forms (curently node, term & user editing forms) you have the option of specifying a title that is different to the title of the item. This field only appears if the <em>Show Field</em> box is checked for the item. If a value is provided it will be used to generate the <samp>[current-page:page-title]</samp> placeholder however if it is left blank the <samp>[current-page:page-title]</samp> token will inherit the item\'s own title.') . '</p>';
  29. $output .= '<p>' . t('The <samp>[current:page-title]</samp> token will default to the value returned from <samp>drupal_get_title</samp> if there is no value specified or no available page title field.') . '</p>';
  30. $output .= '<p>' . t('Certain types of page title pattern have access to special tokens which others do not, depending on their <em>scope</em>. All patterns have access to the <strong>Global</strong> scope. Content type patterns have access to the <strong>Node</strong> tokens, vocabulary patterns have access to the <strong>Taxonomy</strong> tokens and finally the user patterns have access to the <strong>User</strong> tokens.') . '</p>';
  31. break;
  32. }
  33. return $output;
  34. }
  35. /**
  36. * Implements hook_requirements().
  37. */
  38. function page_title_requirements($phase) {
  39. $requirements = array();
  40. if ($phase == 'runtime') {
  41. // Are we on an old version?
  42. if (!page_title_is_up_to_date()) {
  43. $requirements['page_title_version'] = array(
  44. 'title' => t('Page title version'),
  45. 'value' => t('Out of date'),
  46. 'description' => t('The Page Title module must be updated. You should run the !link immediately.', array('!link' => l(t('database update script'), 'update.php'))),
  47. 'severity' => REQUIREMENT_ERROR,
  48. );
  49. }
  50. // Nope - we're on the latest version
  51. else {
  52. // Does the old table exist (it is left after the upgrade in case an admin wants to check the upgrade went ok)
  53. if (db_table_exists('page_title_old')) {
  54. $requirements['upgrade_table'] = array(
  55. 'title' => t('Page Title upgrade table present'),
  56. 'value' => '',
  57. 'description' => t('The Page Title upgrade table (<code>page_title_old</code>) is present. You can remove it !link', array(
  58. '!link' => l(t('using this script'), 'admin/settings/page-title/drop-old-table'),
  59. )),
  60. 'severity' => REQUIREMENT_WARNING,
  61. );
  62. }
  63. // If the page title module exists, check it has the right columns - there are reports of upgrade issues.
  64. // If the table doesn't exists, reinstall the module!
  65. if (!db_table_exists('page_title') || db_field_exists('page_title', 'nid')) {
  66. $requirements['page_title_version'] = array(
  67. 'title' => t('Page title version'),
  68. 'value' => t('Incorrect Schema'),
  69. 'description' => t('It appears Drupal thinks the module is up to date, however the database schema is incorrect. Please uninstall and reinstall the module.'),
  70. 'severity' => REQUIREMENT_ERROR,
  71. );
  72. }
  73. else {
  74. // Everything seems ok...
  75. $rows = db_query('SELECT COUNT(*) FROM {page_title}')->fetchField();
  76. $requirements['page_title_version'] = array(
  77. 'title' => t('Page title version'),
  78. 'value' => t('Enabled (<code>page_title</code> table contains !rows)', array('!rows' => format_plural($rows, '1 row', '@count rows'))),
  79. 'severity' => REQUIREMENT_OK,
  80. );
  81. }
  82. }
  83. }
  84. return $requirements;
  85. }
  86. /**
  87. * Implement hook_perm().
  88. */
  89. function page_title_permission() {
  90. return array(
  91. 'set page title' => array(
  92. 'title' => t('Set Page Title'),
  93. 'description' => t('Allow user to set or modify a page title'),
  94. ),
  95. 'administer page titles' => array(
  96. 'title' => t('Administer Page Title'),
  97. 'description' => t('Perform administration tasks for Page Title'),
  98. ),
  99. );
  100. }
  101. /**
  102. * Implement hook_menu().
  103. */
  104. function page_title_menu() {
  105. $items = array();
  106. $items['admin/config/search/page-title'] = array(
  107. 'title' => 'Page titles',
  108. 'description' => 'Configure the page titles for your site (the title in the &lt;head&gt; tag).',
  109. 'page callback' => 'drupal_get_form',
  110. 'page arguments' => array('page_title_admin_settings'),
  111. 'access arguments' => array('administer page titles'),
  112. 'type' => MENU_NORMAL_ITEM,
  113. 'file' => 'page_title.admin.inc',
  114. );
  115. return $items;
  116. }
  117. /**
  118. * Implement hook_theme().
  119. */
  120. function page_title_theme() {
  121. return array(
  122. 'page_title_admin_settings' => array(
  123. 'template' => 'page_title-admin-settings-form',
  124. 'render element' => 'form',
  125. 'file' => 'page_title.admin.inc',
  126. ),
  127. 'page_title_preprocess_html' => array(
  128. 'arguments' => array('vars' => NULL),
  129. ),
  130. );
  131. }
  132. /**
  133. * Implement hook_node_type().
  134. *
  135. * Updates settings after a node type change.
  136. */
  137. function page_title_node_type($op, $info) {
  138. // Handle a content type rename
  139. if ($op == 'update' && !empty($info->old_type) && $info->type != $info->old_type) {
  140. // Load the old node type settings.
  141. $temp = variable_get('page_title_type_' . $info->old_type, '');
  142. // If the settings aren't empty, then save them into the new type
  143. if (!empty($temp)) {
  144. variable_set('page_title_type_' . $info->type, $temp);
  145. }
  146. // Delete the old setting
  147. variable_del('page_title_type_' . $info->old_type);
  148. // Essentially, do the same as above but with the _showfield suffix for the node type
  149. $temp = variable_get('page_title_type_' . $info->old_type . '_showfield', 0);
  150. if ($temp) {
  151. variable_set('page_title_type_' . $info->type . '_showfield', $temp);
  152. }
  153. variable_del('page_title_type_' . $info->old_type . '_showfield');
  154. }
  155. // If deleted, remove the variables
  156. if ($op == 'delete') {
  157. variable_del('page_title_type_' . $info->type);
  158. variable_del('page_title_type_' . $info->type . '_showfield');
  159. }
  160. }
  161. /**
  162. * Implement hook_form_alter().
  163. * (cant use hook_form_FORM_ID_alter(). here as the form ID changes from node to node)
  164. */
  165. function page_title_form_alter(&$form, $form_state, $form_id) {
  166. // If we dont have permission to set the title then we need to abort this alter now!
  167. if (!user_access('set page title')) return;
  168. // If we're editing a node...
  169. if (!empty($form['#node_edit_form'])) {
  170. // ... and the show field is enabled for this node type
  171. if (variable_get('page_title_type_' . $form['type']['#value'] . '_showfield', 0)) {
  172. $page_title = isset($form['#node']->page_title) ? $form['#node']->page_title : NULL;
  173. $form['page_title'] = array(
  174. '#type' => 'fieldset',
  175. '#title' => t('Page title settings'),
  176. '#collapsible' => TRUE,
  177. '#collapsed' => empty($page_title),
  178. '#group' => 'additional_settings',
  179. '#weight' => 35,
  180. '#attached' => array(
  181. 'js' => array(drupal_get_path('module', 'page_title') . '/page_title.js'),
  182. ),
  183. );
  184. $form['page_title']['page_title'] = array(
  185. '#type' => 'textfield',
  186. '#title' => t('Page title'),
  187. '#description' => t('Provide a description of this node to appear in the &lt;title&gt; tag which search engines can use in search result listings (optional). It is generally accepted this field should be less than 70 characters.'),
  188. '#default_value' => $page_title,
  189. '#size' => 60,
  190. '#maxlength' => 255,
  191. );
  192. }
  193. }
  194. }
  195. /**
  196. * Implement hook_form_FORM_ID_alter().
  197. */
  198. function page_title_form_user_profile_form_alter(&$form, $form_state) {
  199. // If we dont have permission to set the title then we need to abort this alter now!
  200. if (!user_access('set page title')) return;
  201. // Check the user profile form has the show field enabled
  202. if (variable_get('page_title_user_showfield', 0)) {
  203. $form['account']['page_title'] = array(
  204. '#type' => 'textfield',
  205. '#title' => t('Page title'),
  206. '#description' => t('Provide a description of this user to appear in the &lt;title&gt; tag which search engines can use in search result listings (optional). It is generally accepted this field should be less than 70 characters.'),
  207. '#default_value' => page_title_load_title($form['#user']->uid, 'user'),
  208. '#size' => 60,
  209. '#maxlength' => 255,
  210. '#weight' => 20,
  211. );
  212. }
  213. }
  214. /**
  215. * Implement hook_form_FORM_ID_alter().
  216. */
  217. function page_title_form_taxonomy_form_term_alter(&$form, $form_state) {
  218. // If we dont have permission to set the title then we need to abort this alter now!
  219. if (!user_access('set page title')) return;
  220. // For some reason, the term delete confirm form is run through the taxonomy_form_term form - skip if this is case!
  221. //if ($form['delete']['#value'] === TRUE) return;
  222. // Check the term's vocab has the show field enabled
  223. if (variable_get('page_title_vocab_' . $form['#vocabulary']->machine_name . '_showfield', 0)) {
  224. $form['advanced']['page_title'] = array(
  225. '#type' => 'textfield',
  226. '#title' => t('Page title'),
  227. '#description' => t('Provide a description of this term to appear in the &lt;title&gt; tag which search engines can use in search result listings (optional). It is generally accepted this field should be less than 70 characters.'),
  228. '#default_value' => isset($form['tid']) ? page_title_load_title($form['tid']['#value'], 'term') : '',
  229. '#size' => 60,
  230. '#maxlength' => 255,
  231. '#weight' => -20,
  232. );
  233. }
  234. }
  235. /**
  236. * Implement hook_form_FORM_ID_alter().
  237. */
  238. function page_title_form_forum_form_container_alter(&$form, $form_state) {
  239. $forum_vid = variable_get('forum_nav_vocabulary', 0);
  240. $forum_vocab = taxonomy_vocabulary_load($forum_vid);
  241. // Check the forum vocab has the show field enabled
  242. if (variable_get('page_title_vocab_' . $forum_vocab->machine_name . '_showfield', 0)) {
  243. $form['page_title'] = array(
  244. '#type' => 'textfield',
  245. '#title' => t('Page title'),
  246. '#description' => t('Provide a description of this forum to appear in the &lt;title&gt; tag which search engines can use in search result listings (optional). It is generally accepted this field should be less than 70 characters.'),
  247. '#default_value' => isset($form['tid']) ? page_title_load_title($form['tid']['#value'], 'term') : '',
  248. '#size' => 60,
  249. '#maxlength' => 255,
  250. '#weight' => -20,
  251. );
  252. }
  253. }
  254. /**
  255. * Implement hook_form_FORM_ID_alter().
  256. * (We can re-use the above function)
  257. */
  258. function page_title_form_forum_form_forum_alter(&$form, $form_state) {
  259. page_title_form_forum_form_container_alter($form, $form_state);
  260. }
  261. /**
  262. * Implement hook_form_FORM_ID_alter().
  263. */
  264. function page_title_form_node_type_form_alter(&$form, $form_state) {
  265. // If we dont have permission to administer the title then we need to abort this alter now!
  266. if (!user_access('administer page title')) return;
  267. // Add the node-type specific page title settings to the additional settings section
  268. $form['page_title'] = array(
  269. '#type' => 'fieldset',
  270. '#title' => t('Page Title Settings'),
  271. '#collapsible' => TRUE,
  272. '#collapsed' => TRUE,
  273. '#tree' => TRUE,
  274. '#group' => 'additional_settings',
  275. );
  276. $form['page_title']['show_field'] = array(
  277. '#type' => 'checkboxes',
  278. '#title' => t('Page Title Field'),
  279. '#description' => t('If checked, the <em>Page Title</em> field will appear on the node edit form for those who have permission to set the title.'),
  280. '#options' => array(
  281. 'show_field' => t('Show field'),
  282. ),
  283. '#default_value' => variable_get('page_title_type_' . $form['#node_type']->type . '_showfield', 0) ? array('show_field') : array(),
  284. );
  285. $form['page_title']['pattern'] = array(
  286. '#type' => 'textfield',
  287. '#title' => t('Page Title Pattern'),
  288. '#default_value' => variable_get('page_title_type_' . $form['#node_type']->type, ''),
  289. '#description' => t('Enter the <em>Page Title</em> pattern you want to use for this node type. For more information, please use the !link settings page', array('!link' => l('Page Title', 'admin/config/search/page-title'))),
  290. '#maxlength' => 255,
  291. );
  292. $form['#submit'][] = 'page_title_node_type_form_submit';
  293. }
  294. /**
  295. * Submit handler for the node_type_form element added in the hook_form_FORM_ID_alter() above.
  296. */
  297. function page_title_node_type_form_submit($form, &$form_state) {
  298. $show_field = $form_state['values']['page_title']['show_field']['show_field'] ? 1 : 0;
  299. variable_set('page_title_type_' . $form_state['values']['type'] . '_showfield', $show_field);
  300. variable_set('page_title_type_' . $form_state['values']['type'], $form_state['values']['page_title']['pattern']);
  301. // For some reason the node module adds the fieldset as a separate entry in the variables table... we dont want this!
  302. variable_del('page_title_' . $form_state['values']['type']);
  303. // Flush the settings on update/insert.
  304. page_title_get_settings(TRUE);
  305. }
  306. /**
  307. * Implement hook_node_load().
  308. */
  309. function page_title_node_load($nodes) {
  310. $nids = array();
  311. // Get the settings
  312. $settings = page_title_get_settings();
  313. // Get a list of node nids to fetch page_title's later
  314. foreach ($nodes AS $node) {
  315. // Check the node type has the 'Show Field' enabled, otherwise there is no point querying for the data
  316. if (isset($settings['page_title_type_' . $node->type]) && $settings['page_title_type_' . $node->type]['show field']) {
  317. $nids[] = $node->nid;
  318. }
  319. // Otherwise, set a blank value on the node
  320. else {
  321. $nodes[$node->nid] = '';
  322. }
  323. }
  324. // If we have ended up with no nodes to load titles for, lets not query...
  325. if (empty($nids)) {
  326. return;
  327. }
  328. // Fetch page_title information from database and assign it to nodes
  329. // TODO - Can we make this better? IN() queries dont scale welll..
  330. $result = db_query('SELECT page_title, id FROM {page_title} WHERE type = :type AND id IN (:nids)', array(':type' => 'node', ':nids' => $nids));
  331. foreach ($result AS $record) {
  332. $nodes[$record->id]->page_title = $record->page_title;
  333. }
  334. }
  335. /**
  336. * Implement hook_node_insert().
  337. */
  338. function page_title_node_insert($node) {
  339. if (user_access('set page title') && isset($node->page_title) && drupal_strlen(trim($node->page_title)) > 0) {
  340. db_insert('page_title')->fields(array('type' => 'node', 'id' => $node->nid, 'page_title' => $node->page_title))->execute();
  341. }
  342. }
  343. /**
  344. * Implement hook_node_update().
  345. */
  346. function page_title_node_update($node) {
  347. if (user_access('set page title') && isset($node->page_title)) {
  348. // If there is content to the Page Title, 'merge' it (ie, either UPDATE or INSERT)
  349. if (drupal_strlen(trim($node->page_title)) > 0) {
  350. db_merge('page_title')->key(array('type' => 'node', 'id' => $node->nid))->fields(array('page_title' => $node->page_title))->execute();
  351. }
  352. // Otherwise, delete the row (if there is one)
  353. else {
  354. db_delete('page_title')->condition('type', 'node')->condition('id', $node->nid)->execute();
  355. }
  356. }
  357. }
  358. /**
  359. * Implement hook_node_delete().
  360. */
  361. function page_title_node_delete($node) {
  362. db_delete('page_title')->condition('type', 'node')->condition('id', $node->nid)->execute();
  363. }
  364. /**
  365. * Implement hook_taxonomy_term_update().
  366. */
  367. function page_title_taxonomy_term_update($term) {
  368. if (user_access('set page title')) {
  369. if (isset($term->page_title) && drupal_strlen(trim($term->page_title)) > 0) {
  370. db_merge('page_title')->key(array('type' => 'term', 'id' => $term->tid))->fields(array('page_title' => $term->page_title))->execute();
  371. }
  372. else {
  373. page_title_taxonomy_term_delete($term);
  374. }
  375. }
  376. }
  377. /**
  378. * Implement hook_taxonomy_term_delete().
  379. */
  380. function page_title_taxonomy_term_delete($term) {
  381. db_delete('page_title')->condition('type', 'term')->condition('id', $term->tid)->execute();
  382. }
  383. /**
  384. * Implement hook_taxonomy_term_insert().
  385. */
  386. function page_title_taxonomy_term_insert($term) {
  387. if (user_access('set page title') && isset($term->page_title) && drupal_strlen(trim($term->page_title)) > 0) {
  388. db_insert('page_title')->fields(array('type' => 'term', 'id' => $term->tid, 'page_title' => $term->page_title))->execute();
  389. }
  390. }
  391. /**
  392. * Implement hook_taxonomy_vocabulary_insert().
  393. */
  394. function page_title_taxonomy_vocabulary_insert($vocabulary) {
  395. // Flush the settings on insert.
  396. page_title_get_settings(TRUE);
  397. }
  398. /**
  399. * Implement hook_taxonomy_vocabulary_update().
  400. */
  401. function page_title_taxonomy_vocabulary_update($vocabulary) {
  402. // Flush the settings on update.
  403. page_title_get_settings(TRUE);
  404. }
  405. /**
  406. * Implement hook_taxonomy_vocabulary_delete().
  407. */
  408. function page_title_taxonomy_vocabulary_delete($vocabulary) {
  409. // Flush the settings on update.
  410. page_title_get_settings(TRUE);
  411. }
  412. /**
  413. * Implement hook_user_insert().
  414. */
  415. function page_title_user_insert(&$edit, &$account, $category) {
  416. if (user_access('set page title') && isset($edit['page_title']) && drupal_strlen(trim($edit['page_title'])) > 0) {
  417. db_insert('page_title')->fields(array('type' => 'user', 'id' => $account->uid, 'page_title' => $edit['page_title']))->execute();
  418. }
  419. }
  420. /**
  421. * Implement hook_user_update().
  422. */
  423. function page_title_user_update(&$edit, &$account, $category) {
  424. if (user_access('set page title')) {
  425. if (isset($edit['page_title']) && drupal_strlen(trim($edit['page_title'])) > 0) {
  426. db_merge('page_title')->key(array('type' => 'user', 'id' => $account->uid))->fields(array('page_title' => $edit['page_title']))->execute();
  427. }
  428. else {
  429. db_delete('page_title')->condition('type', 'user')->condition('id', $account->uid)->execute();
  430. }
  431. }
  432. }
  433. /**
  434. * Implement hook_user_cancel().
  435. */
  436. function page_title_user_cancel(&$edit, &$account, $method) {
  437. switch ($method) {
  438. case 'user_cancel_block_unpublish' : break;
  439. case 'user_cancel_reassign' : break;
  440. case 'user_cancel_delete' :
  441. db_delete('page_title')->condition('type', 'user')->condition('id', $account->uid)->execute();
  442. break;
  443. }
  444. }
  445. /**
  446. * Simple wrapper function to get the currently set title for a page
  447. *
  448. * @param $raw
  449. * Optionally set the function to return a result not cleaned by filter_xss. This should be used with caution.
  450. *
  451. * @return
  452. * string the title for the current page
  453. */
  454. function page_title_get_title($raw = FALSE, $flush = FALSE) {
  455. $title = &drupal_static(__FUNCTION__);
  456. if ($flush || is_null($title)) {
  457. // Give other modules the oppertunity to use hook_page_title_alter().
  458. drupal_alter('page_title', $title);
  459. }
  460. // Return the title
  461. return $raw ? $title : filter_xss($title, array());
  462. }
  463. /**
  464. * Gets the page title for a type & id.
  465. *
  466. * @param $id
  467. * int The objects id.
  468. * @param $type
  469. * string What is the scope (usually 'node', 'term' or 'user').
  470. *
  471. * @return
  472. * string the page title for the given type & id.
  473. */
  474. function page_title_load_title($id, $type) {
  475. return db_query('SELECT page_title FROM {page_title} WHERE type = :type AND id = :id', array(':type' => $type, ':id' => $id))->fetchField();
  476. }
  477. /**
  478. * Wrapper for old function...
  479. * NOTE: This has been deprecated in favor of page_title_load_title().
  480. */
  481. function page_title_node_get_title($nid) {
  482. return page_title_load_title($nid, 'node');
  483. }
  484. /**
  485. * Legacy page title setting function...
  486. * NOTE: This has been deprecated in favour of hook_page_title_alter().
  487. */
  488. function page_title_set_title($title = NULL) {
  489. $stored_title = &drupal_static(__FUNCTION__);
  490. if (isset($title)) {
  491. $stored_title = $title;
  492. }
  493. return $stored_title;
  494. }
  495. /**
  496. * Determines what title should be sent to the page template.
  497. *
  498. * This function gets called from the implementation of hook_preprocess_html
  499. *
  500. * @param $raw
  501. * Optionally get the result without cleaning it with filter_xss. This should be used with caution.
  502. *
  503. * @return
  504. * string The page's title.
  505. */
  506. function page_title_page_get_title($raw = FALSE) {
  507. $title = &drupal_static(__FUNCTION__);
  508. if (is_null($title)) {
  509. $types = array('global' => NULL);
  510. // Allow hook_page_title_pattern_alter() to modify the pattern and tokens
  511. drupal_alter('page_title_pattern', $page_title_pattern, $types);
  512. // If pattern is empty (either if the type is not overridable or simply not set) fallback to the default pattern
  513. if (empty($page_title_pattern)) {
  514. $settings = page_title_get_settings();
  515. $page_title_pattern = variable_get('page_title_default', $settings['page_title_default']['default']);
  516. }
  517. // Append the pattern for pages with a pager on them
  518. $page_title_pattern .= isset($_REQUEST['page']) ? variable_get('page_title_pager_pattern', '') : '';
  519. // Apply token patterns using token_replace
  520. $title = token_replace($page_title_pattern, $types, array('sanitize' => FALSE));
  521. }
  522. // Trim trailing whitespace from the title
  523. $title = trim($title);
  524. return $raw ? $title : filter_xss($title);
  525. }
  526. /**
  527. * Implement hook_preprocess_html().
  528. */
  529. function page_title_preprocess_html(&$vars) {
  530. $vars['head_title'] = page_title_page_get_title();
  531. }
  532. /**
  533. * Implement hook_init().
  534. */
  535. function page_title_init() {
  536. // Make sure our API includes are included on all page loads.
  537. page_title_include_api_files();
  538. }
  539. /**
  540. * Function to ensure API files are included.
  541. * We use a static variable so we can use include, which is faster than include_one
  542. */
  543. function page_title_include_api_files() {
  544. // Using $runonce, we can ensure the include code below only gets run once.
  545. $runonce = &drupal_static(__FUNCTION__, FALSE);
  546. if ($runonce) return;
  547. // Include relevant page_title.inc's. We cannot use drupal_load() here due to the folder structure.
  548. // We also avoice using include_once due to its performance hit on the Filesystem
  549. foreach (page_title_get_module_apis() as $module => $info) {
  550. if (file_exists(DRUPAL_ROOT . "/{$info['path']}/{$module}.page_title.inc")) {
  551. include DRUPAL_ROOT . "/{$info['path']}/{$module}.page_title.inc";
  552. }
  553. }
  554. $runonce = TRUE;
  555. }
  556. /**
  557. * Form Alter handler for the views ui config form (used for filters and args)
  558. */
  559. function page_title_form_views_ui_config_item_form_alter(&$form, &$form_state) {
  560. // Don't bother altering non-argument forms
  561. if ($form_state['type'] != 'argument') return;
  562. $view = &$form_state['view'];
  563. $display_handler = &$view->display_handler;
  564. // Check the display handler is a page - if not, dont bother altering.
  565. if ($display_handler->display->display_plugin != 'page_with_page_title') return;
  566. // Now check the display has arguments. This ensures we are on an overidden Contextual Filter
  567. if (empty($display_handler->options['arguments'])) return;
  568. list($display_id, $section, $section_id) = explode('-', $form['#secton']);
  569. $argument_handler = &$form_state['handler'];
  570. // Build a page title options fieldset wrapper
  571. $form['options']['page_title_pattern'] = array(
  572. '#type' => 'fieldset',
  573. '#title' => t('Page Title'),
  574. '#collapsible' => TRUE,
  575. '#collapsed' => TRUE,
  576. '#weight' => 120,
  577. );
  578. // Add the Page Title field
  579. $form['options']['page_title_pattern']['page_title_pattern'] = array(
  580. '#type' => 'textfield',
  581. '#title' => t('Page Title Pattern'),
  582. '#description' => t('Optionally enter a Page Title Pattern for this argument. This will override the main view Page Title Pattern. You can also use the tokens below.'),
  583. '#default_value' => $argument_handler->options['page_title_pattern'],
  584. '#parents' => array('options', 'page_title_pattern'),
  585. '#element_validate' => array('token_element_validate_token_context'),
  586. '#token_types' => array(),
  587. );
  588. // Add the token help to a collapsed fieldset at the end of the configuration page.
  589. $form['options']['page_title_pattern']['token_help'] = array(
  590. '#type' => 'fieldset',
  591. '#title' => t('Available Tokens List'),
  592. '#collapsible' => TRUE,
  593. '#collapsed' => TRUE,
  594. );
  595. $form['options']['page_title_pattern']['token_help']['content'] = array(
  596. '#theme' => 'token_tree',
  597. '#token_types' => array(),
  598. );
  599. $form['buttons']['submit']['#submit'][] = 'page_title_form_views_ui_config_item_form_alter_submit';
  600. }
  601. /**
  602. * Submit handler for the above Argument handling code
  603. */
  604. function page_title_form_views_ui_config_item_form_alter_submit($form, &$form_state) {
  605. $options = &$form_state['values']['options'];
  606. $page_title_pattern = isset($options['page_title_pattern']) ? $options['page_title_pattern'] : '';
  607. $view = &$form_state['view'];
  608. $view->set_item_option($form_state['display_id'], $form_state['type'], $form_state['id'], 'page_title_pattern', $page_title_pattern);
  609. views_ui_cache_set($view);
  610. }
  611. /**
  612. * Implements hook_views_api().
  613. */
  614. function page_title_views_api() {
  615. return array(
  616. 'api' => 2,
  617. );
  618. }
  619. /**
  620. * Implements hook_views_plugins().
  621. */
  622. function page_title_views_plugins() {
  623. return array(
  624. 'module' => 'page_title',
  625. 'display' => array(
  626. 'page_with_page_title' => array(
  627. 'title' => t('Page (with Page Title)'),
  628. 'help' => t('Same as a normal Page, but also includes the Page Title control.'),
  629. 'parent' => 'page',
  630. 'uses hook menu' => TRUE,
  631. 'use ajax' => FALSE,
  632. 'use pager' => TRUE,
  633. 'accept attachments' => TRUE,
  634. 'admin' => t('Page with Page Title'),
  635. 'module' => 'page_title',
  636. 'path' => drupal_get_path('module', 'page_title') . '/views/plugins',
  637. 'file' => 'page_title_plugin_display_page_with_page_title.inc',
  638. 'handler' => 'page_title_plugin_display_page_with_page_title',
  639. 'theme' => 'views_view',
  640. 'theme path' => drupal_get_path('module', 'views') . '/theme',
  641. 'theme file' => 'theme.inc',
  642. ),
  643. ),
  644. );
  645. }
  646. /**
  647. * Implements hook_entity_uuid_save().
  648. */
  649. function page_title_uuid_entity_uuid_save(&$entity, $entity_type) {
  650. if (!empty($entity->page_title)) {
  651. // Insert page_title record for this entity.
  652. db_insert('page_title')
  653. ->fields(array(
  654. 'type' => $entity_type,
  655. 'id' => entity_id($entity_type, $entity),
  656. 'page_title' => $entity->page_title,
  657. ))
  658. ->execute();
  659. }
  660. else {
  661. // Delete item so we get an auto-generated value basedo on settings.
  662. db_delete('page_title')
  663. ->condition('type', $entity_type)
  664. ->condition('id', entity_id($entity_type, $entity))
  665. ->execute();
  666. }
  667. }
  668. /**
  669. * Get the Page Title Setttings
  670. */
  671. function page_title_get_settings($flush = FALSE) {
  672. static $settings = NULL;
  673. // Flush the settings, if set.
  674. if ($flush) {
  675. $settings = NULL;
  676. cache_clear_all('page_title:settings', 'cache');
  677. }
  678. // If we have it statically cached, return it.
  679. if (!empty($settings)) {
  680. return $settings;
  681. }
  682. // Get from the cache, if present
  683. if ($cache = cache_get('page_title:settings')) {
  684. $settings = $cache->data;
  685. return $cache->data;
  686. }
  687. // We run this here as there are edge cases where it seems hook_init() and
  688. // cache clearing intefere with each other, casuing INC files to not be included
  689. // See: http://drupal.org/node/1567790
  690. page_title_include_api_files();
  691. // Get the settings from hook_page_title_settings().
  692. $settings = module_invoke_all('page_title_settings');
  693. // For each setting, apply a "default" mask (this makes it easier to use
  694. // later as we can assume presence).
  695. foreach ($settings as $k => $v) {
  696. $settings[$k] = (array) $v + array(
  697. 'label' => '',
  698. 'label arguments' => array(),
  699. 'required' => FALSE,
  700. 'show field' => FALSE,
  701. 'description' => '',
  702. 'description arguments' => array(),
  703. 'weight' => 0,
  704. 'default' => '',
  705. );
  706. }
  707. // Now sort
  708. uasort($settings, '_page_title_settings_sort');
  709. // Cache this so we dont have to do this EVERY time
  710. cache_set('page_title:settings', $settings);
  711. return $settings;
  712. }
  713. /**
  714. * Internal function for sorting the page title settings array.
  715. */
  716. function _page_title_settings_sort($a, $b) {
  717. // Sort by weight and, failing that, label alphabetical.
  718. return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : ($a['label'] < $b['label'] ? -1 : 1));
  719. }
  720. /**
  721. * Get a list of modules that support the current Page Title API.
  722. */
  723. function page_title_get_module_apis() {
  724. // For efficiency, cache the hook implementations and settings
  725. $cache = &drupal_static(__FUNCTION__);
  726. if (!isset($cache)) {
  727. $cache = array();
  728. foreach (module_implements('page_title_api') as $module) {
  729. $function = $module . '_page_title_api';
  730. $info = $function();
  731. if (isset($info['api']) && $info['api'] == 1.000) {
  732. if (!isset($info['path'])) {
  733. $info['path'] = drupal_get_path('module', $module);
  734. }
  735. $cache[$module] = $info;
  736. }
  737. }
  738. }
  739. return $cache;
  740. }
  741. /**
  742. * Implements hook_page_title_api().
  743. */
  744. function page_title_page_title_api() {
  745. return array(
  746. 'api' => 1,
  747. 'path' => drupal_get_path('module', 'page_title') . '/modules',
  748. );
  749. }
  750. /**
  751. * Core implementations of hook_page_title_api().
  752. */
  753. function taxonomy_page_title_api() { return page_title_page_title_api(); }
  754. function node_page_title_api() { return page_title_page_title_api(); }
  755. function comment_page_title_api() { return page_title_page_title_api(); }
  756. function forum_page_title_api() { return page_title_page_title_api(); }
  757. function user_page_title_api() { return page_title_page_title_api(); }
  758. function blog_page_title_api() { return page_title_page_title_api(); }
  759. function views_page_title_api() { return page_title_page_title_api(); }
  760. function uc_catalog_page_title_api() { return page_title_page_title_api(); }
  761. /**
  762. * Internal function for checking if Page Title is ok to run
  763. */
  764. function page_title_is_up_to_date() {
  765. return drupal_get_installed_schema_version('page_title') >= max(drupal_get_schema_versions('page_title'));
  766. }