panels_mini.module 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. <?php
  2. /**
  3. * @file panels_mini.module
  4. *
  5. * This module provides mini panels which are basically panels that can be
  6. * used within blocks or other panels.
  7. */
  8. /**
  9. * Implementation of hook_permission().
  10. */
  11. function panels_mini_permission() {
  12. return array(
  13. 'create mini panels' => array(
  14. 'title' => t('Create mini panels'),
  15. 'description' => t('Create new mini panels'),
  16. ),
  17. 'administer mini panels' => array(
  18. 'title' => t('Administer mini panels'),
  19. 'description' => t('Edit and delete mini panels'),
  20. ),
  21. );
  22. }
  23. /**
  24. * Implementation of hook_menu().
  25. */
  26. function panels_mini_menu() {
  27. // Safety: go away if CTools is not at an appropriate version.
  28. if (!defined('PANELS_REQUIRED_CTOOLS_API') || !module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
  29. return array();
  30. }
  31. $items['admin/structure/mini-panels/settings'] = array(
  32. 'title' => 'Settings',
  33. 'page callback' => 'panels_mini_settings',
  34. 'access arguments' => array('create mini panels'),
  35. 'type' => MENU_LOCAL_TASK,
  36. );
  37. // Also provide settings on the main panel UI
  38. $items['admin/structure/panels/settings/panels-mini'] = array(
  39. 'title' => 'Mini panels',
  40. 'page callback' => 'panels_mini_settings',
  41. 'access arguments' => array('create mini panels'),
  42. 'type' => MENU_LOCAL_TASK,
  43. );
  44. return $items;
  45. }
  46. /**
  47. * Settings for mini panels.
  48. */
  49. function panels_mini_settings() {
  50. ctools_include('common', 'panels');
  51. return drupal_get_form('panels_common_settings', 'panels_mini');
  52. }
  53. // ---------------------------------------------------------------------------
  54. // Allow the rest of the system access to mini panels
  55. /**
  56. * Implementation of hook_block_info().
  57. */
  58. function panels_mini_block_info() {
  59. // Safety: go away if CTools is not at an appropriate version.
  60. if (!defined('PANELS_REQUIRED_CTOOLS_API') || !module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
  61. return array();
  62. }
  63. $blocks = array();
  64. $minis = panels_mini_load_all();
  65. foreach ($minis as $panel_mini) {
  66. if (empty($panel_mini->disabled) && (module_exists('page_manager') || empty($panel_mini->requiredcontexts))) {
  67. $blocks[$panel_mini->name] = array(
  68. 'info' => t('Mini panel: "@title"', array('@title' => $panel_mini->admin_title)),
  69. 'cache' => DRUPAL_NO_CACHE,
  70. );
  71. }
  72. }
  73. return $blocks;
  74. }
  75. /**
  76. * Implementation of hook_block_view().
  77. *
  78. * @see panels_mini_panels_mini_content_type_render().
  79. */
  80. function panels_mini_block_view($delta = 0) {
  81. // static recursion protection.
  82. static $viewing = array();
  83. if (!empty($viewing[$delta])) {
  84. return;
  85. }
  86. $viewing[$delta] = TRUE;
  87. $panel_mini = panels_mini_load($delta);
  88. if (empty($panel_mini)) {
  89. // Bail out early if the specified mini panel doesn't exist.
  90. return;
  91. }
  92. ctools_include('context');
  93. $contexts = array();
  94. if (module_exists('page_manager') && $current_page = page_manager_get_current_page()) {
  95. if (!empty($current_page['contexts'])) {
  96. $contexts = ctools_context_match_required_contexts($panel_mini->requiredcontexts, $current_page['contexts']);
  97. }
  98. }
  99. drupal_alter('panels_mini_block_contexts', $contexts, $panel_mini);
  100. $panel_mini->context = $panel_mini->display->context = ctools_context_load_contexts($panel_mini, FALSE, $contexts);
  101. $panel_mini->display->css_id = panels_mini_get_id($panel_mini->name);
  102. $panel_mini->display->owner = $panel_mini;
  103. $block = array();
  104. $block['content'] = panels_render_display($panel_mini->display);
  105. $block['subject'] = $panel_mini->display->get_title();
  106. unset($viewing[$delta]);
  107. return $block;
  108. }
  109. /**
  110. * Implementation of hook_block_configure().
  111. */
  112. function panels_mini_block_configure($delta = 0) {
  113. return array(
  114. 'admin_shortcut' => array(
  115. '#markup' => l(t('Manage this mini-panel'), 'admin/structure/mini-panels/list/' . $delta . '/edit')
  116. ),
  117. );
  118. }
  119. /**
  120. * Implements hook_block_list_alter().
  121. *
  122. * Remove the block if the required contexts are not available.
  123. */
  124. function panels_mini_block_list_alter(&$blocks) {
  125. if (module_exists('page_manager')) {
  126. $current_page = page_manager_get_current_page();
  127. }
  128. // Load add at once to save time.
  129. panels_mini_load_all();
  130. foreach ($blocks as $key => $block) {
  131. if ($block->module != 'panels_mini') {
  132. // This block was added by a contrib module, leave it in the list.
  133. continue;
  134. }
  135. $panel_mini = panels_mini_load($block->delta);
  136. if (empty($panel_mini)) {
  137. // Bail out early if the specified mini panel doesn't exist.
  138. unset($blocks[$key]);
  139. continue;
  140. }
  141. if (!empty($panel_mini->requiredcontexts)) {
  142. if (!$current_page || empty($current_page['contexts'])) {
  143. unset($blocks[$key]);
  144. continue;
  145. }
  146. else {
  147. $required = array();
  148. foreach ($panel_mini->requiredcontexts as $context) {
  149. $info = ctools_get_context($context['name']);
  150. $required[] = new ctools_context_required($context['identifier'], $info['context name']);
  151. }
  152. if (!ctools_context_match_requirements($current_page['contexts'], $required)) {
  153. unset($blocks[$key]);
  154. continue;
  155. }
  156. }
  157. }
  158. }
  159. }
  160. /**
  161. * Implements hook_get_pane_links_alter().
  162. */
  163. function panels_mini_get_pane_links_alter(&$links, $pane, $content_type) {
  164. if ($pane->type == 'panels_mini') {
  165. $links['top']['edit_panels_mini'] = array(
  166. 'title' => t('Edit mini panel'),
  167. 'href' => url('admin/structure/mini-panels/list/' . $pane->subtype . '/edit/content', array('absolute' => TRUE)),
  168. 'attributes' => array('target' => array('_blank')),
  169. );
  170. }
  171. }
  172. /**
  173. * Implements hook_contextual_links_view_alter().
  174. */
  175. function panels_mini_contextual_links_view_alter(&$element, $items) {
  176. // Add contextual links to all mini panel blocks with bid property.
  177. if (isset($element['#element']['#block']) && isset($element['#element']['#block']->bid) && strpos((string) $element['#element']['#block']->bid, 'panels_mini') === 0) {
  178. $admin_pages = array(
  179. t('Configure mini panel settings') => 'basic',
  180. t('Configure mini panel context') => 'context',
  181. t('Configure mini panel layout') => 'layout',
  182. t('Configure mini panel content') => 'content',
  183. );
  184. foreach ($admin_pages as $title => $tail) {
  185. $element['#links']['mini-panels-' . $tail] = array(
  186. 'title' => $title,
  187. 'href' => 'admin/structure/mini-panels/list/' . $element['#element']['#block']->delta . '/edit/' . $tail,
  188. 'query' => drupal_get_destination(),
  189. );
  190. }
  191. }
  192. }
  193. /**
  194. * Statically store all used IDs to ensure all mini panels get a unique id.
  195. */
  196. function panels_mini_get_id($name) {
  197. $id_cache = &drupal_static(__FUNCTION__, array());
  198. $id = 'mini-panel-' . $name;
  199. if (!empty($id_cache[$name])) {
  200. $id .= "-" . $id_cache[$name]++;
  201. }
  202. else {
  203. $id_cache[$name] = 1;
  204. }
  205. return $id;
  206. }
  207. // ---------------------------------------------------------------------------
  208. // Database functions.
  209. /**
  210. * Create a new page with defaults appropriately set from schema.
  211. */
  212. function panels_mini_new($set_defaults = TRUE) {
  213. ctools_include('export');
  214. return ctools_export_new_object('panels_mini', $set_defaults);
  215. }
  216. /**
  217. * Load a single mini panel.
  218. */
  219. function panels_mini_load($name) {
  220. $cache = &drupal_static('panels_mini_load_all', array());
  221. // We use array_key_exists because failed loads will be NULL and
  222. // isset() will try to load it again.
  223. if (!array_key_exists($name, $cache)) {
  224. $cid = 'panels_mini_load:' . $name;
  225. $result = cache_get($cid, 'cache_panels');
  226. if (!empty($result->data)) {
  227. $cache[$name] = $result->data;
  228. }
  229. else {
  230. ctools_include('export');
  231. $result = ctools_export_load_object('panels_mini', 'names', array($name));
  232. if (isset($result[$name])) {
  233. if (empty($result[$name]->display)) {
  234. $result[$name]->display = panels_load_display($result[$name]->did);
  235. if (!empty($result[$name]->title) && empty($result[$name]->display->title)) {
  236. $result[$name]->display->title = $result[$name]->title;
  237. }
  238. }
  239. $cache[$name] = $result[$name];
  240. if (!empty($result[$name]->title) && empty($result[$name]->admin_title)) {
  241. $cache[$name]->admin_title = $result[$name]->title;
  242. }
  243. cache_set($cid, $cache[$name], 'cache_panels', CACHE_TEMPORARY);
  244. }
  245. else {
  246. $cache[$name] = NULL;
  247. }
  248. }
  249. }
  250. if (isset($cache[$name])) {
  251. return $cache[$name];
  252. }
  253. }
  254. /**
  255. * Load all mini panels.
  256. */
  257. function panels_mini_load_all($reset = FALSE) {
  258. $cache = &drupal_static('panels_mini_load_all', array());
  259. static $all_loaded = FALSE;
  260. // We check our own private static because individual minis could have
  261. // been loaded prior to load all and we need to know that.
  262. if (!$all_loaded || $reset) {
  263. $all_loaded = TRUE;
  264. if ($reset) {
  265. $cache = array();
  266. }
  267. else {
  268. $panel_names = db_select('panels_mini', 'pm')
  269. ->fields('pm', array('name'))
  270. ->execute();
  271. $cids = array();
  272. foreach ($panel_names as $name) {
  273. $cids[] = 'panels_mini_load:' . $name->name;
  274. }
  275. $output = cache_get_multiple($cids, 'cache_panels');
  276. foreach ($output as $mini) {
  277. if (!empty($mini->data)) {
  278. $mini = $mini->data;
  279. $cache[$mini->name] = $mini;
  280. }
  281. }
  282. }
  283. ctools_include('export');
  284. $minis = ctools_export_load_object('panels_mini');
  285. $dids = array();
  286. foreach ($minis as $mini) {
  287. if (empty($cache[$mini->name])) {
  288. if (!empty($mini->did)) {
  289. $dids[$mini->did] = $mini->name;
  290. }
  291. else {
  292. // Translate old style titles into new titles.
  293. if (!empty($mini->title) && empty($mini->display->title)) {
  294. $mini->display->title = $mini->title;
  295. }
  296. }
  297. // Translate old style titles into new titles.
  298. if (isset($mini->title) && empty($mini->admin_title)) {
  299. $mini->admin_title = $mini->title;
  300. }
  301. $cache[$mini->name] = $mini;
  302. }
  303. }
  304. $displays = panels_load_displays(array_keys($dids));
  305. foreach ($displays as $did => $display) {
  306. if (!empty($cache[$dids[$did]]->title) && empty($display->title)) {
  307. $display->title = $cache[$dids[$did]]->title;
  308. }
  309. $cache[$dids[$did]]->display = $display;
  310. }
  311. }
  312. // Strip out NULL entries that may have been added by panels_mini_load().
  313. return array_filter($cache);
  314. }
  315. /**
  316. * Write a mini panel to the database.
  317. */
  318. function panels_mini_save(&$mini) {
  319. if (!empty($mini->display)) {
  320. $mini->display->storage_id = $mini->name;
  321. $display = panels_save_display($mini->display);
  322. $mini->did = $display->did;
  323. }
  324. // Clear the panels_mini_load cache.
  325. cache_clear_all('panels_mini_load:', 'cache_panels', TRUE);
  326. $update = (isset($mini->pid) && $mini->pid != 'new') ? array('pid') : array();
  327. drupal_write_record('panels_mini', $mini, $update);
  328. return $mini;
  329. }
  330. /**
  331. * Remove a mini panel.
  332. */
  333. function panels_mini_delete($mini) {
  334. db_delete('panels_mini')
  335. ->condition('name', $mini->name)
  336. ->execute();
  337. if (db_table_exists('block') && $mini->type != t('Overridden')) {
  338. // Also remove from block table as long as there isn't a default that may appear.
  339. db_delete('block')
  340. ->condition('delta', $mini->name)
  341. ->condition('module', 'panels_mini')
  342. ->execute();
  343. }
  344. return panels_delete_display($mini->did);
  345. }
  346. /**
  347. * Export a mini panel.
  348. */
  349. function panels_mini_export($mini, $indent = '') {
  350. ctools_include('export');
  351. $output = ctools_export_object('panels_mini', $mini, $indent);
  352. // Export the primary display
  353. $display = !empty($mini->display) ? $mini->display : panels_load_display($mini->did);
  354. $output .= panels_export_display($display, $indent);
  355. $output .= $indent . '$mini->display = $display' . ";\n";
  356. return $output;
  357. }
  358. /**
  359. * Remove the block version of mini panels from being available content types.
  360. */
  361. function panels_mini_ctools_block_info($module, $delta, &$info) {
  362. $info = NULL;
  363. }
  364. /**
  365. * Implementation of hook_ctools_plugin_directory() to let the system know
  366. * we implement task and task_handler plugins.
  367. */
  368. function panels_mini_ctools_plugin_directory($module, $plugin) {
  369. if ($module == 'ctools' && ($plugin == 'content_types' || $plugin == 'export_ui')) {
  370. return 'plugins/' . $plugin;
  371. }
  372. if ($module == 'panels' && $plugin == 'panels_storage') {
  373. return 'plugins/' . $plugin;
  374. }
  375. }
  376. /**
  377. * Implements hook_default_panels_mini_alter().
  378. *
  379. * If a default Panels display has no storage type, set it.
  380. */
  381. function panels_default_panels_mini_alter(&$mini_panels) {
  382. foreach ($mini_panels as &$mini_panel) {
  383. $display =& $mini_panel->display;
  384. if (empty($display->storage_type)) {
  385. $display->storage_type = 'panels_mini';
  386. $display->storage_id = $mini_panel->name;
  387. }
  388. }
  389. }
  390. /**
  391. * Get the display cache for the panels_mini plugin.
  392. */
  393. function _panels_mini_panels_cache_get($key) {
  394. ctools_include('export-ui');
  395. $plugin = ctools_get_export_ui('panels_mini');
  396. $handler = ctools_export_ui_get_handler($plugin);
  397. if (!$handler) {
  398. return;
  399. }
  400. $item = $handler->edit_cache_get($key);
  401. if (!$item) {
  402. $item = ctools_export_crud_load($handler->plugin['schema'], $key);
  403. }
  404. return array($handler, $item);
  405. }
  406. /**
  407. * Get display edit cache for the panels mini export UI
  408. *
  409. * The key is the second half of the key in this form:
  410. * panels_mini:TASK_NAME:HANDLER_NAME;
  411. */
  412. function panels_mini_panels_cache_get($key) {
  413. ctools_include('common', 'panels');
  414. list($handler, $item) = _panels_mini_panels_cache_get($key);
  415. if (isset($item->mini_panels_display_cache)) {
  416. return $item->mini_panels_display_cache;
  417. }
  418. $cache = new stdClass();
  419. $cache->display = $item->display;
  420. $cache->display->context = ctools_context_load_contexts($item);
  421. $cache->display->cache_key = 'panels_mini:' . $key;
  422. $cache->display->storage_type = 'panels_mini';
  423. // Temporary storage id that's replaced in panels_mini_save().
  424. $cache->display->storage_id = 'panels_mini';
  425. $cache->content_types = panels_common_get_allowed_types('panels_mini', $cache->display->context);
  426. $cache->display_title = TRUE;
  427. // @TODO support locking
  428. $cache->locked = FALSE;
  429. return $cache;
  430. }
  431. /**
  432. * Store a display edit in progress in the page cache.
  433. */
  434. function panels_mini_panels_cache_set($key, $cache) {
  435. list($handler, $item) = _panels_mini_panels_cache_get($key);
  436. $item->mini_panels_display_cache = $cache;
  437. $handler->edit_cache_set_key($item, $key);
  438. }
  439. /**
  440. * Save all changes made to a display using the panels mini UI cache.
  441. */
  442. function panels_mini_panels_cache_clear($key, $cache) {
  443. list($handler, $item) = _panels_mini_panels_cache_get($key);
  444. $handler->edit_cache_clear($item);
  445. }
  446. /**
  447. * Save all changes made to a display using the panels mini UI cache.
  448. */
  449. function panels_mini_panels_cache_save($key, $cache) {
  450. list($handler, $item) = _panels_mini_panels_cache_get($key);
  451. $item->display = $cache->display;
  452. panels_mini_save($item);
  453. $handler->edit_cache_clear($item);
  454. }
  455. /**
  456. * Break the lock on a panels mini page.
  457. */
  458. function panels_mini_panels_cache_break_lock($key, $cache) {
  459. }
  460. /**
  461. * Implements hook_panels_pre_render().
  462. */
  463. function panels_mini_panels_pre_render($display, $renderer) {
  464. if (isset($display->owner->table) && $display->owner->table == 'panels_mini' && $renderer instanceof panels_renderer_standard) {
  465. $renderer->show_empty_layout = FALSE;
  466. }
  467. }
  468. /**
  469. * Implementation of hook_panels_dashboard_blocks().
  470. *
  471. * Adds mini panels information to the Panels dashboard.
  472. */
  473. function panels_mini_panels_dashboard_blocks(&$vars) {
  474. $vars['links']['panels_mini'] = array(
  475. 'title' => l(t('Mini panel'), 'admin/structure/mini-panels/add'),
  476. 'description' => t('Mini panels are small content areas exposed as blocks, for when you need to have complex block layouts or layouts within layouts.'),
  477. 'weight' => -1,
  478. );
  479. // Load all mini panels and their displays.
  480. $panel_minis = panels_mini_load_all();
  481. $count = 0;
  482. $rows = array();
  483. foreach ($panel_minis as $panel_mini) {
  484. $rows[] = array(
  485. check_plain($panel_mini->admin_title),
  486. array(
  487. 'data' => l(t('Edit'), "admin/structure/mini-panels/list/$panel_mini->name/edit"),
  488. 'class' => 'links',
  489. ),
  490. );
  491. // Only show 10.
  492. if (++$count >= 10) {
  493. break;
  494. }
  495. }
  496. if ($rows) {
  497. $content = theme('table', array('rows' => $rows, 'attributes' => array('class' => 'panels-manage')));
  498. }
  499. else {
  500. $content = '<p>' . t('There are no mini panels.') . '</p>';
  501. }
  502. $vars['blocks']['panels_mini'] = array(
  503. 'weight' => -100,
  504. 'title' => t('Manage mini panels'),
  505. 'link' => l(t('Go to list'), 'admin/structure/mini-panels'),
  506. 'content' => $content,
  507. 'class' => 'dashboard-mini-panels',
  508. 'section' => 'left',
  509. );
  510. }
  511. /**
  512. * Implements template_preprocess_ctools_wizard_trail().
  513. *
  514. * Customize the divider used in the CTools wizard to build the edit pages for
  515. * Mini Panels as a stop-gap measure until the UX can be completely re-done.
  516. */
  517. function panels_mini_preprocess_ctools_wizard_trail(&$variables) {
  518. $variables['divider'] = ' | ';
  519. drupal_add_css(drupal_get_path('module', 'panels_mini') . '/panels_mini.css');
  520. }