views.module 49.7 KB
Newer Older
1
<?php
2
// $Id$
3
4
5
6

// ---------------------------------------------------------------------------
// Drupal Hooks

7
/**
8
9
 * Implementation of hook_help()
 */
10
11
function views_help($section) {
  switch ($section) {
12
    case 'admin/help#views':
13
      return t('The views module creates customized views of node lists. You may need to activate the Views UI module to get to the user administration pages.');
14
  }
15
16
}

17
/**
18
19
 * Implementation of hook_menu()
 */
20
21
function views_menu($may_cache) {
  $items = array();
22
  global $locale;
23
24

  if ($may_cache) {
25
    views_load_cache();
26
27
    // Invalidate the views cache to ensure that views data gets rebuilt.
    // This is the best way to tell that module configuration has changed.
28
    if (arg(0) == 'admin' && arg(2) == 'modules') {
29
30
      views_invalidate_cache();
    }
31

32
    $result = db_query("SELECT * FROM {view_view} WHERE page = 1");
33
34
    $views_with_inline_args = array();

35
    while ($view = db_fetch_object($result)) {
36
37
      // unpack the array
      $view->access = ($view->access ? explode(', ', $view->access) : array());
38
39
40
41

      // This happens before the next check; even if it's put off for later
      // it is still used.
      $used[$view->name] = true;
merlinofchaos's avatar
merlinofchaos committed
42

43
      if (strrpos($view->url, '$arg')) {
44
45
46
        $arg_result = db_query("SELECT * FROM {view_argument} WHERE vid = %d", $view->vid);
        while ($view->argument[] = db_fetch_array($arg_result));
        array_pop($view->argument); // get rid of the NULL at the end.
47
        $views_with_inline_args[$view->name] = $view;
48
49
        continue;
      }
50
      _views_create_menu_item($items, $view, $view->url);
51
    }
52
    $default_views = _views_get_default_views();
53
54
    $views_status = variable_get('views_defaults', array());

55
    foreach ($default_views as $name => $view) {
merlinofchaos's avatar
merlinofchaos committed
56
      if ($view->page && !$used[$name] &&
57
       ($views_status[$name] == 'enabled' || (!$view->disabled && $views_status[$name] != 'disabled'))) {
58

59
        if (strrpos($view->url, '$arg')) {
60
          $views_with_inline_args[$view->name] = $view;
61
62
          continue;
        }
63

64
        _views_create_menu_item($items, $view, $view->url);
65
66
      }
    }
67
    cache_set("views_with_inline_args:$locale", 'cache', serialize($views_with_inline_args));
68
69
  }
  else {
70
    $data = cache_get("views_with_inline_args:$locale", 'cache');
71
72
73
74
    $views = unserialize($data->data);
    if (is_array($views)) {
      foreach ($views as $view) {
        // Do substitution on args.
75
        $view_args = array();
76
        $menu_path = array();
77
78
79
80
        foreach (explode('/', $view->url) as $num => $element) {
          if ($element == '$arg') {
            $menu_path[] = arg($num);
            $view_args[] = arg($num);
81
            $view->args[] = arg($num);
82
83
          }
          else {
84
            $menu_path[] = $element;
85
86
87
          }
        }
        $path = implode('/', $menu_path);
88
        _views_create_menu_item($items, $view, $path, MENU_CALLBACK, $view_args);
89
      }
90
    }
91
92
93
94
  }
  return $items;
}

95
96
97
/**
 * Helper function to add a menu item for a view.
 */
98
function _views_create_menu_item(&$items, $view, $path, $local_task_type = MENU_NORMAL_ITEM, $args = array()) {
99
100
101
102
103
104
105
  static $roles = NULL;
  if ($roles == NULL) {
    global $user;
    $roles = array_keys($user->roles);
  }
  $title = views_get_title($view, 'menu');
  $type = _views_menu_type($view);
106
107
108
  if ($type == MENU_LOCAL_TASK || $type == MENU_DEFAULT_LOCAL_TASK) {
    $weight = $view->menu_tab_weight;
  }
109
  $access = !$view->access || array_intersect($view->access, $roles);
110
  $items[] = _views_menu_item($path, $title, $view, $args, $access, $type, $weight);
merlinofchaos's avatar
merlinofchaos committed
111

112
  if ($type == MENU_DEFAULT_LOCAL_TASK) {
113
    $items[] = _views_menu_item(dirname($path), $title, $view, $args, $access, $local_task_type, $weight);
114
115
116
  }
}

117
118
119
/**
 * Helper function to create a menu item for a view.
 */
120
121
function _views_menu_item($path, $title, $view, $args, $access, $type, $weight = NULL) {
  array_unshift($args, $view->name);
122
  $retval = array('path' => $path,
123
124
125
    'title' => $title,
    'callback' => 'views_view_page',
    'callback arguments' => $args,
126
    'access' => user_access('access content') && $access,
127
128
    'type' => $type,
  );
129
130
131
132
  if ($weight !== NULL) {
    $retval['weight'] = $weight;
  }
  return $retval;
133
134
}

135
136
137
/**
 * Determine what menu type a view needs to use.
 */
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
function _views_menu_type($view) {
  if ($view->menu) {
    if ($view->menu_tab_default) {
      $type = MENU_DEFAULT_LOCAL_TASK;
    }
    else if ($view->menu_tab) {
      $type = MENU_LOCAL_TASK;
    }
    else {
      $type = MENU_NORMAL_ITEM;
    }
  }
  else {
    $type = MENU_CALLBACK;
  }
  return $type;
}
155

156
/**
157
158
 * Implementation of hook_block()
 */
159
160
161
function views_block($op = 'list', $delta = 0) {
  $block = array();
  if ($op == 'list') {
162
    views_load_cache();
163
    // Grab views from the database and provide them as blocks.
164
    $result = db_query("SELECT vid, block_title, page_title, name FROM {view_view} WHERE block = 1");
165
    while ($view = db_fetch_object($result)) {
166
      $block[$view->name]['info'] = views_get_title($view, 'block-info');
167
    }
168

169
    $default_views = _views_get_default_views();
170
171
    $views_status = variable_get('views_defaults', array());

172
    foreach ($default_views as $name => $view) {
173
174
175
      if (!isset($block[$name]) && $view->block &&
        ($views_status[$name] == 'enabled' || (!$view->disabled && $views_status[$name] != 'disabled'))) {
        $block[$name]['info'] = views_get_title($view, 'block');
176
      }
177
178
179
180
181
182
183
184
    }
    return $block;
  }
  else if ($op == 'view') {
    return views_view_block($delta);
  }
}

185
186
187
// ---------------------------------------------------------------------------
// View Construction

188
/**
189
190
191
 * Ensure that all the arrays in a view exist so we don't run into array
 * operations on a non-array error.
 */
merlinofchaos's avatar
merlinofchaos committed
192
function _views_check_arrays(&$view) {
merlinofchaos's avatar
merlinofchaos committed
193
  $fields = array('field', 'sort', 'argument', 'filter', 'exposed_filter', 'access');
194
195
196
197
198

  foreach($fields as $field) {
    if (!is_array($view->$field)) {
      $view->$field = array();
    }
199
200
  }
  return $view;
201
202
}

203
/**
204
205
206
 * This function loads a view by name or vid; if not found in db, it looks
 * for a default view by that name.
 */
207
function views_get_view($view_name) {
208
  views_load_cache();
209
  $view = _views_load_view($view_name);
210
  if ($view) {
merlinofchaos's avatar
merlinofchaos committed
211
    return $view;
212
  }
merlinofchaos's avatar
merlinofchaos committed
213

214
215
  if (is_int($view_name)) {
    return; // don't bother looking if view_name is an int!
216
  }
merlinofchaos's avatar
merlinofchaos committed
217

218
  $default_views = _views_get_default_views();
merlinofchaos's avatar
merlinofchaos committed
219

220
221
  if (isset($default_views[$view_name])) {
    return $default_views[$view_name];
222
  }
merlinofchaos's avatar
merlinofchaos committed
223
224
}

225
/**
226
 * This views a view by page, and should only be used as a callback.
227
228
229
230
231
232
233
234
 *
 * @param $view_name
 *   The name or id of the view to load
 * @param $args
 *   The arguments from the end of the url. Usually passed from the menu system.
 *
 * @return
 *   The view page.
235
 */
236
237
function views_view_page() {
  $args = func_get_args();
238
239
  $view_name = array_shift($args);
  $view = views_get_view($view_name);
240

241
242
243
244
  if (!$view) {
    drupal_not_found();
    exit;
  }
245

246
  $output = views_build_view('page', $view, $args, $view->use_pager, $view->nodes_per_page);
247
  if ($output === FALSE) {
248
249
250
251
    drupal_not_found();
    exit;
  }

252
  return $output;
253
254
}

255
/**
256
257
 * This views a view by block. Can be used as a callback or programmatically.
 */
258
function views_view_block($vid) {
259
  views_load_cache();
260
  $view = views_get_view($vid);
261

262
  if (!$view || !$view->block) {
263
    return NULL;
264
  }
265

266
  global $user;
267
268
269
270
  if (!$user->roles) {
    return NULL;
  }

271
272
273
274
275
  $roles = array_keys($user->roles);
  if ($view->access && !array_intersect($roles, $view->access)) {
    return NULL;
  }

276
  $content = views_build_view('block', $view, array(), false, $view->nodes_per_block);
277
278
279
280
281
282
283
284
  if ($content) {
    $block['content'] = $content;
    $block['subject'] = views_get_title($view, 'block');
    return $block;
  }
  else {
    return NULL;
  }
285
286
}

287
function &views_set_current_view(&$view) {
288
289
290
291
292
293
294
295
296
297
298
  static $current_view = NULL;
  if ($view !== NULL) {
    unset($current_view);
    $current_view = &$view;
    unset($GLOBALS['current_view']);
    $GLOBALS['current_view'] = &$view;
  }
  return $current_view;
}

function &views_get_current_view() {
299
300
  $dummy = NULL;
  return views_set_current_view($dummy);
301
302
}

303
/**
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
 * This builds the basic view.
 * @param $type
 *    'page' -- Produce output as a page, sent through theme.
 *      The only real difference between this and block is that
 *      a page uses drupal_set_title to change the page title.
 *    'block' -- Produce output as a block, sent through theme.
 *    'embed' -- Use this if you want to embed a view onto another page,
 *      and don't want any block or page specific things to happen to it.
 *    'result' -- return an $info array. The array contains:
 *      query: The actual query ran.
 *      countquery: The count query that would be run if limiting was required.
 *      summary: True if an argument was missing and a summary was generated.
 *      level: What level the missing argument was at.
 *      result: Database object you can use db_fetch_object on.
 *    'items' -- return info array as above, except instead of result,
 *      items: An array of objects containing the results of the query.
 * @param $view
 *   The actual view object. Use views_get_view() if you only have the name or
 *   vid.
 * @param $args
 *   args taken from the URL. Not relevant for many views. Can be null.
 * @param $use_pager
 *   If set, use a pager. Set this to the pager id you want it
 *   to use if you plan on using multiple pagers on a page. To go with the
328
329
 *   default setting, set to $view->use_pager. Note that the pager element
 *   id will be decremented in order to have the IDs start at 0.
330
331
332
333
334
335
336
 * @param $limit
 *   Required if $use_pager is set; if $limit is set and $use_pager is
 *   not, this will be the maximum number of records returned. This is ignored
 *   if using a view set to return a random result. To go with the default
 *   setting set to $view->nodes_per_page or $view->nodes_per_block. If
 *   $use_pager is set and this field is not, you'll get a SQL error. Don't
 *   do that!
337
338
339
340
 * @param $page
 *   $use_pager is false, and $limit is !0, $page tells it what page to start
 *   on, in case for some reason a particular section of view is needed,
 *   without paging on.
341
*/
342
function views_build_view($type, &$view, $args = array(), $use_pager = false, $limit = 0, $page = 0) {
343
344
  views_load_cache();

345
346
347
348
349
  // Fix a number of annoying whines when NULL is passed in..
  if ($args == NULL) {
    $args = array();
  }

350
  views_set_current_view($view);
merlinofchaos's avatar
merlinofchaos committed
351

352
353
  $view->build_type = $type;
  $view->type = ($type == 'block' ? $view->block_type : $view->page_type);
354

355
356
  if ($view->view_args_php) {
    ob_start();
357
358
359
360
    $result = eval($view->view_args_php);
    if (is_array($result)) {
      $args = $result;
    }
361
362
363
    ob_end_clean();
  }

364
  $plugins = _views_get_style_plugins();
365
366
367
  if ($view->query) {
    $info['query'] = $view->query;
    $info['countquery'] = $view->countquery;
merlinofchaos's avatar
merlinofchaos committed
368

369
    if ($plugins[$view->type]['needs_table_header']) {
370
      $view->table_header = _views_construct_header($view, _views_get_fields());
371
    }
merlinofchaos's avatar
merlinofchaos committed
372
373
  }
  else {
374
    views_load_query();
375

merlinofchaos's avatar
merlinofchaos committed
376
    $info = _views_build_query($view, $args);
377
    if ($info['fail']) {
378
      return FALSE;
379
    }
merlinofchaos's avatar
merlinofchaos committed
380
381
  }

382
  // Run-time replacement so we can do cacheing
383
384
385
386
  $replacements = module_invoke_all('views_query_substitutions', $view);
  foreach ($replacements as $src => $dest) {
    $info['query'] = str_replace($src, $dest, $info['query']);
    $info['countquery'] = str_replace($src, $dest, $info['countquery']);
387

388
389
390
391
    if (is_array($info['args'])) {
      foreach ($info['args'] as $id => $arg) {
        $info['args'][$id] = str_replace($src, $dest, $arg);
      }
392
    }
393
  }
394

395
396
  $query = db_rewrite_sql($info['query'], 'node');

397
  if ($use_pager) {
398
    $cquery = db_rewrite_sql($info['countquery'], 'node', 'nid', $info['rewrite_args']);
399
    $result = pager_query($query, $limit, $use_pager - 1, $cquery, $info['args']);
400
    $view->total_rows = $GLOBALS['pager_total_items'][$use_pager - 1];
401
402
  }
  else {
403
    $result = ($limit ? db_query_range($query, $info['args'], $page * $limit, $limit) : db_query($query, $info['args']));
404
  }
405
  $view->num_rows = db_num_rows($result);
406
407
  if ($type == 'result') {
    $info['result'] = $result;
merlinofchaos's avatar
merlinofchaos committed
408
    return $info;
409
410
  }

merlinofchaos's avatar
merlinofchaos committed
411
  $items = array();
412
413
414
  while ($item = db_fetch_object($result)) {
    $items[] = $item;
  }
415
416
417
418
419

  if ($type == 'items') {
    $info['items'] = $items;
    return $info;
  }
420

421
422
423
  // Call a hook that'll let modules modify the view just before it is displayed.
  foreach (module_implements('views_pre_view') as $module) {
    $function = $module .'_views_pre_view';
424
    $output .= $function($view, $items);
425
426
  }

427
428
  $view->real_url = views_get_url($view, $args);

429
  $view->use_pager = $use_pager;
430
  $view->pager_limit = $limit;
431
  $output .= views_theme('views_view', $view, $type, $items, $info['level'], $args);
merlinofchaos's avatar
merlinofchaos committed
432

433
434
435
  // Call a hook that'll let modules modify the view just after it is displayed.
  foreach (module_implements('views_post_view') as $module) {
    $function = $module .'_views_post_view';
436
    $output .= $function($view, $items, $output);
437
438
  }

merlinofchaos's avatar
merlinofchaos committed
439
  return $output;
440
441
}

442
443
444
// ---------------------------------------------------------------------------
// Utility

445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
/**
 * Load the cache sub-module
 */
function views_load_cache() {
  $path = drupal_get_path('module', 'views');
  require_once("./$path/views_cache.inc");
}

/**
 * Load the query sub-module
 */
function views_load_query() {
  $path = drupal_get_path('module', 'views');
  require_once("./$path/views_query.inc");
}

merlinofchaos's avatar
merlinofchaos committed
461
/**
462
 * Easily theme any item to a view.
merlinofchaos's avatar
merlinofchaos committed
463
 * @param $function
464
465
466
467
468
469
470
 *   The name of the function to call.
 * @param $view
 *   The view being themed.
 */
function views_theme() {
  $args = func_get_args();
  $function = array_shift($args);
471
  $view = $args[0];
472
473
474
475
476
477
478
479
480
481

  if (!($func = theme_get_function($function . "_" . $view->name))) {
    $func = theme_get_function($function);
  }

  if ($func) {
    return call_user_func_array($func, $args);
  }
}

482
483
484
485
486
487
488
489
490
491
492
493
494
495
/**
 * Easily theme any item to a field name.
 * field name will be in the format of TABLENAME_FIELDNAME
 * You have to understand a bit about the views data to utilize this.
 *
 * @param $function
 *   The name of the function to call.
 * @param $field_name
 *   The field being themed.
 */
function views_theme_field() {
  $args = func_get_args();
  $function = array_shift($args);
  $field_name = array_shift($args);
496
  $view = array_pop($args);
497

498
499
500
501
  if (!($func = theme_get_function($function . '_' . $view->name . '_' . $field_name))) {
    if (!($func = theme_get_function($function . '_' . $field_name))) {
      $func = theme_get_function($function);
    }
502
503
504
505
506
507
508
  }

  if ($func) {
    return call_user_func_array($func, $args);
  }
}

509
/**
510
 * Figure out what timezone we're in; needed for some date manipulations.
511
 */
512
513
514
515
516
517
518
function _views_get_timezone() {
  global $user;
  if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
    $timezone = $user->timezone;
  }
  else {
    $timezone = variable_get('date_default_timezone', 0);
merlinofchaos's avatar
merlinofchaos committed
519
520
  }

521
522
523
524
  // set up the database timezone
  if (in_array($GLOBALS['db_type'], array('mysql', 'mysqli'))) {
    static $already_set = false;
    if (!$already_set) {
merlinofchaos's avatar
merlinofchaos committed
525
      if ($GLOBALS['db_type'] == 'mysqli' || version_compare(mysql_get_server_info(), '4.1.3', '>=')) {
526
527
528
529
530
531
        db_query("SET @@session.time_zone = '+00:00'");
      } 
      $already_set = true;
    }
  }

532
  return $timezone;
533
}
534
535
536
537
538
539
540

/**
 * Figure out what the URL of the view we're currently looking at is.
 */
function views_get_url($view, $args) {
  $url = $view->url;

541
542
543
544
545
  if (!empty($url)) {
    $where = 1;
    foreach ($args as $arg) {
      // This odd construct prevents us from strposing once there is no
      // longer an $arg to replace.
546
      if ($where && $where = strpos($url, '$arg')) {
547
548
549
550
551
        $url = substr_replace($url, $arg, $where, 4);
      }
      else {
        $url .= "/$arg";
      }
552
553
554
555
556
557
    }
  }

  return $url;
}

558
/**
559
560
 * Figure out what the title of a view should be.
 */
561
562
563
564
function views_get_title($view, $context = 'menu', $args = NULL) {
  if ($context == 'menu' && $view->menu_title)
    return $view->menu_title;

565
566
567
568
  if ($context == 'block-info') {
    return $view->description ? $view->description : $view->name;
  }

569
  if ($args === NULL) {
570
    $args = $view->args;
571
  }
572
573
574
575
576
577
578
579
580
581
  // Grab the title from the highest argument we got. If there is no such
  // title, track back until we find a title.

  if (is_array($args)) {
    $rargs = array_reverse(array_keys($args));
    foreach ($rargs as $arg_id) {
      if ($title = $view->argument[$arg_id]['title']) {
        break;
      }
    }
582
  }
583

584
  if (!$title && ($context == 'menu' || $context == 'page')) {
585
    $title = $view->page_title;
586
587
  }

588
  if (!$title) {
589
    $title = $view->block_title;
590
591
  }

592
593
594
595
  if (!$view->argument) {
    return $title;
  }

596
597
  $arginfo = _views_get_arguments();
  foreach ($view->argument as $i => $arg) {
598
599
600
    if (!isset($args[$i])) {
      break;
    }
601
    $argtype = $arg['type'];
602
603
604
605
    if ($arg['wildcard'] == $args[$i] && $arg['wildcard_substitution'] != '') {
      $title = str_replace("%" . ($i + 1), $arg['wildcard_substitution'], $title);
    }
    else if (function_exists($arginfo[$argtype]['handler'])) {
606
      // call the handler
607
      $rep = $arginfo[$argtype]['handler']('title', $args[$i], $argtype);
608
609
610
611
612
613
      $title = str_replace("%" . ($i + 1), $rep, $title);
    }
  }
  return $title;
}

614
/**
615
616
 * Determine whether or not a view is cacheable. A view is not cacheable if
 * there is some kind of user input or data required. For example, views
617
 * that need to restrict to the 'current' user, or any views that require
618
619
 * arguments or allow click-sorting are not cacheable.
 */
merlinofchaos's avatar
merlinofchaos committed
620
621
function _views_is_cacheable(&$view) {
  // views with arguments are immediately not cacheable.
622
  if (!empty($view->argument) || !empty($view->exposed_filter) || !empty($view->no_cache)) {
merlinofchaos's avatar
merlinofchaos committed
623
    return false;
624
  }
merlinofchaos's avatar
merlinofchaos committed
625
626
627
628

  $filters = _views_get_filters();

  foreach ($view->filter as $i => $filter) {
629
    if ($filters[$filter['field']]['cacheable'] == 'no')  {
merlinofchaos's avatar
merlinofchaos committed
630
      return false;
631
    }
merlinofchaos's avatar
merlinofchaos committed
632
  }
633

634
  foreach ($view->field as $i => $field) {
635
    if ($field['sortable']) {
636
      return false;
637
638
    }
  }
merlinofchaos's avatar
merlinofchaos committed
639
640
641
  return true;
}

642
643
644
645
646
647
648
/**
 * Invalidate the views cache, forcing a rebuild on the next grab of table data.
 */
function views_invalidate_cache() {
  cache_clear_all('views_', 'cache', true);
}

649
650
651
// ---------------------------------------------------------------------------
// Database functions

652
/**
653
654
 * Provide all the fields in a view.
 */
655
function _views_view_fields() {
656
  return array('vid', 'name', 'description', 'access', 'page', 'page_title', 'page_header', 'page_header_format', 'page_footer', 'page_footer_format', 'page_empty', 'page_empty_format', 'page_type', 'use_pager', 'nodes_per_page', 'url', 'menu', 'menu_tab', 'menu_tab_default', 'menu_tab_weight', 'menu_title', 'block', 'block_title', 'block_use_page_header', 'block_header', 'block_header_format', 'block_use_page_footer', 'block_footer', 'block_footer_format', 'block_use_page_empty', 'block_empty', 'block_empty_format', 'block_type', 'nodes_per_block', 'block_more', 'url', 'breadcrumb_no_home', 'changed', 'query', 'countquery', 'view_args_php');
657
658
}

659
/**
660
661
 * Delete a view from the database.
 */
662
663
function _views_delete_view($view) {
  $view->vid = intval($view->vid);
664
  if (!$view->vid) {
665
    return;
666
  }
667

merlinofchaos's avatar
merlinofchaos committed
668
669
670
671
  db_query("DELETE FROM {view_view} where vid=%d", $view->vid);
  db_query("DELETE FROM {view_sort} where vid=%d", $view->vid);
  db_query("DELETE FROM {view_argument} where vid=%d", $view->vid);
  db_query("DELETE FROM {view_tablefield} where vid=%d", $view->vid);
672
673
}

674
675
676
677
678
679
680
/**
 * Load a view from the database -- public version of the function.
 */
function views_load_view($arg) {
  return _views_load_view($arg);
}

681
/**
682
 * Load a view from the database.
683
 * (deprecated; use views_load_view in favor of this function).
684
 */
685
function _views_load_view($arg) {
686
687
688
689
690
  static $cache = array();
  $which = is_numeric($arg) ? 'vid' : 'name';
  if (isset($cache[$which][$arg])) {
    return $cache[$which][$arg];
  }
691

692
  $where = (is_numeric($arg) ? "v.vid =  %d" : "v.name = '%s'");
693
  $view = db_fetch_object(db_query("SELECT v.* FROM {view_view} v WHERE $where", $arg));
694

695
  if (!$view->name) {
696
    return NULL;
697
  }
698

699
700
  $view->access = ($view->access ? explode(', ', $view->access) : array());

701
702
703
  // load the sorting criteria too.
  $result = db_query("SELECT * FROM {view_sort} vs WHERE vid = $view->vid ORDER BY position ASC");

merlinofchaos's avatar
merlinofchaos committed
704
705
  $view->sort = array();
  while ($sort = db_fetch_array($result)) {
706
707
708
    if (substr($sort['field'], 0, 2) == 'n.') {
      $sort['field'] = 'node' . substr($sort['field'], 1);
    }
709
    $sort['id'] = $sort['field'];
merlinofchaos's avatar
merlinofchaos committed
710
    $view->sort[] = $sort;
711
712
  }

713
  $result = db_query("SELECT * FROM {view_argument} WHERE vid = $view->vid ORDER BY position ASC");
714
715
716

  $view->argument = array();
  while ($arg = db_fetch_array($result)) {
717
    $arg['id'] = $arg['type'];
718
719
720
    $view->argument[] = $arg;
  }

721
  $result = db_query("SELECT * FROM {view_tablefield} WHERE vid = $view->vid ORDER BY position ASC");
722

723
  $view->field = array();
724
  while ($arg = db_fetch_array($result)) {
725
726
727
    if ($arg['tablename'] == 'n') {
      $arg['tablename'] = 'node';
    }
728
    $arg['id'] = $arg['fullname'] = "$arg[tablename].$arg[field]";
729
730
    $arg['queryname'] = "$arg[tablename]_$arg[field]";
    $view->field[] = $arg;
731
732
  }

733
  $result = db_query("SELECT * FROM {view_filter} WHERE vid = $view->vid ORDER BY position ASC");
734

735
  $filters = _views_get_filters();
736
  $view->filter = array();
737
738
739
  while ($filter = db_fetch_array($result)) {
    if (substr($filter['field'], 0, 2) == 'n.') {
      $filter['field'] = 'node' . substr($filter['field'], 1);
740
    }
741

merlinofchaos's avatar
merlinofchaos committed
742
743
744
    if ($filter['operator'] == 'AND' ||
        $filter['operator'] == 'OR' ||
        $filter['operator'] == 'NOR' ||
745
746
747
748
749
750
751
        $filters[$filter['field']]['value-type'] == 'array' ) {
      if ($filter['value'] !== NULL && $filter['value'] !== '') {
        $filter['value'] = explode(',', $filter['value']);
      }
      else {
        $filter['value'] = array();
      }
752
    }
753
    $filter['id'] = $filter['field'];
754
    $view->filter[] = $filter;
755
756
  }

757
758
759
760
761
762
763
764
  $result = db_query("SELECT * FROM {view_exposed_filter} WHERE vid = $view->vid ORDER BY position ASC");

  $view->exposed_filter = array();
  while ($arg = db_fetch_array($result)) {
    $arg['id'] = $arg['field'];
    $view->exposed_filter[] = $arg;
  }

765
  $cache['vid'][$view->vid] = $view;
merlinofchaos's avatar
merlinofchaos committed
766
  $cache['name'][$view->name] = $view;
767

768
769
770
  return $view;
}

771
/**
772
773
 * Save a view to the database.
 */
774
function _views_save_view($view) {
merlinofchaos's avatar
merlinofchaos committed
775
  _views_check_arrays($view);
merlinofchaos's avatar
merlinofchaos committed
776
777
778

  // cache the query
  if (_views_is_cacheable($view)) {
779
    views_load_query();
780

merlinofchaos's avatar
merlinofchaos committed
781
    $info = _views_build_query($view);
782
783
    $view->query = _views_replace_args($info['query'], $info['args']);
    $view->countquery = _views_replace_args($info['countquery'], $info['args']);
merlinofchaos's avatar
merlinofchaos committed
784
  }
785
  else {
786
787
    $view->query = NULL;
    $view->countquery = NULL;
788
  }
789

790
791
  $view->access = implode(', ', $view->access);

merlinofchaos's avatar
merlinofchaos committed
792
  $view->changed = time();
793
794
795
796
797
798
799
800
801
802
803
804
  $fields = _views_view_fields();
  if ($view->vid) {
    // update
    // Prepare the query:
    foreach ($view as $key => $value) {
      if (in_array($key, $fields)) {
        $q[] = db_escape_string($key) ." = '%s'";
        $v[] = $value;
      }
    }

    // Update the view in the database:
805
    db_query("UPDATE {view_view} SET " . implode(', ', $q) . " WHERE vid = '$view->vid'", $v);
806
807
808
809
    db_query("DELETE from {view_sort} WHERE vid='$view->vid'");
    db_query("DELETE from {view_argument} WHERE vid='$view->vid'");
    db_query("DELETE from {view_tablefield} WHERE vid='$view->vid'");
    db_query("DELETE from {view_filter} WHERE vid='$view->vid'");
810
    db_query("DELETE from {view_exposed_filter} WHERE vid='$view->vid'");
811
812
813
814
815
816
817
818
819
820
821
822
823
  }
  else {
    // insert

    // This method really saves on typos, and makes it a lot easier to add fields
    // later on.
    $view->vid = db_next_id('{view_view}_vid');

    // Prepare the query:
    foreach ($view as $key => $value) {
      if (in_array((string) $key, $fields)) {
        $k[] = db_escape_string($key);
        $v[] = $value;
824
        $s[] = is_numeric($value) ? '%d' : "'%s'";
825
826
827
      }
    }

828
    db_query("INSERT INTO {view_view} (" . implode(", ", $k) . ") VALUES (" . implode(", ", $s) . ")", $v);
829
830
  }

merlinofchaos's avatar
merlinofchaos committed
831
  foreach ($view->sort as $i => $sort) {
832
    db_query("INSERT INTO {view_sort} (vid, position, field, sortorder, options) VALUES (%d, %d, '%s', '%s', '%s')", $view->vid, $i, $sort['field'], $sort['sortorder'], $sort['options']);
833
834
  }

merlinofchaos's avatar
merlinofchaos committed
835
  foreach ($view->argument as $i => $arg) {
836
    db_query("INSERT INTO {view_argument} (vid, type, argdefault, title, options, position, wildcard, wildcard_substitution) VALUES (%d, '%s', %d, '%s', '%s', %d, '%s', '%s')", $view->vid, $arg['type'], $arg['argdefault'], $arg['title'], $arg['options'], $i, $arg['wildcard'], $arg['wildcard_substitution']);
837
838
  }

merlinofchaos's avatar
merlinofchaos committed
839
  foreach ($view->field as $i => $arg) {
840
    db_query("INSERT INTO {view_tablefield} (vid, tablename, field, label, handler, sortable, defaultsort, options, position) VALUES (%d, '%s', '%s', '%s', '%s', %d, '%s', '%s', %d)", $view->vid, $arg['tablename'], $arg['field'], $arg['label'], $arg['handler'], $arg['sortable'], $arg['defaultsort'], $arg['options'], $i);
841
842
  }

merlinofchaos's avatar
merlinofchaos committed
843
  foreach ($view->filter as $i => $arg) {
844
    if (is_array($arg['value'])) {
merlinofchaos's avatar
merlinofchaos committed
845
      $arg['value'] = implode(',', $arg['value']);
846
    }
847
    db_query("INSERT INTO {view_filter} (vid, tablename, field, value, operator, options, position) VALUES (%d, '%s', '%s', '%s', '%s', '%s', %d)", $view->vid, $arg['tablename'], $arg['field'], $arg['value'], $arg['operator'], $arg['options'], $i);
848
849
  }

850
851
852
  foreach ($view->exposed_filter as $i => $arg) {
    db_query("INSERT INTO {view_exposed_filter} (vid, field, label, optional, is_default, single, operator, position) VALUES (%d, '%s', '%s', %d, %d, %d, %d, %d)", $view->vid, $arg['field'], $arg['label'], $arg['optional'], $arg['is_default'], $arg['single'], $arg['operator'], $i);
  }
853
854
855
}

// ---------------------------------------------------------------------------
856
// Helper functions to build views and view data
857

858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
/**
 * Helper function to make table creation a little easier. It adds the necessary
 * data to a $table array and returns it.
 */
function views_new_table($table_name, $provider, $left_table, $left_field, $right_field, $extra = NULL) {
  $table['name'] = $table_name;
  $table['provider'] = $provider;
  $table['join']['left']['table'] = $left_table;
  $table['join']['left']['field'] = $left_field;
  $table['join']['right']['field'] = $left_field;
  if ($extra) {
    $table['join']['extra'] = $extra;
  }
  return $table;
}

/**
 * Helper function to make table creation a little easier. It adds the necessary
 * data to the $table array.
 */
878
function views_table_add_field(&$table, $name, $label, $help, $others = array()) {
879
880
881
882
883
884
885
  views_table_add_data($table, 'fields', $name, $label, $help, $others);
}

/**
 * Helper function to make table creation a little easier. It adds the necessary
 * data to the $table array.
 */
886
function views_table_add_filter(&$table, $name, $label, $help, $others = array()) {
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
  views_table_add_data($table, 'filters', $name, $label, $help, $others);
}

/**
 * Helper function to make table creation a little easier. It adds the necessary
 * data to the $table array.
 */
function views_table_add_sort(&$table, $name, $label, $help, $others = array()) {
  views_table_add_data($table, 'sorts', $name, $label, $help, $others);
}

/**
 * Helper function to make table creation a little easier. It adds the necessary
 * data to the $table array.
 */
function views_table_add_data(&$table, $type, $name, $label, $help, $others = array()) {
  $table[$type][$name]['name'] = $label;
  $table[$type][$name]['help'] = $help;
  foreach ($others as $key => $value) {
    $table[$type][$name][$key] = $value;
  }
}

910
911
912
913
914
915
916
917
918
919
/**
 * Create a blank view.
 */
function views_create_view($name, $description, $access = array()) {
  $view = new stdClass();
  _views_check_arrays($view);

  $view->name = $name;
  $view->description = $description;
  $view->access = $access;
920

921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
  // ensure some things are numerically 0.
  $view->nodes_per_page = 0;
  $view->nodes_per_block = 0;
  return $view;
}

/**
 * Add page info to a view.
 */
function views_view_add_page(&$view, $title, $url, $type, $pager, $nodes_per_page, $header, $header_format, $breadcrumb_no_home = FALSE) {
  $view->page = TRUE;
  $view->page_title = $title;
  $view->url = $url;
  $view->page_type = $type;
  $view->use_pager = $pager;
  $view->nodes_per_page = $nodes_per_page;
  $view->page_header = $header;
  $view->page_header_format = $header_format;
  $view->breadcrumb_no_home = $breadcrumb_no_home;
}

/**
 * Add menu info to a view.
 */
function views_view_add_menu(&$view, $title, $tab, $tab_weight, $default_tab) {
  $view->menu = TRUE;
  $view->menu_title = $title;
  $view->menu_tab = $tab;
  $view->menu_tab_weight = $tab_weight;
  $view->menu_tab_default = $default_tab;
}

/**
 * Add block info to a view.
 */
function views_view_add_block(&$view, $title, $type, $nodes_per_block, $more, $use_page_header, $header = '', $header_format = 0) {
  $view->block = TRUE;
  $view->block_title = $title;
  $view->block_type = $type;
  $view->nodes_per_block = $nodes_per_block;
  $view->block_more = $more;
  $view->block_use_page_header = $use_page_header;
  $view->block_header = $header;
  $view->block_header_format = $header_format;
}

/**
 * Add field info to a view.
 */
function views_view_add_field(&$view, $table, $field, $label, $sortable = FALSE, $default_sort = 0, $handler = '') {
merlinofchaos's avatar
merlinofchaos committed
971
  $view->field[] = array(
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
    'tablename' => $table,
    'field' => $field,
    'label' => $label,
    'sortable' => $sortable,
    'defaultsort' => $default_sort,
    'handler' => $handler
  );
}

/**
 * Add argument info to a view.
 */
function views_view_add_argument(&$view, $type, $default, $title, $option = '') {
  $view->argument[] = array(
    'type' => $type,
    'argdefault' => $default,
    'title' => $title,
    'options' => $option,
  );
}

/**
 * Add filter info to a view.
 */
function views_view_add_filter(&$view, $table, $field, $operator, $value, $option) {
  $view->filter[] = array(
    'tablename' => $table,
    'field' => $field,
    'operator' => $operator,
    'value' => $value,
    'options' => $option,
  );
}

/**
 * Add exposed_filter info to a view.
 */
function views_view_add_exposed_filter(&$view, $table, $field, $optional, $is_default, $lock_operator, $single) {
  $view->exposed_filter[] = array(
    'tablename' => $table,
    'field' => $field,
    'optional' => $optional,
    'is_default' => $is_default,
    'operator' => $lock_operator,
    'single' => $single
  );
}

/**
 * Add sort info to a view.
 */
function views_view_add_sort(&$view, $table, $field, $order, $option) {
  $view->sort[] = array(
    'tablename' => $table,
    'field' => $field,
    'sortorder' => $order,
    'options' => $option
  );
}

1032
1033
1034
// ---------------------------------------------------------------------------
// Themeable and support for themeables.

1035
1036
1037
/**
 * Themeable function to handle displaying a specific field.
 */
1038
function theme_views_handle_field($fields, $field, $data) {
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
  $info = $fields[$field['fullname']];

  if ($field['handler'] && function_exists($field['handler'])) {
    return $field['handler']($info, $field, $data->$field['queryname'], $data);
  }

  if ($info['handler'] && is_string($info['handler']) && function_exists($info['handler'])) {
    return $info['handler']($info, $field, $data->$field['queryname'], $data);
  }

1049
  return check_plain($data->$field['queryname']);
1050
1051
}

1052
/**
1053
1054
 * Construct a header for a table view.
 */
1055
function _views_construct_header($view, $fields) {
1056
  foreach ($view->field as $field) {
1057
1058
1059
    $header = array();
    $info = $fields[$field['fullname']];

1060
    $header['data'] = ($field['label'] ? $field['label'] : $info['name']);
1061

1062
    if ($field['sortable']) {
1063
1064
1065
1066
1067
1068
      if (function_exists($info['sort_handler']))  {
        $header['field'] = $info['sort_handler']($field, $info);
      }
      else {
        $header['field'] = $field['fullname'];
      }
1069
1070
    }
    if ($field['defaultsort']) {
merlinofchaos's avatar
merlinofchaos committed
1071
      $header['sort'] = strtolower($field['defaultsort']);
1072
    }
1073
1074
1075

    // Add CSS id to table cell header cell.
    $header['id'] = "view-field-$field[queryname]";
merlinofchaos's avatar
merlinofchaos committed
1076
    $header['class'] = "view-cell-header";
1077
1078
1079
1080
1081
    $headers[] = $header;
  }
  return $headers;