quicktabs.module 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. <?php
  2. /**
  3. * Implements hook_help().
  4. */
  5. function quicktabs_help($path, $arg) {
  6. switch ($path) {
  7. case 'admin/help#quicktabs':
  8. $output = '<p>' . t('The Quicktabs module allows you to create blocks of tabbed content. Clicking on the tabs makes the corresponding content display instantly (it uses jQuery). The content for each tabbed section can be a node, view, block or another Quicktabs instance. You can create an unlimited number of Quicktabs instances, each of which will automatically have an associated block.') . '</p>';
  9. $output .= '<p>' . t('The <a href="@quicktabs">quicktabs page</a> displays all quicktabs currently available on your site. Create new quicktabs using the <a href="@add-quicktab">add quicktab page</a> (the block containing a new quicktab must also be enabled on the <a href="@blocks">blocks administration page</a>).', array('@quicktabs' => url('admin/structure/quicktabs'), '@add-quicktab' => url('admin/structure/quicktab/add'), '@blocks' => url('admin/structure/block'))) . '</p>';
  10. return $output;
  11. }
  12. if ($path == 'admin/structure/quicktabs' && module_exists('block')) {
  13. return '<p>' . t('Each Quicktabs instance has a corresponding block that is managed on the <a href="@blocks">blocks administration page</a>.', array('@blocks' => url('admin/structure/block'))) . '</p>';
  14. }
  15. }
  16. /**
  17. * Implements hook_menu().
  18. */
  19. function quicktabs_menu() {
  20. $items['admin/structure/quicktabs'] = array(
  21. 'title' => 'Quicktabs',
  22. 'description' => 'Create blocks of tabbed content.',
  23. 'page callback' => 'quicktabs_list',
  24. 'access callback' => 'user_access',
  25. 'access arguments' => array('administer quicktabs'),
  26. 'type' => MENU_NORMAL_ITEM,
  27. 'file' => 'quicktabs.admin.inc',
  28. );
  29. $items['admin/structure/quicktabs/list'] = array(
  30. 'title' => 'List quicktabs',
  31. 'type' => MENU_DEFAULT_LOCAL_TASK,
  32. );
  33. $items['admin/structure/quicktabs/add'] = array(
  34. 'title' => 'Add Quicktabs Instance',
  35. 'page callback' => 'drupal_get_form',
  36. 'page arguments' => array('quicktabs_form', 'add'),
  37. 'access arguments' => array('administer quicktabs'),
  38. 'type' => MENU_LOCAL_ACTION,
  39. 'file' => 'quicktabs.admin.inc',
  40. );
  41. $items['admin/structure/quicktabs/manage/%quicktabs'] = array(
  42. 'title' => 'Edit quicktab',
  43. 'page callback' => 'drupal_get_form',
  44. 'page arguments' => array('quicktabs_form', 'edit', 4),
  45. 'access arguments' => array('administer quicktabs'),
  46. 'file' => 'quicktabs.admin.inc',
  47. );
  48. $items['admin/structure/quicktabs/manage/%quicktabs/edit'] = array(
  49. 'title' => 'Edit quicktab',
  50. 'type' => MENU_DEFAULT_LOCAL_TASK,
  51. 'context' => MENU_CONTEXT_INLINE,
  52. );
  53. $items['admin/structure/quicktabs/manage/%quicktabs/delete'] = array(
  54. 'title' => 'Delete quicktab',
  55. 'page callback' => 'drupal_get_form',
  56. 'page arguments' => array('quicktabs_block_delete', 4),
  57. 'access arguments' => array('administer quicktabs'),
  58. 'type' => MENU_LOCAL_TASK,
  59. 'file' => 'quicktabs.admin.inc',
  60. );
  61. $items['admin/structure/quicktabs/manage/%quicktabs/clone'] = array(
  62. 'title' => 'Clone quicktab',
  63. 'page callback' => 'quicktabs_clone',
  64. 'page arguments' => array(4),
  65. 'access arguments' => array('administer quicktabs'),
  66. 'type' => MENU_LOCAL_TASK,
  67. 'file' => 'quicktabs.admin.inc',
  68. );
  69. $items['admin/structure/quicktabs/manage/%quicktabs/export'] = array(
  70. 'title' => 'Export',
  71. 'page callback' => 'drupal_get_form',
  72. 'page arguments' => array('quicktabs_export_form', 4),
  73. 'access arguments' => array('administer quicktabs'),
  74. 'type' => MENU_LOCAL_TASK,
  75. 'file' => 'quicktabs.admin.inc',
  76. );
  77. $items['quicktabs/ajax'] = array(
  78. 'page callback' => 'quicktabs_ajax',
  79. 'access callback' => 'user_access',
  80. 'access arguments' => array('access content'),
  81. 'type' => MENU_CALLBACK,
  82. );
  83. return $items;
  84. }
  85. /**
  86. * Implements hook_permission().
  87. */
  88. function quicktabs_permission() {
  89. return array(
  90. 'administer quicktabs' => array(
  91. 'title' => t('Administer Quicktabs'),
  92. ),
  93. );
  94. }
  95. /**
  96. * Implements hook_theme().
  97. */
  98. function quicktabs_theme() {
  99. return array(
  100. 'quicktabs_admin_form_tabs' => array(
  101. 'render element' => 'tabs',
  102. 'file' => 'quicktabs.admin.inc',
  103. ),
  104. 'qt_ui_tabs' => array(
  105. 'render element' => 'element',
  106. ),
  107. 'qt_ui_tabs_tabset' => array(
  108. 'render element' => 'tabset',
  109. ),
  110. 'qt_quicktabs' => array(
  111. 'render element' => 'element',
  112. ),
  113. 'qt_quicktabs_tabset' => array(
  114. 'render element' => 'tabset',
  115. ),
  116. 'qt_accordion' => array(
  117. 'render element' => 'element',
  118. ),
  119. 'quicktabs_tab_access_denied' => array(
  120. 'variables' => array('tab'),
  121. ),
  122. );
  123. }
  124. /**
  125. * Implements hook_block_info().
  126. */
  127. function quicktabs_block_info() {
  128. $blocks = array();
  129. foreach (quicktabs_load_multiple() as $qt_name => $quicktabs) {
  130. $blocks[$qt_name]['info'] = $quicktabs->title;
  131. }
  132. return $blocks;
  133. }
  134. /**
  135. * Implements hook_block_view().
  136. */
  137. function quicktabs_block_view($delta = '') {
  138. $block = array();
  139. if ($qt = quicktabs_build_quicktabs($delta)) {
  140. if (isset($qt['content']) && !empty($qt['content'])) {
  141. $block['content'] = $qt['content'];
  142. $block['content']['#contextual_links']['quicktabs'] = array('admin/structure/quicktabs/manage', array($delta));
  143. $block['subject'] = check_plain($qt['#title']);
  144. }
  145. }
  146. return $block;
  147. }
  148. /**
  149. * Constructs a Quicktabs instance.
  150. *
  151. * This function can be called by other modules to programmatically build a
  152. * quicktabs instance.
  153. *
  154. * @param name. The machine name of the Quicktabs instance to build - if a name
  155. * is passed that does not correspond to an existing instance, then it is taken
  156. * to be a completely custom instance and is built from only the custom tabs
  157. * that are passed in.
  158. *
  159. * @param settings. An array of settings that will override the options of the Quicktabs
  160. * instance from the database, or if no existing instance is being used, these
  161. * will override the default settings. Possible keys are 'style', 'hide_empty_tabs',
  162. * ajax', 'default_tab', 'renderer', 'title' and 'options'.
  163. *
  164. * @param custom_tabs. An array representing custom tab contents, which will be
  165. * appended to the Quicktabs instance from the database, or if no existing instance
  166. * is being used, the custom tabs will be the entire contents. An example custom_tabs
  167. * array would be array(array('title' => 'custom', 'contents' => array('#markup' =>
  168. * t('Some markup'), 'weight' => 5));
  169. *
  170. * @return A render array that can be used as block content in hook_block_view
  171. * (see quicktabs_block_view()), but can also just be added to the page array
  172. * during hook_page_alter, or output anywhere else where it's sure to get
  173. * passed through drupal_render().
  174. */
  175. function quicktabs_build_quicktabs($name, $settings = array(), $custom_tabs = array()) {
  176. if ($info = quicktabs_load($name)) {
  177. // Allow other modules to alter the Quicktabs instance before it gets output.
  178. drupal_alter('quicktabs', $info);
  179. $info = (array) $info;
  180. $settings = array_merge($info, $settings);
  181. $contents = $settings['tabs'];
  182. unset($settings['tabs'], $settings['machine_name']);
  183. }
  184. elseif (!empty($custom_tabs)) {
  185. // We'll be creating a custom Quicktabs instance. Make sure we're using an
  186. // alphanumeric name.
  187. $name = preg_replace('/[^[a-zA-Z]_]/', '_', $name);
  188. $contents = array();
  189. }
  190. else {
  191. // If $name doesn't correspond to an existing Quicktabs instance, and there
  192. // are no custom tabs to render, then we have nothing to do.
  193. return array();
  194. }
  195. $renderer = isset($settings['renderer']) ? $settings['renderer'] : 'quicktabs';
  196. unset($settings['renderer']);
  197. foreach ($custom_tabs as &$tab) {
  198. $tab += array(
  199. 'type' => 'prerendered',
  200. 'weight' => 0,
  201. );
  202. }
  203. $contents = array_merge($custom_tabs, $contents);
  204. $weight = array();
  205. foreach ($contents as $key => $item) {
  206. // Load the plugin responsible for rendering this item, if it is not a
  207. // prerendered tab.
  208. if ($item['type'] != 'prerendered') {
  209. ctools_plugin_load_class('quicktabs', 'contents', $item['type'], 'handler');
  210. }
  211. // Add item's weight to our weights array so that we can then sort by weight.
  212. $weight[$key] = $item['weight'];
  213. // Make sure we're not going to try to load the same QuickSet instance
  214. // inside itself.
  215. if ($item['type'] == 'qtabs' && $item['machine_name'] == $name) {
  216. unset($contents[$key]);
  217. unset($weight[$key]);
  218. }
  219. }
  220. // Only sort by weight if the tabs haven't already been sorted by some other
  221. // mechanism, e.g. Views in the case of the Views style plugin.
  222. if (!isset($settings['sorted']) || !$settings['sorted']) {
  223. array_multisort($weight, SORT_ASC, $contents);
  224. }
  225. else {
  226. unset($settings['sorted']);
  227. }
  228. if ($qt = quickset_renderer_factory($name, $contents, $renderer, $settings)) {
  229. $renderable_qt = array('#title' => $qt->getTitle(), 'content' => $qt->render());
  230. return $renderable_qt;
  231. }
  232. return array();
  233. }
  234. /**
  235. * Ajax callback for tab content.
  236. *
  237. * @param name The machine name of the quicktabs instance.
  238. *
  239. * @param index The tab index we're returning content for.
  240. *
  241. * @param type The type of content we're rendering.
  242. *
  243. * @return a json-formatted ajax commands array.
  244. */
  245. function quicktabs_ajax($name, $index, $type) {
  246. $args = func_get_args();
  247. $variable_args = array_slice($args, 3);
  248. // Add the Quicktabs machine name to the args we pass to the content renderer
  249. array_unshift($variable_args, $name);
  250. $data = QuickSet::ajaxRenderContent($type, $variable_args);
  251. $commands = array();
  252. $tabpage_id = 'quicktabs-tabpage-'. $name .'-' . $index;
  253. $commands[] = ajax_command_append('#quicktabs-container-'. $name, '<div id="' . $tabpage_id .'" class="quicktabs-tabpage">'. $data .'</div>');
  254. $page = array('#type' => 'ajax', '#commands' => $commands);
  255. ajax_deliver($page);
  256. }
  257. /**
  258. * Load the quicktabs data for a particular instance.
  259. */
  260. function quicktabs_load($name) {
  261. $qts = quicktabs_load_multiple(array($name));
  262. return isset($qts[$name]) ? $qts[$name] : NULL;
  263. }
  264. /**
  265. * Load the quicktabs data.
  266. */
  267. function quicktabs_load_multiple($names = array()) {
  268. ctools_include('export');
  269. $defaults = empty($names) ? ctools_export_load_object('quicktabs', 'all') : ctools_export_load_object('quicktabs', 'names', $names);
  270. return $defaults;
  271. }
  272. /**
  273. * Exports the specified Quicktabs instance with translatable strings.
  274. */
  275. function quicktabs_export($qt, $indent = '') {
  276. $output = ctools_export_object('quicktabs', $qt, $indent);
  277. $translatables = array();
  278. if (!empty($qt->title)) {
  279. $translatables[] = $qt->title;
  280. }
  281. foreach ($qt->tabs as $tab) {
  282. $translatables[] = $tab['title'];
  283. }
  284. $translatables = array_filter(array_unique($translatables));
  285. if (!empty($translatables)) {
  286. $output .= "\n";
  287. $output .= "{$indent}// Translatables\n";
  288. $output .= "{$indent}// Included for use with string extractors like potx.\n";
  289. sort($translatables);
  290. foreach ($translatables as $string) {
  291. $output .= "{$indent}t(" . ctools_var_export($string) . ");\n";
  292. }
  293. $output .= "\n";
  294. }
  295. return $output;
  296. }
  297. /**
  298. * Implements hook_i18n_string_info()
  299. */
  300. function quicktabs_i18n_string_info() {
  301. $groups['quicktabs'] = array(
  302. 'title' => t('Quicktabs'),
  303. 'description' => t('Vocabulary titles and term names for localizable quicktabs.'),
  304. 'format' => FALSE, // This group doesn't have strings with format
  305. 'list' => TRUE, // This group can list all strings
  306. );
  307. return $groups;
  308. }
  309. function quicktabs_translate($name, $string, $langcode = NULL, $textgroup = 'quicktabs') {
  310. return function_exists('i18n_string') ? i18n_string($textgroup . ':' . $name, $string, array('langcode' => $langcode)) : $string;
  311. }
  312. /**
  313. * Update translatable strings.
  314. */
  315. function quicktabs_i18n_update_strings($names = array()) {
  316. if (!function_exists('i18n_string_update')) return;
  317. $qts = quicktabs_load_multiple($names);
  318. foreach ($qts as $name => $quicktabs) {
  319. i18n_string_update("quicktabs:title:$name", $quicktabs->title);
  320. foreach ($quicktabs->tabs as $tabkey => $tab) {
  321. i18n_string_update("quicktabs:tab:$name-$tabkey:title", $tab['title']);
  322. }
  323. }
  324. }
  325. /**
  326. * Implements hook_i18n_string_refresh().
  327. *
  328. * Refresh translations for all user-generated strings managed by quicktabs.
  329. * This will load all strings inputted via the quicktabs user interface and
  330. * register them (and their translations, if there are any) with the
  331. * i18n_strings system.
  332. */
  333. function quicktabs_i18n_string_refresh($group) {
  334. if ($group === 'quicktabs') {
  335. quicktabs_i18n_update_strings();
  336. }
  337. return TRUE;
  338. }
  339. /**
  340. * Implements hook_ctools_plugin_type().
  341. */
  342. function quicktabs_ctools_plugin_type() {
  343. return array(
  344. // Renderer plugins control the display of sets of items, e.g. as tabs.
  345. 'renderers' => array(
  346. 'cache' => TRUE,
  347. 'use hooks' => TRUE,
  348. 'classes' => array('handler'),
  349. ),
  350. // Content plugins control the display of individual items.
  351. 'contents' => array(
  352. 'cache' => TRUE,
  353. 'use hooks' => TRUE,
  354. 'classes' => array('handler'),
  355. )
  356. );
  357. }
  358. /**
  359. * Implements hook_quicktabs_renderers().
  360. */
  361. function quicktabs_quicktabs_renderers() {
  362. $info = array();
  363. $path = drupal_get_path('module', 'quicktabs') . '/plugins';
  364. $info['quicktabs'] = array(
  365. 'path' => $path,
  366. 'handler' => array(
  367. 'file' => 'QuickQuicktabs.inc',
  368. 'class' => 'QuickQuicktabs',
  369. ),
  370. );
  371. $info['ui_tabs'] = array(
  372. 'path' => $path,
  373. 'handler' => array(
  374. 'file' => 'QuickUiTabs.inc',
  375. 'class' => 'QuickUitabs',
  376. ),
  377. );
  378. $info['accordion'] = array(
  379. 'path' => $path,
  380. 'handler' => array(
  381. 'file' => 'QuickAccordion.inc',
  382. 'class' => 'QuickAccordion',
  383. ),
  384. );
  385. return $info;
  386. }
  387. /**
  388. * Implements hook_quicktabs_contents().
  389. */
  390. function quicktabs_quicktabs_contents() {
  391. $info = array();
  392. $path = drupal_get_path('module', 'quicktabs') . '/plugins';
  393. $info['block'] = array(
  394. 'path' => $path,
  395. 'handler' => array(
  396. 'file' => 'QuickBlockContent.inc',
  397. 'class' => 'QuickBlockContent',
  398. ),
  399. 'dependencies' => array('block'),
  400. );
  401. $info['view'] = array(
  402. 'path' => $path,
  403. 'handler' => array(
  404. 'file' => 'QuickViewContent.inc',
  405. 'class' => 'QuickViewContent',
  406. ),
  407. 'dependencies' => array('views'),
  408. );
  409. $info['node'] = array(
  410. 'path' => $path,
  411. 'handler' => array(
  412. 'file' => 'QuickNodeContent.inc',
  413. 'class' => 'QuickNodeContent',
  414. ),
  415. );
  416. $info['qtabs'] = array(
  417. 'path' => $path,
  418. 'handler' => array(
  419. 'file' => 'QuickQtabsContent.inc',
  420. 'class' => 'QuickQtabsContent',
  421. ),
  422. );
  423. $info['callback'] = array(
  424. 'path' => $path,
  425. 'handler' => array(
  426. 'file' => 'QuickCallbackContent.inc',
  427. 'class' => 'QuickCallbackContent',
  428. ),
  429. );
  430. return $info;
  431. }
  432. /**
  433. * Returns a renderered QuickSet.
  434. */
  435. function quickset_renderer_factory($name, $contents, $renderer, $settings) {
  436. return QuickSet::QuickSetRendererFactory($name, $contents, $renderer, $settings);
  437. }
  438. /**
  439. * Returns an object that implements the QuickContent interface.
  440. */
  441. function quick_content_factory($name, $item) {
  442. return QuickContent::factory($name, $item);
  443. }
  444. /**
  445. * Determine if the machine name is in use.
  446. */
  447. function quicktabs_machine_name_exists($machine_name) {
  448. $qt_exists = db_query_range('SELECT 1 FROM {quicktabs} WHERE machine_name = :name', 0, 1, array(':name' => $machine_name))->fetchField();
  449. return $qt_exists;
  450. }
  451. /**
  452. * Implementation of hook_views_api().
  453. */
  454. function quicktabs_views_api() {
  455. return array(
  456. 'api' => 3,
  457. );
  458. }
  459. /**
  460. * Theme function to display the access denied tab.
  461. *
  462. * @ingroup themeable
  463. */
  464. function theme_quicktabs_tab_access_denied($variables) {
  465. return t('You are not authorized to access this content.');
  466. }
  467. /**
  468. * Fetch the necessary CSS files for the tab style.
  469. */
  470. function quicktabs_get_css($style) {
  471. if ($style == 'default') {
  472. // Get the default style.
  473. $style = variable_get('quicktabs_tabstyle', 'nostyle');
  474. }
  475. if ($style == 'nostyle') return array();
  476. $style_css = _quicktabs_get_style_css($style);
  477. return $style_css;
  478. }
  479. /**
  480. * Helper function to get the css file for given style.
  481. */
  482. function _quicktabs_get_style_css($style) {
  483. $tabstyles = &drupal_static(__FUNCTION__);
  484. if (empty($tabstyles)) {
  485. $cid = 'quicktabs_tabstyles';
  486. $cache = cache_get($cid);
  487. if (!$cache) {
  488. $tabstyles = module_invoke_all('quicktabs_tabstyles');
  489. cache_set($cid, $tabstyles);
  490. }
  491. else {
  492. $tabstyles = $cache->data;
  493. }
  494. }
  495. if ($css_file = array_search($style, $tabstyles)) {
  496. return array('data' => $css_file);
  497. }
  498. return array();
  499. }
  500. /**
  501. * Theme function to output tablinks for jQuery UI style tabs.
  502. *
  503. * @ingroup themeable
  504. */
  505. function theme_qt_ui_tabs_tabset($vars) {
  506. $output = '<ul>';
  507. foreach ($vars['tabset']['tablinks'] as $i => $tab) {
  508. if (is_array($tab)) {
  509. $output .= '<li>'. drupal_render($tab) .'</li>';
  510. }
  511. }
  512. $output .= '</ul>';
  513. return $output;
  514. }
  515. /**
  516. * Theme function to output content for jQuery UI style tabs.
  517. *
  518. * @ingroup themeable
  519. */
  520. function theme_qt_ui_tabs($variables) {
  521. $element = $variables['element'];
  522. $output = '<div '. drupal_attributes($element['#options']['attributes']) .'>';
  523. $output .= drupal_render($element['tabs']);
  524. if (isset($element['active'])) {
  525. $output .= drupal_render($element['active']);
  526. }
  527. else {
  528. foreach ($element['divs'] as $div) {
  529. $output .= drupal_render($div);
  530. }
  531. }
  532. $output .= '</div>';
  533. return $output;
  534. }
  535. /**
  536. * Theme function to output tablinks for classic Quicktabs style tabs.
  537. *
  538. * @ingroup themeable
  539. */
  540. function theme_qt_quicktabs_tabset($vars) {
  541. $variables = array(
  542. 'attributes' => array(
  543. 'class' => 'quicktabs-tabs quicktabs-style-' . $vars['tabset']['#options']['style'],
  544. ),
  545. 'items' => array(),
  546. );
  547. foreach (element_children($vars['tabset']['tablinks']) as $key) {
  548. $item = array();
  549. if (is_array($vars['tabset']['tablinks'][$key])) {
  550. $tab = $vars['tabset']['tablinks'][$key];
  551. if ($key == $vars['tabset']['#options']['active']) {
  552. $item['class'] = array('active');
  553. }
  554. $item['data'] = drupal_render($tab);
  555. $variables['items'][] = $item;
  556. }
  557. }
  558. return theme('item_list', $variables);
  559. }
  560. /**
  561. * Theme function to output content for classic Quicktabs style tabs.
  562. *
  563. * @ingroup themeable
  564. */
  565. function theme_qt_quicktabs($variables) {
  566. $element = $variables['element'];
  567. $output = '<div '. drupal_attributes($element['#options']['attributes']) .'>';
  568. $output .= drupal_render($element['tabs']);
  569. $output .= drupal_render($element['container']);
  570. $output .= '</div>';
  571. return $output;
  572. }
  573. /**
  574. * Theme function to output markup for the accordion style.
  575. *
  576. * @ingroup themeable
  577. */
  578. function theme_qt_accordion($variables) {
  579. $element = $variables['element'];
  580. $output = '<div '. drupal_attributes($element['#options']['attributes']) .'>';
  581. foreach ($element['divs'] as $div) {
  582. $output .= drupal_render($div);
  583. }
  584. $output .= '</div>';
  585. return $output;
  586. }