views_data_export_plugin_style_export.inc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. <?php
  2. /**
  3. * @file
  4. * Plugin include file for export style plugin.
  5. */
  6. /**
  7. * Generalized style plugin for export plugins.
  8. *
  9. * @ingroup views_style_plugins
  10. */
  11. class views_data_export_plugin_style_export extends views_plugin_style {
  12. /**
  13. * Set options fields and default values.
  14. *
  15. * @return
  16. * An array of options information.
  17. */
  18. function option_definition() {
  19. $options = parent::option_definition();
  20. $options['attach_text'] = array(
  21. 'default' => $this->definition['export feed text'],
  22. 'translatable' => TRUE,
  23. );
  24. $options['provide_file'] = array(
  25. 'default' => FALSE,
  26. 'translatable' => FALSE,
  27. );
  28. $options['filename'] = array(
  29. 'default' => $this->definition['export feed file'],
  30. 'translatable' => FALSE,
  31. );
  32. $options['parent_sort'] = array(
  33. 'default' => FALSE,
  34. 'translatable' => FALSE,
  35. );
  36. return $options;
  37. }
  38. /**
  39. * Options form mini callback.
  40. *
  41. * @param $form
  42. * Form array to add additional fields to.
  43. * @param $form_state
  44. * State of the form.
  45. * @return
  46. * None.
  47. */
  48. function options_form(&$form, &$form_state) {
  49. $form['attach_text'] = array(
  50. '#type' => 'textfield',
  51. '#title' => t('Attach text'),
  52. '#default_value' => $this->options['attach_text'],
  53. '#description' => t('This text is used in building the feed link. By default it is the "alt" text for the feed image.'),
  54. );
  55. $form['provide_file'] = array(
  56. '#type' => 'checkbox',
  57. '#title' => t('Provide as file'),
  58. '#default_value' => $this->options['provide_file'],
  59. '#description' => t('By deselecting this, the xml file will be provided as a feed instead of a file for download.'),
  60. );
  61. $form['filename'] = array(
  62. '#type' => 'textfield',
  63. '#title' => t('Filename'),
  64. '#default_value' => $this->options['filename'],
  65. '#description' => t('The filename that will be suggested to the browser for downloading purposes. You may include replacement patterns from the list below.'),
  66. '#process' => array('ctools_dependent_process'),
  67. '#dependency' => array(
  68. 'edit-style-options-provide-file' => array(TRUE),
  69. ),
  70. );
  71. // General token replacement.
  72. $output = t('<p>The following substitution patterns are available for this display. Use the pattern shown on the left to display the value indicated on the right.</p>');
  73. $items = array(
  74. '%view == ' . t('View name'),
  75. '%display == ' . t('Display name'),
  76. );
  77. $output .= theme('item_list', array('items' => $items));
  78. // Get a list of the available arguments for token replacement.
  79. $options = array();
  80. $count = 0; // This lets us prepare the key as we want it printed.
  81. foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) {
  82. $options[t('Arguments')]['%' . ++$count . '-title'] = t('@argument title', array('@argument' => $handler->ui_name()));
  83. $options[t('Arguments')]['%' . $count . '-value'] = t('@argument value', array('@argument' => $handler->ui_name()));
  84. }
  85. // Append the list with exposed filters stuff.
  86. $options[t('Exposed filters')]['%exposed'] = t('effective exposed filters, like <em>filter1_foo-filter2_bar</em>');
  87. // ...and datestamp.
  88. $time = REQUEST_TIME;
  89. $parts = array(
  90. 'full' => 'Y-m-d\TH-i-s',
  91. 'yy' => 'y',
  92. 'yyyy' => 'Y',
  93. 'mm' => 'm',
  94. 'mmm' => 'M',
  95. 'dd' => 'd',
  96. 'ddd' => 'D',
  97. 'hh' => 'H',
  98. 'ii' => 'i',
  99. 'ss' => 's',
  100. );
  101. foreach ($parts as $part => $format) {
  102. $options[t('Timestamp')]['%timestamp-' . $part] = format_date($time, 'custom', $format);
  103. }
  104. // We have some options, so make a list.
  105. if (!empty($options)) {
  106. foreach (array_keys($options) as $type) {
  107. if (!empty($options[$type])) {
  108. $items = array();
  109. foreach ($options[$type] as $key => $value) {
  110. $items[] = $key . ' == ' . $value;
  111. }
  112. $output .= theme('item_list', array('items' => $items, 'title' => $type));
  113. }
  114. }
  115. }
  116. $form['help'] = array(
  117. '#type' => 'fieldset',
  118. '#title' => t('Replacement patterns'),
  119. '#collapsible' => TRUE,
  120. '#collapsed' => TRUE,
  121. '#value' => $output,
  122. '#dependency' => array(
  123. 'edit-style-options-provide-file' => array(1),
  124. ),
  125. );
  126. $form['parent_sort'] = array(
  127. '#type' => 'checkbox',
  128. '#title' => t('Parent sort'),
  129. '#default_value' => $this->options['parent_sort'],
  130. '#description' => t('Try to apply any additional sorting from the attached display like table sorting to the exported feed.'),
  131. );
  132. }
  133. /**
  134. * Attach this view to another display as a feed.
  135. *
  136. * Provide basic functionality for all export style views like attaching a
  137. * feed image link.
  138. */
  139. function attach_to($display_id, $path, $title) {
  140. $type = $this->definition['export feed type'];
  141. $theme_pattern = array(
  142. 'views_data_export_feed_icon__' . $this->view->name . '__' . $display_id . '__' . $type,
  143. 'views_data_export_feed_icon__' . $this->view->name . '__' . $display_id,
  144. 'views_data_export_feed_icon__' . $this->view->name . '__' . $type,
  145. 'views_data_export_feed_icon__' . $display_id . '__' . $type,
  146. 'views_data_export_feed_icon__' . $display_id,
  147. 'views_data_export_feed_icon__' . $type,
  148. 'views_data_export_feed_icon',
  149. );
  150. $query = $this->view->get_exposed_input();
  151. // Stash the display id we're coming form in the url so we can hijack it later.
  152. if ($this->options['parent_sort']) {
  153. $query['attach'] = $display_id;
  154. }
  155. $this->view->feed_icon .= theme($theme_pattern, array(
  156. 'image_path' => $this->definition['export feed icon'],
  157. 'url' => $this->view->get_url(NULL, $path),
  158. 'query' => $query,
  159. 'text' => $this->options['attach_text'],
  160. )
  161. );
  162. }
  163. function build_sort() {
  164. // Bypass doing any sort of testing if parent sorting is disabled.
  165. if (!$this->options['parent_sort']) {
  166. return parent::build_sort();
  167. }
  168. $displays = $this->display->handler->get_option('displays');
  169. // Here is later. We can get the passed argument and use it to know which
  170. // display we can from and then do some addition processing.
  171. // If the display exists and is attached these two tests will succeed.
  172. if (isset($_GET['attach']) && isset($displays[$_GET['attach']]) && $displays[$_GET['attach']]) {
  173. // Setup the second style we're going to be using to sort on.
  174. $plugin_id = $displays[$_GET['attach']];
  175. $parent_display = $this->view->display[$plugin_id];
  176. $style_name = $parent_display->handler->get_option('style_plugin');
  177. $style_options = $parent_display->handler->get_option('style_options');
  178. $this->extra_style = views_get_plugin('style', $style_name);
  179. $this->extra_style->init($this->view, $parent_display, $style_options);
  180. // Call the second styles sort funciton and return the value.
  181. return $this->extra_style->build_sort();
  182. }
  183. }
  184. function build_sort_post() {
  185. // If we found an extra style plugin earlier, pass off the build_sort_post call to it.
  186. if (isset($this->extra_style)) {
  187. return $this->extra_style->build_sort_post();
  188. }
  189. else {
  190. return parent::build_sort_post();
  191. }
  192. }
  193. /**
  194. * Render the display in this style.
  195. */
  196. function render() {
  197. if ($this->uses_row_plugin() && empty($this->row_plugin)) {
  198. vpr('views_plugin_style_default: Missing row plugin');
  199. return;
  200. }
  201. $output = '';
  202. $rows['header'] = $this->render_header();
  203. $rows['body'] = $this->render_body();
  204. $rows['footer'] = $this->render_footer();
  205. $title = '';
  206. $output .= theme($this->theme_functions(), array('view' => $this->view, 'options' => $this->options, 'rows' => $rows, 'title' => $title));
  207. return $output;
  208. }
  209. function render_header() {
  210. $rows = array();
  211. $title = '';
  212. $output = '';
  213. $output .= theme($this->theme_functions($this->definition['additional themes base'] . '_header'), array('view' => $this->view, 'options' => $this->options, 'rows' => $rows, 'title' => $title));
  214. return $output;
  215. }
  216. function render_footer() {
  217. $rows = array();
  218. $title = '';
  219. $output = '';
  220. $output .= theme($this->theme_functions($this->definition['additional themes base'] . '_footer'), array('view' => $this->view, 'options' => $this->options, 'rows' => $rows, 'title' => $title));
  221. return $output;
  222. }
  223. function render_body() {
  224. if ($this->uses_row_plugin() && empty($this->row_plugin)) {
  225. vpr('views_plugin_style_default: Missing row plugin');
  226. return;
  227. }
  228. // Group the rows according to the grouping field, if specified.
  229. $sets = $this->render_grouping($this->view->result, $this->options['grouping']);
  230. // Render each group separately and concatenate. Plugins may override this
  231. // method if they wish some other way of handling grouping.
  232. $output = '';
  233. foreach ($sets as $title => $records) {
  234. if ($this->uses_row_plugin()) {
  235. $rows = array();
  236. foreach ($records as $row_index => $row) {
  237. $this->view->row_index = $row_index;
  238. $rows[] = $this->row_plugin->render($row);
  239. }
  240. }
  241. else {
  242. $rows = $records;
  243. }
  244. $title = '';
  245. $output .= theme($this->theme_functions($this->definition['additional themes base'] . '_body'), array('view' => $this->view, 'options' => $this->options, 'rows' => $rows, 'title' => $title));
  246. }
  247. unset($this->view->row_index);
  248. return $output;
  249. }
  250. /**
  251. * Provide a full list of possible theme templates used by this style.
  252. */
  253. function theme_functions($hook = NULL) {
  254. if (is_null($hook)) {
  255. $hook = $this->definition['theme'];
  256. }
  257. return views_theme_functions($hook, $this->view, $this->display);
  258. }
  259. /**
  260. * Add any HTTP headers that this style plugin wants to.
  261. */
  262. function add_http_headers() {
  263. $view = &$this->view;
  264. drupal_add_http_header('Cache-Control', 'max-age=60, must-revalidate');
  265. if (!empty($this->definition['export headers'])) {
  266. foreach ($this->definition['export headers'] as $name => $value) {
  267. drupal_add_http_header($name, $value);
  268. }
  269. }
  270. if (isset($this->options['filename']) && !empty($this->options['provide_file'])) {
  271. // General tokens.
  272. $tokens = array(
  273. '%view' => check_plain($view->name),
  274. '%display' => check_plain($view->current_display),
  275. );
  276. // Argument tokens.
  277. $count = 0;
  278. foreach ($view->display_handler->get_handlers('argument') as $arg => $handler) {
  279. $token = '%' . ++$count;
  280. $tokens[$token . '-title'] = check_plain($handler->title());
  281. $tokens[$token . '-value'] = isset($view->args[$count - 1]) ? check_plain($view->args[$count - 1]) : '';
  282. }
  283. // Effective exposed filters token.
  284. $exposed = array();
  285. foreach ($view->display_handler->get_handlers('filter') as $arg => $handler) {
  286. if (!$handler->options['exposed']) {
  287. continue;
  288. }
  289. if (!empty($view->exposed_input[$handler->options['expose']['identifier']])) {
  290. $exposed[] = check_plain($handler->options['expose']['identifier']) . '_' . check_plain($view->exposed_input[$handler->options['expose']['identifier']]);
  291. }
  292. }
  293. if (!empty($exposed)) {
  294. $tokens['%exposed'] = implode('-', $exposed);
  295. }
  296. else {
  297. $tokens['%exposed'] = 'default' ;
  298. }
  299. // Timestamp token.
  300. $time = REQUEST_TIME;
  301. $parts = array(
  302. 'full' => 'Y-m-d\TH-i-s',
  303. 'yy' => 'y',
  304. 'yyyy' => 'Y',
  305. 'mm' => 'm',
  306. 'mmm' => 'M',
  307. 'dd' => 'd',
  308. 'ddd' => 'D',
  309. 'hh' => 'H',
  310. 'ii' => 'i',
  311. 'ss' => 's',
  312. );
  313. foreach ($parts as $part => $format) {
  314. $tokens['%timestamp-' . $part] = format_date($time, 'custom', $format);
  315. }
  316. $filename = strtr($this->options['filename'], $tokens);
  317. if ($filename) {
  318. drupal_add_http_header('Content-Disposition', 'attachment; filename="'. $filename .'"');
  319. }
  320. }
  321. }
  322. }