views_boxes_view.inc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. <?php
  2. /**
  3. * @file
  4. * Provides a new box-type that provides Views as boxes.
  5. */
  6. /**
  7. * Views content listing box.
  8. */
  9. class views_boxes_view extends boxes_box {
  10. /**
  11. * Implementation of boxes_box::options_defaults().
  12. */
  13. public function options_defaults() {
  14. return array(
  15. 'view' => '',
  16. 'settings' => array(),
  17. 'view_object' => NULL,
  18. 'view_more_text' => '',
  19. 'view_more_link' => '',
  20. 'expose_filters' => FALSE,
  21. );
  22. }
  23. /**
  24. * implements boxes_box::option_submit()
  25. * This needs a boxes patch
  26. */
  27. public function options_submit($form, &$form_state) {
  28. //$settings_form = $form['options']['settings'];
  29. //views_exposed_form_submit($settings_form, $form_state);
  30. foreach ($form['options']['settings'] as $key => $element) {
  31. if (isset($form_state['input'][$key])) {
  32. $this->options['settings'][$key] = $form_state['input'][$key];
  33. }
  34. }
  35. }
  36. /**
  37. * we use fancy ajax on our options form and this breaks with the context ui
  38. */
  39. function use_multistep_create() {
  40. return TRUE;
  41. }
  42. /**
  43. * Overrides boxes_box::cache_setting().
  44. *
  45. * This delegates each Views Box instance's cache setting to the value on the
  46. * View's Block display 'Block cache' value.
  47. */
  48. public function cache_setting() {
  49. $view_info = explode('--', $this->options['view']);
  50. // We are getting this from the cached views
  51. if ($view_info && count($view_info) == 2 && $view = views_get_view($view_info[0])) {
  52. foreach ($view->display as $id => $display) {
  53. if (($id == $view_info[1]) && (isset($display->display_options['block_caching']))) {
  54. return $display->display_options['block_caching'];
  55. }
  56. }
  57. }
  58. return DRUPAL_CACHE_CUSTOM;
  59. }
  60. /**
  61. * Implementation of boxes_box::options_form().
  62. *
  63. * We are presenting a form element for the view to use
  64. * and then a field set of elements based on that views exposed form and arguments
  65. */
  66. public function options_form(&$form_state) {
  67. drupal_add_css(drupal_get_path('module', 'views_boxes') . '/plugins/views_boxes_view.css');
  68. drupal_add_js(drupal_get_path('module', 'views_boxes') . '/plugins/views_boxes_view.js');
  69. $form = array();
  70. $view_options = array();
  71. // Find all view displays that are blocks
  72. foreach (views_get_enabled_views() as $view) {
  73. $name = $view->name;
  74. foreach ($view->display as $display_name => $display) {
  75. // Only show block displays
  76. if ($display->display_plugin == 'block') {
  77. $view_options[$view->human_name]["$name--$display_name"] = "{$view->human_name} - {$display->display_title}";
  78. }
  79. }
  80. }
  81. // form element for the view, change this set off an ajax call on the settings
  82. $form['view'] = array(
  83. '#type' => 'select',
  84. '#title' => t('View'),
  85. '#description' => t('Select the View and Display to use.'),
  86. '#options' => $view_options,
  87. '#default_value' => $this->options['view'],
  88. '#ajax' => array(
  89. 'callback' => 'views_boxes_view_settings_callback',
  90. 'wrapper' => 'view_settings',
  91. 'method' => 'replace',
  92. 'effect' => 'fade',
  93. ),
  94. );
  95. // wrapper for exposed form and arguments
  96. $form['settings'] = array(
  97. '#type' => 'fieldset',
  98. '#title' => 'Settings',
  99. '#prefix' => '<div id="view_settings">',
  100. '#suffix' => '</div>'
  101. );
  102. // EXPOSED FILTERS
  103. $view_string = !empty($form_state['values']['view']) ? $form_state['values']['view'] : $this->options['view'];
  104. list($view_name, $display_name) = (preg_match('/--/', $view_string)) ? explode('--', $view_string) : array('', '');
  105. if ($view_name) {
  106. // Insure that we use values that have been set in the form
  107. // on ajax calls
  108. $view = views_get_view($view_name, TRUE);
  109. $view->set_display($display_name);
  110. $view->set_exposed_input($this->options['settings']);
  111. $view->execute();
  112. // Get the exposed form and use it to build the settings_form
  113. $exposed_form = $view->display_handler->get_plugin('exposed_form');
  114. $settings_form_state = array();
  115. list($settings_form, $settings_form_state) = $this->render_exposed_form($exposed_form, $settings_form_state);
  116. $form_state['settings_form_state'] = $settings_form_state;
  117. $temp_form_state = $form_state;
  118. $this->options_submit(array("options" => array("settings" => $settings_form)), $temp_form_state);
  119. // Iterate over each item and make sure it has a title and add back
  120. // our default values
  121. foreach ($settings_form as $id => $element) {
  122. if (isset($view->filter[$id])) {
  123. $handler = $view->filter[$id];
  124. $title = $handler->options['ui_name'] ? $handler->options['ui_name'] : $id;
  125. $title = $handler->options['expose']['label'] ? $handler->options['expose']['label'] : $title;
  126. $settings_form[$id]['#title'] = $title;
  127. }
  128. if (isset($this->options['settings'][$id])) {
  129. // if we have an options array we want to make sure our default value
  130. // is in the options before we set it as the value
  131. $default_value = $this->options['settings'][$id];
  132. if (isset($settings_form[$id]['#options'])) {
  133. if (is_array($default_value)) {
  134. foreach ($default_value as $key => $value) {
  135. if (!in_array($value, array_keys($settings_form[$id]['#options']))) {
  136. unset($default_value[$key]);
  137. }
  138. }
  139. if (empty($default_value)) {
  140. unset($default_value);
  141. }
  142. else {
  143. if (!($settings_form[$id]['#multiple'])) {
  144. $default_value = $default_value[0];
  145. unset($default_value);
  146. }
  147. }
  148. }
  149. else {
  150. if (!in_array($default_value, array_keys($settings_form[$id]['#options']))) {
  151. unset($default_value);
  152. }
  153. }
  154. }
  155. if (isset($default_value)) {
  156. $settings_form[$id]['#default_value'] = $default_value;
  157. }
  158. }
  159. /*
  160. // we want to reorder the options to the same order as our stored values
  161. // incase the order matters
  162. if (is_array($this->options['settings'][$id])) {
  163. $options = array();
  164. foreach ($this->options['settings'][$id] as $key => $on) {
  165. $options[$key] = $settings_form[$id]['#options'][$key];
  166. }
  167. $settings_form[$id]['#options'] = $options + $settings_form[$id]['#options'];
  168. }
  169. // Make checkboxes instead of multi select for the sort options
  170. // This really should be part of the filter settings
  171. if ($settings_form[$id]['#type'] =='select' && $settings_form[$id]['#multiple'] == TRUE && $id == 'sort') {
  172. $settings_form[$id]['#type'] ='checkboxes';
  173. }
  174. */
  175. }
  176. // ARGUMENTS
  177. // foreach argument let's make a element in the settings_form
  178. foreach ($view->argument as $key => $arg) {
  179. // We are doing some custom handling on the id argument type
  180. // basically we are hiding the field that holds all of the ids
  181. // and making autocomplete fields for node selection.
  182. // We then update the original field with JS.
  183. $re = "/\[(.*)\]/";
  184. $entity_type = FALSE;
  185. unset($bundles);
  186. // Check for the following format of the admin title
  187. // [entity type]
  188. // if we have it remove it from the name and set the entity type
  189. if (preg_match($re, $arg->options['ui_name'], $matches)) {
  190. $entity_type = $matches[1];
  191. $arg->options['ui_name'] = preg_replace($re, '', $arg->options['ui_name']);
  192. }
  193. // See if we're going to filters on specific bundles
  194. $re = "/\{bundles:(.*)\}/";
  195. if (preg_match($re, $arg->options['ui_name'], $matches)) {
  196. $bundles = $matches[1];
  197. $arg->options['ui_name'] = preg_replace($re, '', $arg->options['ui_name']);
  198. }
  199. if ((get_class($arg) == 'views_handler_argument_null') ||
  200. ($arg->field == 'tid') ||
  201. ($arg->field == 'nid') ||
  202. $entity_type) {
  203. // we need to get info about our entity
  204. // for now we are only looking at nodes and taxonomy_terms
  205. if (isset($form_state['values'][$key])) {
  206. $this->options['settings'][$key] = $form_state['values'][$key];
  207. }
  208. $entity_type_array = array(
  209. 'nid' => 'node',
  210. 'tid' => 'taxonomy_term',
  211. );
  212. $entity_type = $entity_type ? $entity_type : (isset($entity_type_array[$arg->field]) ? $entity_type_array[$arg->field] : 'node');
  213. $entity_info = entity_get_info($entity_type);
  214. // Grab the name of the label and id field so that we can build the
  215. // autocomplete field values later
  216. $id_field = $entity_info['entity keys']['id'];
  217. $label_field = $entity_info['entity keys']['label'];
  218. // Build the field group to put all of our widgets in
  219. $title = $arg->options['ui_name'] ? $arg->options['ui_name'] : $key;
  220. $settings_form["{$key}_group"] = array(
  221. '#title' => $title,
  222. '#type' => "fieldset",
  223. '#prefix' => "<div id = '$key-group' class='id-group'>",
  224. '#suffix' => "</div>",
  225. );
  226. // Build the actual value field (this is what gets sent to the view)
  227. $settings_form[$key] = array(
  228. '#type' => 'textfield',
  229. '#default_value' => $this->options['settings'][$key],
  230. '#attributes' => array('class' => array('id-value'), 'group' => "$key-group", 'style' => 'display:none'),
  231. );
  232. // Wrap the sortable items
  233. $settings_form["{$key}_group"]["{$key}_container_start"] = array(
  234. '#markup' => "<div class='id-sortable' key='$key' id='$key-wrapper'>",
  235. '#weight' => -1,
  236. );
  237. // Find the current values and build auto complete fields that are populated
  238. $ids = explode("+", $this->options['settings'][$key]);
  239. $entities = entity_load($entity_type, $ids);
  240. $count = 0;
  241. // Restrict autocomplete to specific entity or bundle(s)
  242. if (!isset($bundles)) {
  243. $autocomplete_path = "entity-autocomplete/{$entity_type}";
  244. }
  245. else {
  246. $autocomplete_path = "entity-autocomplete/bundle/{$entity_type}/" . $bundles;
  247. }
  248. foreach ($entities as $index => $entity) {
  249. $id = $index;
  250. // Adding a rand to the key so the value does not get cached
  251. $settings_form["{$key}_group"][$key . "_$id-" . rand(0, 10000)] = array(
  252. '#type' => 'textfield',
  253. '#default_value' => "{$entity->{$label_field}} [id:$id]",
  254. '#autocomplete_path' => $autocomplete_path,
  255. '#prefix' => '<div class = "id-item"><div class= handle></div>',
  256. '#attributes' => array('class' => array('id-field'), 'key' => $key),
  257. '#suffix' => '</div>',
  258. '#weight' => $count,
  259. );
  260. $count ++;
  261. }
  262. // if the use asked for a new field or we have no fields add a empty field
  263. if ($form_state['clicked_button']['#key'] == $key || ($count == 0)) {
  264. $settings_form["{$key}_group"][$key . "new-" . rand(0, 10000)] = array(
  265. '#type' => 'textfield',
  266. '#default_value' => "",
  267. '#autocomplete_path' => $autocomplete_path,
  268. '#prefix' => '<div class = "id-item"><div class= handle></div>',
  269. '#attributes' => array('class' => array('id-field'), 'key' => $key),
  270. '#suffix' => '</div>',
  271. '#weight' => 1000,
  272. );
  273. }
  274. // End the sortable group
  275. $settings_form["{$key}_group"]["{$key}_container_end"] = array(
  276. '#markup' => '</div>',
  277. '#weight' => 1001,
  278. );
  279. // Add a button to add a new item
  280. $settings_form["{$key}_group"]["{$key}_add"] = array(
  281. '#type' => 'button',
  282. '#value' => "Add New $title",
  283. '#key' => $key,
  284. '#weight' => 1002,
  285. '#attributes' => array('key' => $key),
  286. '#ajax' => array(
  287. 'callback' => 'views_boxes_view_id_callback',
  288. 'wrapper' => "{$key}-group",
  289. 'method' => 'replace',
  290. 'effect' => 'fade',
  291. ),
  292. );
  293. }
  294. else {
  295. $settings_form[$key] = array(
  296. '#type' => 'textfield',
  297. '#default_value' => $this->options['settings'][$key],
  298. '#title' => $arg->options['ui_name'] ? $arg->options['ui_name'] : $key,
  299. );
  300. }
  301. }
  302. // if there are no settings we want to keep the field set
  303. // but make it clear that it is empty
  304. if (empty($settings_form)) {
  305. $form['settings']['empty'] = array(
  306. '#markup' => t('This view has no settings'),
  307. );
  308. }
  309. else {
  310. $form['settings'] = $form['settings'] + $settings_form;
  311. }
  312. }
  313. else {
  314. // Leave the setting fieldset but make it clear that we do not yet have a view
  315. $form['settings']['empty'] = array(
  316. '#markup' => t('No view selected'),
  317. );
  318. }
  319. $form['view_more_text'] = array(
  320. '#title' => t('More link text'),
  321. '#description' => t('Optional text for a custom link that follows the listing.'),
  322. '#type' => 'textfield',
  323. '#default_value' => $this->options['view_more_text'],
  324. );
  325. $form['view_more_link'] = array(
  326. '#title' => t('More link URL'),
  327. '#description' => t('Optional URL for a custom link that follows the listing. Need to be a valid URL.'),
  328. '#type' => 'textfield',
  329. '#default_value' => $this->options['view_more_link'],
  330. );
  331. $form['expose_filters'] = array(
  332. '#title' => t('Expose filters in render'),
  333. '#description' => t('By default in Views Boxes, exposed filters are meant to be used for editing the box settings for the view and are not rendered in the output. Checking this box will show them in the rendered view.'),
  334. '#type' => 'checkbox',
  335. '#default_value' => $this->options['expose_filters'],
  336. );
  337. return $form;
  338. }
  339. /**
  340. * Implementation of boxes_box::render().
  341. */
  342. public function render() {
  343. $content = '';
  344. // Only build a view if we have one
  345. if (
  346. ($view_info = explode('--', $this->options['view'])) &&
  347. ($view = views_get_view($view_info[0], TRUE))
  348. ) {
  349. $display_name = $view_info[1];
  350. $view->set_exposed_input($this->options['settings']);
  351. $view->exposed_data = $this->options['settings'];
  352. $view->set_display($display_name);
  353. // Set arguments
  354. $args = array();
  355. $view->display_handler->get_handlers('argument');
  356. // Run though the arguments and set values if we have them, set wildcard otherwise
  357. foreach ($view->display_handler->handlers['argument'] as $key => $arg) {
  358. if (!empty($this->options['settings'][$key])) {
  359. $args[] = $this->options['settings'][$key];
  360. }
  361. else {
  362. $args[] = $arg->options['exception']['value'];
  363. }
  364. }
  365. if (!empty($args)) {
  366. $view->set_arguments($args);
  367. }
  368. // we need to execute before we remove the exposed widgets
  369. // but this is not an issue because render will not re-execute
  370. $view->pre_execute($args);
  371. $view->execute($display_name);
  372. $view->post_execute();
  373. // We want to remove the filters by default
  374. if (!isset($this->options['expose_filters']) || !$this->options['expose_filters']) {
  375. unset($view->exposed_widgets);
  376. }
  377. $content = $view->render($display_name);
  378. // Let's use the box title first,
  379. // if it is not set, let's use the views
  380. // if it is set to <none>, let's not have a title
  381. $title = $this->title;
  382. if (!$title) {
  383. $options = $view->display_handler->options;
  384. if ($options['defaults']['title']) {
  385. $title = isset($view->display['default']->display_options['title']) ? check_plain($view->display['default']->display_options['title']) : NULL;
  386. }
  387. else {
  388. $title = isset($view->display_handler->options['title']) ? check_plain($view->display_handler->options['title']) : NULL;
  389. }
  390. }
  391. elseif ($title == '<none>') {
  392. $title = NULL;
  393. }
  394. }
  395. else {
  396. $content = t("No view selected");
  397. $title = $this->title;
  398. }
  399. // More link
  400. if ($this->options['view_more_text']) {
  401. if (valid_url($this->options['view_more_link'])) {
  402. $content .= '<div class="box-more-link">' .
  403. l($this->options['view_more_text'], $this->options['view_more_link']) . '</div>';
  404. }
  405. }
  406. $box = array(
  407. 'delta' => $this->delta, // Crucial.
  408. 'title' => $title,
  409. 'subject' => $title,
  410. 'content' => $content,
  411. 'is_empty' => FALSE,
  412. 'view_more_text' => $this->options['view_more_text'],
  413. 'view_more_link' => $this->options['view_more_link'],
  414. );
  415. if ((isset($view)) && (count($view->result) == 0) && !boxes_access_edit()) {
  416. $box['is_empty'] = TRUE;
  417. }
  418. if ($display_name) {
  419. $box['current_display'] = $display_name;
  420. }
  421. if ($view->name) {
  422. $box['current_view'] = $view->name;
  423. }
  424. return $box;
  425. }
  426. /*
  427. * This is mostly a copy of exposed_form::render_exposed_form()
  428. *
  429. * But we do not do the render step and instead pass on the form and form_state
  430. */
  431. function render_exposed_form($that, &$form_state, $block = FALSE) {
  432. // Deal with any exposed filters we may have, before building.
  433. $e = $this->options['settings']['exposed_input'];
  434. //$_SESSION['views'][$that->view->name][]
  435. //$that->view->exposed_data = $e;
  436. //$that->view->set_exposed_input($e);
  437. $form_state = $form_state + array(
  438. 'view' => &$that->view,
  439. 'display' => &$that->display,
  440. 'method' => 'get',
  441. 'rerender' => TRUE,
  442. 'no_redirect' => TRUE,
  443. 'always_process' => TRUE,
  444. 'input' => $e
  445. );
  446. // Some types of displays (eg. attachments) may wish to use the exposed
  447. // filters of their parent displays instead of showing an additional
  448. // exposed filter form for the attachment as well as that for the parent.
  449. if (!$that->view->display_handler->displays_exposed() || (!$block && $that->view->display_handler->get_option('exposed_block'))) {
  450. unset($form_state['rerender']);
  451. }
  452. if (!empty($that->ajax)) {
  453. $form_state['ajax'] = TRUE;
  454. }
  455. $form_state['exposed_form_plugin'] = $that;
  456. $form = drupal_build_form('views_exposed_form', $form_state);
  457. foreach ($form as $key => $item) {
  458. if (preg_match("/(^#|^form|submit)/", $key)) {
  459. unset($form[$key]);
  460. }
  461. }
  462. $output = array($form, $form_state);
  463. if (!empty($form_state['js settings'])) {
  464. $that->view->js_settings = $form_state['js settings'];
  465. }
  466. if (!$that->view->display_handler->displays_exposed() || (!$block && $that->view->display_handler->get_option('exposed_block'))) {
  467. return "";
  468. }
  469. else {
  470. return $output;
  471. }
  472. }
  473. }
  474. drupal_add_js(drupal_get_path('module', 'views_boxes') . '/plugins/views_boxes_view.js');
  475. drupal_add_css(drupal_get_path('module', 'views_boxes') . '/plugins/views_boxes_view.css');