boxes.module 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847
  1. <?php
  2. /**
  3. * Edit locations.
  4. */
  5. define('BOXES_EDIT_IN_PLACE', 1);
  6. define('BOXES_EDIT_SEPARATE_PAGE', 2);
  7. /**
  8. * Implements hook_menu().
  9. */
  10. function boxes_menu() {
  11. $items = array();
  12. ctools_include('plugins');
  13. $plugins = ctools_get_plugins('boxes', 'plugins');
  14. foreach ($plugins as $key => $info) {
  15. if (isset($info['title'])) {
  16. $items['admin/structure/block/box-add/' . $key] = array(
  17. 'title' => 'Add ' . strtolower($info['title']),
  18. 'page callback' => 'drupal_get_form',
  19. 'page arguments' => array('boxes_add_form', 4),
  20. 'access arguments' => array('administer boxes'),
  21. 'type' => MENU_LOCAL_ACTION,
  22. 'file' => 'boxes.admin.inc',
  23. );
  24. }
  25. }
  26. $items['admin/structure/block/manage/boxes/%boxes_box/delete'] = array(
  27. 'title' => 'Delete box',
  28. 'page callback' => 'drupal_get_form',
  29. 'page arguments' => array('boxes_delete_form', 5),
  30. 'access arguments' => array('administer boxes'),
  31. 'type' => MENU_CALLBACK,
  32. 'file' => 'boxes.admin.inc',
  33. );
  34. $items['admin/config/user-interface/boxes'] = array(
  35. 'title' => 'Boxes',
  36. 'page callback' => 'drupal_get_form',
  37. 'page arguments' => array('boxes_settings'),
  38. 'access arguments' => array('administer boxes'),
  39. 'file' => 'boxes.admin.inc',
  40. );
  41. return $items;
  42. }
  43. /**
  44. * Access check for whether current user is able to edit boxes.
  45. */
  46. function boxes_access_edit() {
  47. return user_access('administer boxes') || user_access('edit boxes');
  48. }
  49. /**
  50. * Access check for whether current user should be able to administer boxes.
  51. */
  52. function boxes_access_admin() {
  53. return user_access('administer boxes') || (module_exists('spaces') && spaces_access_admin());
  54. }
  55. /**
  56. * Implements hook_permission().
  57. */
  58. function boxes_permission() {
  59. return array(
  60. 'edit boxes' => array(
  61. 'title' => t('Edit boxes'),
  62. 'description' => t('Edit existing boxes'),
  63. ),
  64. 'administer boxes' => array(
  65. 'title' => t('Administer boxes'),
  66. 'description' => t('Administer boxes'),
  67. ),
  68. );
  69. }
  70. /**
  71. * Implements hook_theme().
  72. */
  73. function boxes_theme($existing, $type, $theme, $path) {
  74. return array(
  75. 'boxes_box' => array(
  76. 'variables' => array('block' => NULL),
  77. 'path' => drupal_get_path('module', 'boxes'),
  78. 'file' => 'boxes.admin.inc',
  79. ),
  80. );
  81. }
  82. /**
  83. * Implements hook_block_info().
  84. */
  85. function boxes_block_info() {
  86. ctools_include('plugins');
  87. $plugins = ctools_get_plugins('boxes', 'plugins');
  88. $boxes = boxes_box_load();
  89. $blocks = array();
  90. foreach ($boxes as $box) {
  91. $blocks[$box->delta]['info'] = $box->description;
  92. // Let context group the instances together
  93. $blocks[$box->delta]['context_group'] = t('Boxes for @plugin', array('@plugin' => $plugins[$box->plugin_key]['title']));
  94. // Let each box determine how it is cached
  95. $blocks[$box->delta]['cache'] = $box->cache_setting();
  96. }
  97. // 'Add' blocks for editing a page inline
  98. foreach ($plugins as $key => $info) {
  99. if (isset($info['title'])) {
  100. $blocks["boxes_add__$key"]['info'] = t('Add custom @title', array('@title' => strtolower($info['title'])));
  101. $blocks["boxes_add__$key"]['cache'] = DRUPAL_CACHE_CUSTOM;
  102. }
  103. }
  104. return $blocks;
  105. }
  106. /**
  107. * Implements hook_block_configure().
  108. */
  109. function boxes_block_configure($delta) {
  110. if ($delta && strpos($delta, 'boxes_add__') !== 0) {
  111. if ($box = boxes_box_load($delta)) {
  112. $form_state = array('box' => $box);
  113. if ($box->options_form($form_state)) {
  114. $form = boxes_box_form(array(), $form_state);
  115. unset($form['submit']);
  116. return $form;
  117. }
  118. }
  119. }
  120. }
  121. /**
  122. * Implements hook_block_save().
  123. */
  124. function boxes_block_save($delta, $edit) {
  125. }
  126. /**
  127. * Implements hook_block_view().
  128. */
  129. function boxes_block_view($delta) {
  130. // Add boxes JS.
  131. boxes_add_js();
  132. ctools_include('export');
  133. // If the 'add' box was requested, replace the delta with a unique delta.
  134. if (strpos($delta, 'boxes_add__') === 0) {
  135. $plugin_key = str_replace('boxes_add__', '', $delta);
  136. $identifier = (module_exists('spaces') && $space = spaces_get_space()) ? "{$space->type}-{$space->id}" : 'box';
  137. $hash = boxes_create_hash($identifier);
  138. $delta = $identifier . "-" . $hash;
  139. $box = boxes_factory($plugin_key, array('delta' => $delta));
  140. $form_state = array(
  141. 'box' => $box,
  142. 'plugin_key' => $plugin_key,
  143. 'custom_action' => TRUE,
  144. 'no_redirect' => TRUE,
  145. );
  146. // We need to explicitly set the form input as empty to avoid the form being
  147. // processed. The actual form submission is handled in the footer.
  148. $form_state['input'] = array();
  149. $form_state['init_form'] = TRUE;
  150. $form = drupal_build_form('boxes_box_form', $form_state);
  151. $block['delta'] = $delta;
  152. $block['content'] = '';
  153. $block['editing'] = $form;
  154. $block['content'] = theme('boxes_box', array('block' => $block));
  155. $plugin = ctools_get_plugins('boxes', 'plugins', $plugin_key);
  156. $block['subject'] = t('Add custom @title', array('@title' => strtolower($plugin['title'])));
  157. $block['boxes_plugin'] = $box->plugin_key;
  158. return $block;
  159. }
  160. elseif ($box = boxes_box_load($delta)) {
  161. // Generate content and provide an editing form if user has proper
  162. // permissions.
  163. $block = $box->render();
  164. if (boxes_access_edit()) {
  165. if (variable_get('boxes_edit_location', BOXES_EDIT_IN_PLACE) == BOXES_EDIT_IN_PLACE) {
  166. $edit_link = array(
  167. 'title' => t('Edit Box'),
  168. 'href' => $_GET['q'],
  169. 'query' => array(
  170. 'plugin_key' => $box->plugin_key,
  171. 'boxes_delta' => $block['delta'],
  172. ),
  173. 'attributes' => array('class' => array('use-ajax')),
  174. );
  175. }
  176. else {
  177. $edit_link = array(
  178. 'title' => t('Edit Box'),
  179. 'href' => 'admin/structure/block/manage/boxes/' . $block['delta'] . '/configure',
  180. 'query' => drupal_get_destination(),
  181. );
  182. }
  183. $block['controls'] = theme('links', array(
  184. 'links' => array(
  185. 'edit' => $edit_link,
  186. 'cancel' => array(
  187. 'title' => t('Cancel'),
  188. 'href' => $_GET['q'],
  189. ),
  190. ),
  191. ));
  192. }
  193. // Add additional_classes
  194. if (!empty($box->options['additional_classes'])) {
  195. $block['additional_classes'] = $box->options['additional_classes'];
  196. }
  197. $block['content'] = theme('boxes_box', array('block' => $block));
  198. $block['boxes_plugin'] = $box->plugin_key;
  199. return $block;
  200. }
  201. }
  202. /**
  203. * Implements hook_page_alter().
  204. */
  205. function boxes_page_alter(&$page) {
  206. $page['page_bottom']['boxes'] = array(
  207. '#markup' => boxes_footer(),
  208. );
  209. }
  210. /**
  211. * Implements hook_footer().
  212. *
  213. * All ajax requests are targeted back to the current page so that the proper
  214. * environment can be re-setup. Here in hook_footer we then detect that this is
  215. * an ajax submission and handle it.
  216. */
  217. function boxes_footer() {
  218. // if ckeditor is used, the base path needs to be set on the original
  219. // page load, or ckeditor will set it incorrectly to the root.
  220. // while this is loaded on every page it is just a single set of a variable.
  221. if (module_exists("wysiwyg") && ($editor = wysiwyg_get_editor('ckeditor'))) {
  222. $base = base_path();
  223. drupal_add_js("window.CKEDITOR_BASEPATH = '$base{$editor['library path']}/'", 'inline');
  224. }
  225. // Besure the page isn't a 404 or 403.
  226. $headers = drupal_get_http_header();
  227. foreach ($headers as $header) {
  228. if ($header == "HTTP/1.1 404 Not Found" || $header == "HTTP/1.1 403 Forbidden") {
  229. return;
  230. }
  231. }
  232. if (!empty($_GET['boxes_delta']) && boxes_access_edit()) {
  233. include_once('includes/ajax.inc');
  234. $output = array();
  235. if (isset($_GET['plugin_key'])) {
  236. $plugin_key = $_GET['plugin_key'];
  237. $delta = $_GET['boxes_delta'];
  238. $box = boxes_box_load($delta);
  239. if (!$box && $plugin_key) {
  240. $box = boxes_factory($plugin_key, array('delta' => $delta));
  241. $form_state = array(
  242. 'box' => $box,
  243. 'plugin_key' => $plugin_key,
  244. 'custom_action' => TRUE,
  245. 'no_redirect' => TRUE,
  246. );
  247. }
  248. elseif (!$box) {
  249. return;
  250. }
  251. else {
  252. $form_state = array(
  253. 'box' => $box,
  254. 'no_redirect' => TRUE,
  255. );
  256. }
  257. $form = drupal_build_form('boxes_box_form', $form_state);
  258. $errors = form_set_error();
  259. if (!empty($errors)) { /* validation error */
  260. $msg_and_form = theme('status_messages') . drupal_render($form);
  261. $output[] = array(
  262. 'command' => 'showBoxForm',
  263. 'method' => 'html',
  264. 'selector' => '#block-boxes-' . $delta,
  265. 'data' => $msg_and_form,
  266. 'settings' => NULL,
  267. );
  268. }
  269. // if we are comming from a init form and need to continue then lets render the form again
  270. else if (empty($form_state['submitted']) || isset($_GET['init_form_continue'])) {
  271. $output[] = array(
  272. 'command' => 'showBoxForm',
  273. 'method' => 'html',
  274. 'selector' => '#boxes-box-' . $delta,
  275. 'data' => drupal_render($form),
  276. 'settings' => NULL,
  277. );
  278. }
  279. else {
  280. $output[] = array(
  281. 'selector' => '#block-boxes-' . $delta,
  282. 'command' => 'getBlock',
  283. 'method' => 'replaceWith',
  284. 'delta' => $delta,
  285. 'url' => url($_GET['q'], array('absolute' => TRUE)),
  286. );
  287. }
  288. }
  289. else {
  290. $block = boxes_block_view($_GET['boxes_delta']);
  291. $block['module'] = 'boxes';
  292. $block['region'] = 'page_bottom';
  293. $block['content'] = array('#markup' => $block['content']);
  294. $block = (object) $block;
  295. $renderable = _block_get_renderable_array(_block_render_blocks(array('boxes_' . $_GET['boxes_delta'] => $block)));
  296. $markup = trim(drupal_render($renderable));
  297. $id = 'block-boxes-' . $_GET['boxes_delta'];
  298. // We cannot use drupal_html_id() here because it will increment the id on
  299. // pages where a box is already present. So we do its other transforms
  300. // manually.
  301. $id = strtr(drupal_strtolower($id), array(' ' => '-', '_' => '-', '[' => '-', ']' => ''));
  302. $id = preg_replace('/[^A-Za-z0-9\-_]/', '', $id);
  303. $id = preg_replace('/\-+/', '-', $id);
  304. $output[] = ajax_command_replace('#' . $id, $markup);
  305. if (module_exists('context')) {
  306. array_unshift($output, array(
  307. 'selector' => '#' . $id,
  308. 'command' => 'preReplaceContextBlock',
  309. 'id' => $id,
  310. ));
  311. $output[] = array(
  312. 'selector' => '#' . $id,
  313. 'command' => 'postReplaceContextBlock',
  314. 'id' => $id,
  315. );
  316. }
  317. }
  318. ajax_deliver(array('#type' => 'ajax', '#commands' => $output));
  319. exit();
  320. }
  321. }
  322. /**
  323. * Instantiate box object.
  324. *
  325. * @param $plugin_key
  326. * The string id of the boxes plugin.
  327. * @param $values
  328. * The values to set on the box.
  329. *
  330. * @return a box object.
  331. */
  332. function boxes_factory($plugin_key, $values = array()) {
  333. module_load_include('inc', 'boxes', 'plugins/boxes_box');
  334. return boxes_box::factory($plugin_key, $values);
  335. }
  336. /**
  337. * Load a box.
  338. */
  339. function boxes_box_load($delta = NULL) {
  340. if (isset($delta)) {
  341. $box = boxes_box::load($delta);
  342. drupal_alter('boxes_box_load', $box, $delta);
  343. return $box;
  344. }
  345. ctools_include('export');
  346. $boxes = array();
  347. foreach (ctools_export_load_object('box') as $box) {
  348. $box = boxes_box::load($box->delta);
  349. if($box) {
  350. drupal_alter('boxes_box_load', $box, $delta);
  351. $boxes[$box->delta] = $box;
  352. }
  353. }
  354. return $boxes;
  355. }
  356. /**
  357. * Reset boxes load caches.
  358. */
  359. function boxes_box_load_reset() {
  360. boxes_box::reset();
  361. }
  362. /**
  363. * Common element of the box form
  364. */
  365. function boxes_box_form($form, &$form_state) {
  366. $box = $form_state['box'];
  367. if(method_exists($box, "box_form")) {
  368. return $box->box_form($form, $form_state);
  369. }
  370. // For hook_block_save()
  371. $form['plugin_key'] = array(
  372. '#type' => 'value',
  373. '#value' => $box->plugin_key,
  374. );
  375. $form['delta'] = array(
  376. '#type' => 'value',
  377. '#value' => $box->delta,
  378. );
  379. $form['description'] = array(
  380. '#type' => 'textfield',
  381. '#title' => t('Box description'),
  382. '#default_value' => $box->description,
  383. '#maxlength' => 64,
  384. '#description' => t('A brief description of your box. Used for administrative purposes.'),
  385. '#required' => TRUE,
  386. '#weight' => -19,
  387. );
  388. $form['title'] = array(
  389. '#type' => 'textfield',
  390. '#title' => t('Box title'),
  391. '#maxlength' => 64,
  392. '#description' => t('The rendered title of the box as shown to the user.'),
  393. '#default_value' => $box->title,
  394. '#weight' => -18,
  395. );
  396. $form['options'] = $box->options_form($form_state);
  397. $form['options']['#weight'] = -17;
  398. $form['boxes_adv'] = array(
  399. '#type'=> 'fieldset',
  400. '#collapsed' => TRUE,
  401. '#collapsible' => TRUE,
  402. '#title' => t('Advanced Settings'),
  403. );
  404. $form['boxes_adv']['additional_classes'] = array(
  405. '#title' => t('Additional CSS classes'),
  406. '#description' => t('Optional CSS classes that will be added to the top-level div container for this box. Separate them with spaces.'),
  407. '#type' => 'textfield',
  408. '#default_value' => isset($box->options['additional_classes']) ? $box->options['additional_classes'] : '',
  409. );
  410. $form['submit'] = array(
  411. '#type' => 'submit',
  412. '#value' => t('Save'),
  413. '#attributes' => array('class' => array('boxes-ajax', 'use-ajax-submit')),
  414. );
  415. $form['cancel'] = array(
  416. '#type' => 'submit',
  417. '#value' => t('Cancel'),
  418. '#limit_validation_errors' => array(),
  419. '#attributes' => array('class' => array('boxes-ajax', 'use-ajax-submit')),
  420. );
  421. if (!empty($form_state['custom_action'])) {
  422. $form['#action'] = url($_GET['q'], array('query' => array(
  423. 'boxes_delta' => $box->delta,
  424. 'plugin_key' => $form_state['plugin_key'],
  425. )));
  426. }
  427. if (isset($form_state['init_form']) && $box->use_multistep_create()) {
  428. unset($form['options']);
  429. unset($form['cancel']);
  430. $form['submit']['#value'] = t('Continue');
  431. if (!empty($form_state['custom_action'])) {
  432. $form['#action'] = url($_GET['q'], array(
  433. 'query' => array(
  434. 'boxes_delta' => $box->delta,
  435. 'init_form_continue' => TRUE,
  436. 'plugin_key' => $form_state['plugin_key'],
  437. )
  438. ));
  439. }
  440. }
  441. return $form;
  442. }
  443. /**
  444. * Implements hook_form_alter().
  445. */
  446. function boxes_form_alter(&$form, &$form_state, $form_id) {
  447. $path = implode('/', array_slice(arg(), 0, 5));
  448. if (($form_id != 'block_admin_configure' || $path != 'admin/structure/block/manage/boxes') &&
  449. ($form_id != 'boxes_add_form' || arg(3) != 'box-add')) {
  450. return;
  451. }
  452. // Use the Box form submit handler and properly redirect to the correct admin page
  453. array_unshift($form['#submit'], 'boxes_box_form_submit');
  454. $form['#action'] = '?destination=admin/structure/block';
  455. // Cancel behavior is different here
  456. unset($form['cancel']['#attributes']);
  457. $form['cancel']['#submit'] = array('boxes_block_cancel_submit');
  458. }
  459. /**
  460. * Submit handler for the inline form.
  461. */
  462. function boxes_box_form_submit($form, $form_state) {
  463. if ($form_state['clicked_button']['#value'] == t('Cancel')) {
  464. return;
  465. }
  466. $box = boxes_factory($form_state['values']['plugin_key'], $form_state['values']);
  467. // if options_submit is defined let the box process the submit
  468. if (method_exists($box, 'options_submit')) {
  469. $box->options_submit($form, $form_state);
  470. }
  471. // Let's set the additional_classes as an option
  472. $box->options['additional_classes'] = isset($form_state['values']['additional_classes']) ?
  473. $form_state['values']['additional_classes'] : array();
  474. if (module_exists('spaces') && $space = spaces_get_space()) {
  475. $space->controllers->boxes->set($box->delta, $box);
  476. }
  477. else {
  478. $box->save();
  479. }
  480. }
  481. /**
  482. * Implements hook_form_alter for block_admin_configure().
  483. */
  484. function boxes_form_block_admin_configure_alter(&$form, &$form_state) {
  485. if ($form['module']['#value'] == 'boxes') {
  486. $box = boxes_box_load($form['delta']['#value']);
  487. if (($box->export_type & EXPORT_IN_DATABASE) && ($box->export_type & EXPORT_IN_CODE)) {
  488. $form['actions']['revert'] = array(
  489. '#type' => 'submit',
  490. '#value' => t('Revert'),
  491. '#submit' => array('boxes_block_delete_submit'),
  492. );
  493. }
  494. elseif (!($box->export_type & EXPORT_IN_CODE)) {
  495. $form['actions']['delete'] = array(
  496. '#type' => 'submit',
  497. '#value' => t('Delete'),
  498. '#submit' => array('boxes_block_delete_submit'),
  499. );
  500. }
  501. // Cancel behavior is different on admin/structure page
  502. unset($form['settings']['cancel']);
  503. $form['actions']['cancel'] = array(
  504. '#type' => 'submit',
  505. '#value' => t('Cancel'),
  506. '#submit' => array('boxes_block_cancel_submit'),
  507. );
  508. }
  509. }
  510. /**
  511. * Submit handler for box cancel from the block/manage/boxes/[MACHINE-NAME]/configure
  512. */
  513. function boxes_block_cancel_submit($form, &$form_state) {
  514. if (module_exists('overlay')) {
  515. overlay_close_dialog();
  516. }
  517. else {
  518. drupal_goto('/admin/structure/block');
  519. }
  520. }
  521. // Submit handler for box deletion.
  522. /**
  523. * @todo Please document this function.
  524. * @see http://drupal.org/node/1354
  525. */
  526. function boxes_block_delete_submit($form, &$form_state) {
  527. // $form_state['redirect'] will not work here since we are using $form['#action'] with destination
  528. $_GET['destination'] = 'admin/structure/block/manage/boxes/' . $form_state['values']['delta'] . '/delete';
  529. }
  530. /**
  531. * Alters the block admin form to add delete links next to boxes blocks.
  532. */
  533. function boxes_form_block_admin_display_form_alter(&$form, $form_state) {
  534. foreach (element_children($form['blocks']) as $i) {
  535. if (isset($form['blocks'][$i]['module']['#value']) && $form['blocks'][$i]['module']['#value'] == 'boxes') {
  536. $delta = $form['blocks'][$i]['delta']['#value'];
  537. if (strpos($delta, 'boxes_add__') !== 0) {
  538. $box = boxes_box_load($delta);
  539. if (($box->export_type & EXPORT_IN_DATABASE) && ($box->export_type & EXPORT_IN_CODE)) {
  540. $form['blocks'][$i]['delete'] = array(
  541. '#title' => t('revert'),
  542. '#type' => 'link',
  543. '#href' => 'admin/structure/block/manage/boxes/' . $delta . '/delete'
  544. );
  545. }
  546. elseif (!($box->export_type & EXPORT_IN_CODE)) {
  547. $form['blocks'][$i]['delete'] = array(
  548. '#title' => t('delete'),
  549. '#type' => 'link',
  550. '#href' => 'admin/structure/block/manage/boxes/' . $delta . '/delete'
  551. );
  552. }
  553. }
  554. }
  555. }
  556. }
  557. /**
  558. * Implements hook_ctools_plugin_api().
  559. */
  560. function boxes_ctools_plugin_api($module, $api) {
  561. if ($module == 'spaces' && $api == 'plugins') {
  562. return array('version' => 3);
  563. }
  564. elseif ($module == 'boxes' && $api == 'plugins') {
  565. return array('version' => 1);
  566. }
  567. }
  568. /**
  569. * Implements hook_ctools_plugin_plugins().
  570. */
  571. function boxes_ctools_plugin_type() {
  572. return array(
  573. 'plugins' => array(
  574. 'cache' => TRUE,
  575. 'use hooks' => TRUE,
  576. 'classes' => array('handler'),
  577. ),
  578. );
  579. }
  580. /**
  581. * Implements hook_boxes_plugins().
  582. */
  583. function boxes_boxes_plugins() {
  584. $info = array();
  585. $path = drupal_get_path('module', 'boxes') . '/plugins';
  586. $info['box'] = array(
  587. 'handler' => array(
  588. 'class' => 'boxes_box',
  589. 'file' => 'boxes_box.inc',
  590. 'path' => $path,
  591. ),
  592. );
  593. $info['simple'] = array(
  594. 'title' => 'Box',
  595. 'handler' => array(
  596. 'parent' => 'box',
  597. 'class' => 'boxes_simple',
  598. 'file' => 'boxes_simple.inc',
  599. 'path' => $path,
  600. ),
  601. );
  602. return $info;
  603. }
  604. /**
  605. * Implements hook_spaces_plugins().
  606. */
  607. function boxes_spaces_plugins() {
  608. $plugins = array();
  609. $plugins['spaces_controller_boxes'] = array(
  610. 'handler' => array(
  611. 'path' => drupal_get_path('module', 'boxes') . '/plugins',
  612. 'file' => 'spaces_controller_boxes.inc',
  613. 'class' => 'spaces_controller_boxes',
  614. 'parent' => 'spaces_controler',
  615. ),
  616. );
  617. return $plugins;
  618. }
  619. /**
  620. * Implements hook_spaces_registry().
  621. */
  622. function boxes_spaces_registry() {
  623. return array(
  624. 'controllers' => array(
  625. 'boxes' => array(
  626. 'title' => t('Boxes'),
  627. 'plugin' => 'spaces_controller_boxes',
  628. ),
  629. ),
  630. );
  631. }
  632. /**
  633. * Implements hook_spaces_dashboard_block_access_alter().
  634. *
  635. * Provides access to blocks specific to each space.
  636. */
  637. function boxes_spaces_dashboard_block_access_alter(&$access) {
  638. foreach (array_keys($access) as $bid) {
  639. list($module, $delta) = explode('-', $bid, 2);
  640. if ($module === 'boxes') {
  641. // This is the add block, give access to admins.
  642. if (strpos($delta, 'boxes_add__') === 0) {
  643. $access[$bid] = boxes_access_admin();
  644. }
  645. // If this box is specific to the current space, allow access.
  646. elseif (module_exists('spaces') && $space = spaces_get_space()) {
  647. $in_space = $space->controllers->boxes->get($delta, 'space');
  648. $access[$bid] = $access[$bid] || !empty($in_space);
  649. }
  650. // Outside of spaces, give admins access to all boxes.
  651. // @todo boxes_access_admin() may need to be boxes_access_edit().
  652. else {
  653. $access[$bid] = boxes_access_admin();
  654. }
  655. }
  656. }
  657. }
  658. /**
  659. * Implements hook_context_block_info_alter().
  660. *
  661. * Provides spaces integration when working with blocks using context.
  662. */
  663. function boxes_context_block_info_alter(&$blocks) {
  664. // Add boxes JS. If this is getting called, it's highly likely a context
  665. // inline editor is on the page.
  666. boxes_add_js();
  667. if (module_exists('spaces') && $space = spaces_get_space()) {
  668. $item = menu_get_item();
  669. // Prevent space-specific blocks from appearing on the dashboard settings
  670. // page within a space.
  671. if (!(isset($item['page_callback'], $item['page_arguments'][0]) && $item['page_callback'] === 'drupal_get_form' && $item['page_arguments'][0] === 'spaces_dashboard_admin_form')) {
  672. foreach ($space->controllers->boxes->get() as $box) {
  673. $add = new stdClass();
  674. $add->bid = "boxes-{$box->delta}";
  675. $add->delta = $box->delta;
  676. $add->info = $box->description;
  677. $add->cache = DRUPAL_CACHE_CUSTOM;
  678. $add->module = 'boxes';
  679. $blocks[$add->bid] = $add;
  680. }
  681. }
  682. }
  683. }
  684. /**
  685. * Implements hook_boxes_box_load_alter().
  686. *
  687. * Provides spaces integration for per-space overrides of a given box.
  688. */
  689. function boxes_boxes_box_load_alter(&$box, $delta) {
  690. if (!$delta) {
  691. $delta = $box->delta;
  692. }
  693. if (module_exists('spaces') && $space = spaces_get_space()) {
  694. if ($space_box = $space->controllers->boxes->get($delta)) {
  695. // Some older overrides may be stored as an array, check for these and
  696. // convert them to objects.
  697. if (is_array($space_box)) {
  698. $space_box = (object) $space_box;
  699. }
  700. $box = boxes_factory($space_box->plugin_key, $space_box);
  701. $box->new = FALSE;
  702. }
  703. }
  704. }
  705. /**
  706. * Implements hook_features_pipe_MODULE_alter().
  707. */
  708. function boxes_features_pipe_block_alter(&$more, $data, $export) {
  709. foreach ($data as $bid) {
  710. $split = explode('-', $bid);
  711. $module = array_shift($split);
  712. $delta = implode('-', $split);
  713. if ($module == 'boxes') {
  714. $more['box'][] = $delta;
  715. }
  716. }
  717. }
  718. /**
  719. * Add JavaScript to the page.
  720. */
  721. function boxes_add_js() {
  722. static $added = FALSE;
  723. if ($added || (!boxes_access_admin() && !boxes_access_edit())) {
  724. return;
  725. }
  726. $added = TRUE;
  727. drupal_add_library('system', 'jquery.form', TRUE);
  728. drupal_add_library('system', 'drupal.form', TRUE);
  729. drupal_add_library('system', 'drupal.progress', TRUE);
  730. drupal_add_library('system', 'drupal.ajax', TRUE);
  731. drupal_add_library('system', 'ui.dialog');
  732. drupal_add_js(drupal_get_path('module', 'boxes') . '/boxes.js');
  733. drupal_add_css(drupal_get_path('module', 'boxes') . '/boxes.css');
  734. drupal_add_js(array('getQ' => $_GET['q']), array('type' => 'setting', 'scope' => JS_DEFAULT));
  735. }
  736. /**
  737. * Create a hash for a block id.
  738. */
  739. function boxes_create_hash($identifier) {
  740. global $user;
  741. $boxes = boxes_block_info();
  742. $hash = dechex(crc32($user->sid . microtime()));
  743. while (isset($boxes["{$identifier}-{$hash}"])) {
  744. $hash = dechex(crc32($user->sid . microtime()));
  745. }
  746. return $hash;
  747. }
  748. /**
  749. * Preprocessor for theme('block').
  750. */
  751. function boxes_preprocess_block(&$vars) {
  752. $vars['classes_array'] = isset($vars['classes_array']) ? $vars['classes_array'] : array();
  753. if ($vars['block']->module === 'boxes') {
  754. }
  755. if ($vars['block']->module === 'boxes' && isset($vars['block']->boxes_plugin)) {
  756. $vars['classes_array'][] = 'block-boxes-' . $vars['block']->boxes_plugin;
  757. }
  758. if ($vars['block']->module === 'boxes' && isset($vars['block']->additional_classes)) {
  759. $vars['classes_array'] = array_merge($vars['classes_array'], explode(' ', $vars['block']->additional_classes));
  760. }
  761. }
  762. /**
  763. * Preprocessor for theme('panels_pane').
  764. */
  765. function boxes_preprocess_panels_pane(&$vars) {
  766. if ($vars['pane']->type == 'block') {
  767. list($module, $delta) = explode('-', $vars['pane']->subtype, 2);
  768. // Load up the actual box to check for additional classes.
  769. $box = boxes_box_load($delta);
  770. if (isset($box->plugin_key)) {
  771. $vars['classes_array'][] = 'block-boxes-' . $box->plugin_key;
  772. }
  773. if (isset($box->options['additional_classes'])) {
  774. $vars['classes_array'] = array_merge($vars['classes_array'], explode(' ', $box->options['additional_classes']));
  775. }
  776. }
  777. }
  778. /**
  779. * Implements hook_ctools_block_info().
  780. */
  781. function boxes_ctools_block_info($module, $delta, &$info) {
  782. $info['category'] = t('Boxes');
  783. }