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
288
289
290
291
292
293
294
295
296
297
298
299
300
301
function &views_set_current_view($view = NULL) {
  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() {
  return views_set_current_view();
}

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
 * 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
327
328
 *   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.
329
330
331
332
333
334
335
 * @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!
336
337
338
339
 * @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.
340
*/
341
function views_build_view($type, &$view, $args = array(), $use_pager = false, $limit = 0, $page = 0) {
342
343
  views_load_cache();

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

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

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

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

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

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

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

381
  // Run-time replacement so we can do cacheing
382
383
384
385
  $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']);
386

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

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

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

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

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

420
421
422
  // 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';
423
    $output .= $function($view, $items);
424
425
  }

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

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

432
433
434
  // 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';
435
    $output .= $function($view, $items, $output);
436
437
  }

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

441
442
443
// ---------------------------------------------------------------------------
// Utility

444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
/**
 * 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
460
/**
461
 * Easily theme any item to a view.
merlinofchaos's avatar
merlinofchaos committed
462
 * @param $function
463
464
465
466
467
468
469
 *   The name of the function to call.
 * @param $view
 *   The view being themed.
 */
function views_theme() {
  $args = func_get_args();
  $function = array_shift($args);
470
  $view = $args[0];
471
472
473
474
475
476
477
478
479
480

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

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

481
482
483
484
485
486
487
488
489
490
491
492
493
494
/**
 * 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);
495
  $view = array_pop($args);
496

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

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

508
/**
509
 * Figure out what timezone we're in; needed for some date manipulations.
510
 */
511
512
513
514
515
516
517
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
518
519
  }

520
521
522
523
  // 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
524
      if ($GLOBALS['db_type'] == 'mysqli' || version_compare(mysql_get_server_info(), '4.1.3', '>=')) {
525
526
527
528
529
530
        db_query("SET @@session.time_zone = '+00:00'");
      } 
      $already_set = true;
    }
  }

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

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

540
541
542
543
544
  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.
545
      if ($where && $where = strpos($url, '$arg')) {
546
547
548
549
550
        $url = substr_replace($url, $arg, $where, 4);
      }
      else {
        $url .= "/$arg";
      }
551
552
553
554
555
556
    }
  }

  return $url;
}

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

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

568
  if ($args === NULL) {
569
    $args = $view->args;
570
  }
571
572
573
574
575
576
577
578
579
580
  // 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;
      }
    }
581
  }
582

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

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

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

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

613
/**
614
615
 * 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
616
 * that need to restrict to the 'current' user, or any views that require
617
618
 * arguments or allow click-sorting are not cacheable.
 */
merlinofchaos's avatar
merlinofchaos committed
619
620
function _views_is_cacheable(&$view) {
  // views with arguments are immediately not cacheable.
621
  if (!empty($view->argument) || !empty($view->exposed_filter) || !empty($view->no_cache)) {
merlinofchaos's avatar
merlinofchaos committed
622
    return false;
623
  }
merlinofchaos's avatar
merlinofchaos committed
624
625
626
627

  $filters = _views_get_filters();

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

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

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

648
649
650
// ---------------------------------------------------------------------------
// Database functions

651
/**
652
653
 * Provide all the fields in a view.
 */
654
function _views_view_fields() {
655
  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');
656
657
}

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

merlinofchaos's avatar
merlinofchaos committed
667
668
669
670
  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);
671
672
}

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

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

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

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

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

700
701
702
  // 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
703
704
  $view->sort = array();
  while ($sort = db_fetch_array($result)) {
705
706
707
    if (substr($sort['field'], 0, 2) == 'n.') {
      $sort['field'] = 'node' . substr($sort['field'], 1);
    }
708
    $sort['id'] = $sort['field'];
merlinofchaos's avatar
merlinofchaos committed
709
    $view->sort[] = $sort;
710
711
  }

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

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

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

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

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

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

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

756
757
758
759
760
761
762
763
  $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;
  }

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

767
768
769
  return $view;
}

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

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

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

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

merlinofchaos's avatar
merlinofchaos committed
791
  $view->changed = time();
792
793
794
795
796
797
798
799
800
801
802
803
  $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:
804
    db_query("UPDATE {view_view} SET " . implode(', ', $q) . " WHERE vid = '$view->vid'", $v);
805
806
807
808
    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'");
809
    db_query("DELETE from {view_exposed_filter} WHERE vid='$view->vid'");
810
811
812
813
814
815
816
817
818
819
820
821
822
  }
  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;
823
        $s[] = is_numeric($value) ? '%d' : "'%s'";
824
825
826
      }
    }

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

merlinofchaos's avatar
merlinofchaos committed
830
  foreach ($view->sort as $i => $sort) {
831
    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']);
832
833
  }

merlinofchaos's avatar
merlinofchaos committed
834
  foreach ($view->argument as $i => $arg) {
835
    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']);
836
837
  }

merlinofchaos's avatar
merlinofchaos committed
838
  foreach ($view->field as $i => $arg) {
839
    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);
840
841
  }

merlinofchaos's avatar
merlinofchaos committed
842
  foreach ($view->filter as $i => $arg) {
843
    if (is_array($arg['value'])) {
merlinofchaos's avatar
merlinofchaos committed
844
      $arg['value'] = implode(',', $arg['value']);
845
    }
846
    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);
847
848
  }

849
850
851
  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);
  }
852
853
854
}

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

857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
/**
 * 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.
 */
877
function views_table_add_field(&$table, $name, $label, $help, $others = array()) {
878
879
880
881
882
883
884
  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.
 */
885
function views_table_add_filter(&$table, $name, $label, $help, $others = array()) {
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
  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;
  }
}

909
910
911
912
913
914
915
916
917
918
/**
 * 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;
919

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
  // 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
970
  $view->field[] = array(
971
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
    '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
  );
}

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

1034
1035
1036
/**
 * Themeable function to handle displaying a specific field.
 */
1037
function theme_views_handle_field($fields, $field, $data) {
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
  $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);
  }

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

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

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

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

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

1081
function theme_views_display_filters($view) {
1082
1083
1084
1085
1086
  $form = views_filters_form($view);
  return drupal_get_form("views_filters_