This commit was manufactured as part of Drupal's Great Git Migration to
[project/ctools.git] / views_content / plugins / content_types / views.inc
1 <?php
2 // $Id$
3
4 /**
5 * @file
6 * Content type plugin to expose all views as content.
7 */
8
9 /**
10 * Implementation of hook_ctools_content_types()
11 */
12 function views_content_views_ctools_content_types() {
13 if (!variable_get('ctools_content_all_views', TRUE)) {
14 return;
15 }
16
17 return array(
18 'title' => t('All views'),
19 'defaults' => array(
20 'override_pager_settings' => FALSE,
21 'use_pager' => FALSE,
22 'nodes_per_page' => 10,
23 'pager_id' => 0,
24 'offset' => 0,
25 'more_link' => FALSE,
26 'feed_icons' => FALSE,
27 'panel_args' => FALSE,
28 'link_to_view' => FALSE,
29 'args' => '',
30 'url' => '',
31 ),
32 'add form' => array(
33 'views_content_views_select_display' => t('Select display'),
34 'views_content_views_content_type_edit_form' => array(
35 'default' => TRUE, // put wrapper here, not on the previous form.
36 'title' => t('Configure view'),
37 ),
38 ),
39 'all contexts' => TRUE,
40 );
41 }
42
43 /**
44 * Return all content types available.
45 */
46 function views_content_views_content_type_content_types($plugin) {
47 $types = array();
48 // It can be fairly intensive to calculate this, so let's cache this in the
49 // cache_views table. The nice thing there is that if views ever change, that
50 // table will always be cleared. Except for the occasional default view, so
51 // we must use the Views caching functions in order to respect Views caching
52 // settings.
53 views_include('cache');
54 $data = views_cache_get('views_content_all', TRUE);
55 if (!empty($data->data)) {
56 $types = $data->data;
57 }
58
59 if (empty($types)) {
60 $views = views_get_all_views();
61
62 foreach ($views as $view) {
63 if (empty($view->disabled)) {
64 $types[$view->name] = _views_content_views_content_type($view);
65 }
66 }
67
68 views_cache_set('views_content_all', $types, TRUE);
69 }
70
71 return $types;
72 }
73
74 /**
75 * Return a single content type.
76 */
77 function views_content_views_content_type_content_type($subtype, $plugin) {
78 $view = views_get_view($name);
79 if (empty($view)) {
80 return;
81 }
82
83 return _views_content_views_content_type($view);
84 }
85
86 /**
87 * Create the content type info array to give back to ctools for a given display.
88 */
89 function _views_content_views_content_type($view) {
90 $title = $view->name;
91
92 $icon = 'icon_views_page_legacy.png';
93
94 return array(
95 'view' => $view->name,
96 'title' => $title,
97 'icon' => $icon,
98 'description' => filter_xss_admin($view->description),
99 'category' => t('Views'),
100 );
101
102 }
103
104 /**
105 * Output function for the 'views' content type.
106 *
107 * Outputs a view based on the module and delta supplied in the configuration.
108 */
109 function views_content_views_content_type_render($subtype, $conf, $panel_args, $contexts) {
110 if (!is_array($contexts)) {
111 $contexts = array($contexts);
112 }
113
114 $view = _views_content_views_update_conf($conf, $subtype);
115
116 if (empty($view) || !is_object($view) || empty($view->display_handler)) {
117 return;
118 }
119
120 if (!$view->display_handler->access($GLOBALS['user'])) {
121 return;
122 }
123
124 $arguments = explode('/', $_GET['q']);
125 $args = $conf['args'];
126
127 foreach ($arguments as $id => $arg) {
128 $args = str_replace("%$id", $arg, $args);
129 }
130
131 foreach ($panel_args as $id => $arg) {
132 $args = str_replace("@$id", $arg, $args);
133 }
134
135 $args = preg_replace(',/?(%\d|@\d),', '', $args);
136 $args = $args ? explode('/', $args) : array();
137
138 if ($conf['panel_args'] && is_array($panel_args)) {
139 $args = array_merge($panel_args, $args);
140 }
141
142 if (isset($conf['context']) && is_array($conf['context'])) {
143 foreach ($conf['context'] as $count => $context_info) {
144 if (!strpos($context_info, '.')) {
145 // old skool: support pre-converter contexts as well.
146 $cid = $context_info;
147 $converter = '';
148 }
149 else {
150 list($cid, $converter) = explode('.', $context_info, 2);
151 }
152 if (!empty($contexts[$cid])) {
153 $arg = ctools_context_convert_context($contexts[$cid], $converter);
154 array_splice($args, $count, 0, array($arg));
155 }
156 }
157 }
158
159 $view->set_arguments($args);
160
161 if ($conf['url']) {
162 $view->override_path = $conf['url'];
163 }
164
165 $block = new stdClass();
166 $block->module = 'views';
167 $block->delta = $view->name .'-'. $view->current_display;
168
169 if (!empty($conf['link_to_view'])) {
170 $block->title_link = $view->get_url();
171 }
172
173 if (!empty($conf['more_link'])) {
174 $block->more = array('href' => $view->get_url());
175 $view->display_handler->set_option('use_more', FALSE);
176 }
177
178 if ($conf['override_pager_settings']) {
179 // Only set use_pager if they differ, this way we can avoid overwriting the
180 // pager type that Views uses.
181 if (!$view->display_handler->get_option('use_pager') || empty($conf['use_pager'])) {
182 $view->display_handler->set_option('use_pager', $conf['use_pager']);
183 }
184 $view->display_handler->set_option('pager_element', $conf['pager_id']);
185 $view->display_handler->set_option('items_per_page', $conf['nodes_per_page']);
186 $view->display_handler->set_option('offset', $conf['offset']);
187 }
188
189 $stored_feeds = drupal_add_feed();
190 $block->content = $view->preview();
191 $block->title = $view->get_title();
192
193 if (empty($view->result) && !$view->display_handler->get_option('empty') && empty($view->style_plugin->definition['even empty'])) {
194 return;
195 }
196
197 if (!empty($conf['feed_icons'])) {
198 $new_feeds = drupal_add_feed();
199 if ($diff = array_diff(array_keys($new_feeds), array_keys($stored_feeds))) {
200 foreach ($diff as $url) {
201 $block->feeds[$url] = $new_feeds[$url];
202 }
203 }
204 }
205
206 $view->destroy();
207 return $block;
208 }
209
210 /**
211 * Returns an edit form for a block.
212 */
213 function views_content_views_select_display(&$form, &$form_state) {
214 $view = views_get_view($form_state['subtype_name']);
215 if (empty($view)) {
216 return;
217 }
218
219 $displays = array();
220 foreach ($view->display as $id => $display) {
221 // Content pane views should never be used this way.
222 if ($display->display_plugin != 'panel_pane') {
223 $displays[$id] = $display->display_title;
224 }
225 }
226
227 $form['display'] = array(
228 '#type' => 'select',
229 '#title' => t('Display'),
230 '#options' => $displays,
231 '#description' => t('Choose which display of this view you wish to use.')
232 );
233 }
234
235 /**
236 * Submit the basic view edit form.
237 *
238 * This just dumps everything into the $conf array.
239 */
240 function views_content_views_select_display_submit(&$form, &$form_state) {
241 $form_state['conf']['display'] = $form_state['values']['display'];
242 }
243
244 /**
245 * Returns an edit form for a block.
246 */
247 function views_content_views_content_type_edit_form(&$form, &$form_state) {
248 $conf = $form_state['conf'];
249 $view = _views_content_views_update_conf($conf, $form_state['subtype_name']);
250
251 if (empty($view) || !is_object($view)) {
252 $form['markup'] = array('#value' => t('Broken/missing/deleted view.'));
253 return;
254 }
255
256 $form_state['title'] = t('Configure view @view (@display)', array('@view' => $view->name, '@display' => $view->display[$conf['display']]->display_title));
257
258 // @todo
259 // If using the older format, just a context is listed. We should go through
260 // and check for that and forcibly set them to the right converter so that
261 // it doesn't get changed to some whacky default. Oooor just let it get changed
262 // to 'no context', I suppose.
263
264 $required = array();
265 if (isset($view->display_handler) && $arguments = $view->display_handler->get_handlers('argument')) {
266 foreach ($arguments as $arg) {
267 $required[] = new ctools_context_optional($arg->ui_name(), 'any');
268 }
269 }
270
271 if ($required) {
272 $form['context'] = ctools_context_converter_selector($form_state['contexts'], $required, isset($conf['context']) ? $conf['context'] : array());
273 }
274
275 $form['link_to_view'] = array(
276 '#type' => 'checkbox',
277 '#default_value' => $conf['link_to_view'],
278 '#title' => t('Link title to view'),
279 );
280
281 $form['more_link'] = array(
282 '#type' => 'checkbox',
283 '#default_value' => $conf['more_link'],
284 '#title' => t('Provide a "more" link that links to the view'),
285 '#description' => t('This is independent of any more link that may be provided by the view itself; if you see two more links, turn this one off. Views will only provide a more link if using the "block" type, however, so if using embed, use this one.'),
286 );
287
288 $form['feed_icons'] = array(
289 '#type' => 'checkbox',
290 '#default_value' => $conf['feed_icons'],
291 '#title' => t('Display feed icons'),
292 );
293
294 $form['pager_settings'] = array(
295 '#type' => 'fieldset',
296 '#collapsible' => FALSE,
297 '#title' => t('Custom pager settings'),
298 );
299
300 $form['pager_settings']['override_pager_settings'] = array(
301 '#type' => 'checkbox',
302 '#title' => t('Use different pager settings from view settings'),
303 '#default_value' => $conf['override_pager_settings'],
304 '#id' => 'override-pager-checkbox',
305 );
306
307 if ($view->display_handler->get_option('use_ajax')) {
308 $form['pager_settings']['warning'] = array(
309 '#value' => '<div>' . t('<strong>Warning: </strong> This view has AJAX enabled. Overriding the pager settings will work initially, but when the view is updated via AJAX, the original settings will be used. You should not override pager settings on Views with the AJAX setting enabled.') . '</div>',
310 );
311 }
312
313 $form['pager_settings']['use_pager'] = array(
314 '#prefix' => '<div class="container-inline">',
315 '#type' => 'checkbox',
316 '#title' => t('Use pager'),
317 '#default_value' => $conf['use_pager'],
318 '#id' => 'use-pager-checkbox',
319 '#process' => array('ctools_dependent_process'),
320 '#dependency' => array('override-pager-checkbox' => array(1)),
321 );
322 $form['pager_settings']['pager_id'] = array(
323 '#type' => 'textfield',
324 '#default_value' => $conf['pager_id'],
325 '#title' => t('Pager ID'),
326 '#size' => 4,
327 '#id' => 'use-pager-textfield',
328 '#process' => array('ctools_dependent_process'),
329 '#dependency' => array('override-pager-checkbox' => array(1), 'use-pager-checkbox' => array(1)),
330 '#dependency_count' => 2,
331 '#suffix' => '</div>',
332 );
333
334 $form['pager_settings']['nodes_per_page'] = array(
335 '#type' => 'textfield',
336 '#default_value' => $conf['nodes_per_page'],
337 '#size' => 4,
338 '#title' => t('Num posts'),
339 '#process' => array('ctools_dependent_process'),
340 '#dependency' => array('override-pager-checkbox' => array(1)),
341 );
342
343 $form['pager_settings']['offset'] = array(
344 '#type' => 'textfield',
345 '#default_value' => $conf['offset'],
346 '#title' => t('Offset'),
347 '#size' => 4,
348 '#description' => t('The number of items to skip and not display.'),
349 '#process' => array('ctools_dependent_process'),
350 '#dependency' => array('override-pager-checkbox' => array(1)),
351 );
352
353 $form['panel_args'] = array(
354 '#type' => 'checkbox',
355 '#title' => t('Send arguments'),
356 '#default_value' => $conf['panel_args'],
357 '#description' => t('Select this to send all arguments from the panel directly to the view. If checked, the panel arguments will come after any context arguments above and precede any additional arguments passed in through the Arguments field below. Note that arguments do not include the base URL; only values after the URL or set as placeholders are considered arguments.'),
358 );
359
360 $form['args'] = array(
361 '#type' => 'textfield',
362 '#default_value' => $conf['args'],
363 '#title' => t('Arguments'),
364 '#size' => 30,
365 '#description' => t('Additional arguments to send to the view as if they were part of the URL in the form of arg1/arg2/arg3. You may use %0, %1, ..., %N to grab arguments from the URL. Or use @0, @1, @2, ..., @N to use arguments passed into the panel. Note: use these values only as a last resort. In future versions of Panels these may go away.'),
366 );
367
368 $form['url'] = array(
369 '#type' => 'textfield',
370 '#default_value' => $conf['url'],
371 '#title' => t('Override URL'),
372 '#size' => 30,
373 '#description' => t('If this is set, override the View URL; this can sometimes be useful to set to the panel URL'),
374 );
375
376 $view->destroy();
377 return $form;
378 }
379
380 /**
381 * Store form values in $conf.
382 */
383 function views_content_views_content_type_edit_form_submit(&$form, &$form_state) {
384 // Copy everything from our defaults.
385 foreach (array_keys($form_state['plugin']['defaults']) as $key) {
386 $form_state['conf'][$key] = $form_state['values'][$key];
387 }
388 }
389
390 /**
391 * Returns the administrative title for a type.
392 */
393 function views_content_views_content_type_admin_title($subtype, $conf) {
394 $view = _views_content_views_update_conf($conf, $subtype);
395
396 if (!is_object($view)) {
397 return t('Deleted/missing view @view', array('@view' => $view));
398 }
399
400 $title = $view->display[$view->current_display]->display_title;
401 return t('View: @name', array('@name' => $view->name . '-' . $title));
402 }
403
404 /**
405 * Returns the administrative title for a type.
406 */
407 function views_content_views_content_type_admin_info($subtype, $conf, $contexts) {
408 $view = _views_content_views_update_conf($conf, $subtype);
409
410 if (!is_object($view)) {
411 return t('Deleted/missing view @view', array('@view' => $view));
412 }
413
414 $display = empty($conf['display']) ? $view->current_display : $conf['display'];
415 $block->title = t('View information');
416
417 $block->content = '<ul>';
418 $block->content .= '<li>' . t('Using display @display.', array('@display' => $view->display[$display]->display_title)) . '</li>';
419
420 if (!empty($conf['context']) && $arguments = $view->display_handler->get_handlers('argument')) {
421 $argument = reset($arguments);
422 foreach ($conf['context'] as $count => $context_info) {
423 if (!$argument) {
424 break;
425 }
426
427 if (!strpos($context_info, '.')) {
428 // old skool: support pre-converter contexts as well.
429 $cid = $context_info;
430 $converter = '';
431 }
432 else {
433 list($cid, $converter) = explode('.', $context_info, 2);
434 }
435
436 if (!empty($contexts[$cid])) {
437 $converters = ctools_context_get_converters($cid . '.', $contexts[$cid]);
438 $converter = !empty($converters[$context_info]) ? $converters[$context_info] : t('Default');
439 $block->content .= '<li>' . t('Argument @arg using context @context converted into @converter', array(
440 '@arg' => $argument->ui_name(), '@context' => $contexts[$cid]->get_identifier(),
441 '@converter' => $converter)) . '</li>';
442 }
443 $argument = next($arguments);
444 }
445 }
446
447 $block->content .= '<li>' . t('@count items displayed.', array('@count' => $conf['nodes_per_page'])) . '</li>';
448 if ($conf['use_pager']) {
449 $block->content .= '<li>' . t('With pager.') . '</li>';
450 }
451 else {
452 $block->content .= '<li>' . t('Without pager.') . '</li>';
453 }
454
455 if ($conf['offset']) {
456 $block->content .= '<li>' . t('Skipping first @count results', array('@count' => $conf['offset'])) . '</li>';
457 }
458 if ($conf['more_link']) {
459 $block->content .= '<li>' . t('With more link.') . '</li>';
460 }
461 if ($conf['feed_icons']) {
462 $block->content .= '<li>' . t('With feed icon.') . '</li>';
463 }
464 if ($conf['panel_args']) {
465 $block->content .= '<li>' . t('Sending arguments.') . '</li>';
466 }
467 if ($conf['args']) {
468 $block->content .= '<li>' . t('Using arguments: @args', array('@args' => $conf['args'])) . '</li>';
469 }
470 if ($conf['url']) {
471 $block->content .= '<li>' . t('Using url: @url', array('@url' => $conf['url'])) . '</li>';
472 }
473
474 $view->destroy();
475 return $block;
476 }
477
478 /**
479 * Update the $conf to deal with updates from Drupal 5.
480 *
481 * @param &$conf
482 * The $conf array to modify.
483 * @param $subtype
484 * The subtype in use. This should just be the view name, but in older
485 * versions it was the view name with a dash and the display ID.
486 * If this is the case, we can use it to correct the 'display' setting
487 * in the $conf.
488 * @return
489 * The $view with the initialized display. If the $view could not be
490 * loaded, the name attempted will be loaded for use in errors.
491 * Correct error checking on this function checks against is_object().
492 */
493 function _views_content_views_update_conf(&$conf, $subtype) {
494 $task = views_content_views_ctools_content_types();
495
496 // Special: Existing content types get a different default than new ones:
497 if (!empty($conf) && !isset($conf['override_pager_settings'])) {
498 $conf['override_pager_settings'] = TRUE;
499 }
500
501 // Make sure that our defaults are always set if there is no
502 // previous setting. This helps updates go more smoothly.
503 foreach ($task['defaults'] as $key => $value) {
504 if (!isset($conf[$key])) {
505 $conf[$key] = $value;
506 }
507 }
508
509 if (strpos($subtype, '-')) {
510 list($name, $display) = explode('-', $subtype);
511 $view = views_get_view($name);
512 if (!isset($conf['display'])) {
513 $conf['display'] = $display;
514 }
515 }
516 else {
517 $name = $subtype;
518 $view = views_get_view($subtype);
519 $display = isset($conf['display']) ? $conf['display'] : 'default';
520 }
521
522 if (empty($view)) {
523 return $name;
524 }
525
526 $view->set_display($display);
527 // $view->current_display will now reflect this value.
528
529 // If set NOT to override, go ahead and refresh from the view.
530 if (empty($conf['override_pager_settings'])) {
531 $conf['use_pager'] = $view->display_handler->get_option('use_pager');
532 $conf['pager_id'] = $view->display_handler->get_option('element_id');
533 $conf['nodes_per_page'] = $view->display_handler->get_option('items_per_page');
534 $conf['offset'] = $view->display_handler->get_option('offset');
535 }
536
537 return $view;
538 }