views.module 56.5 KB
Newer Older
1
<?php
2
// $Id$
3

4
function views_init() {
5
6
7
8
9
  // hook init is called even on cached pages, but we don't want to
  // actually do anything in that case.
  if (!function_exists('drupal_get_path')) {
    return;
  }
10
11
12
13
14
15
16
17
  // Load all our module 'on behalfs'.
  $path = drupal_get_path('module', 'views') . '/modules';
  $files = system_listing('views_.*\.inc$', $path, 'name', 0);

  foreach($files as $file) {
    // The filename format is very specific. It must be views_MODULENAME.inc
    $module = substr_replace($file->name, '', 0, 6);
    if (module_exist($module)) {
18
      require_once("./$file->filename");
19
20
21
22
    }
  }
}

23
// ---------------------------------------------------------------------------
24
// Acquire Views Data
25

26
/**
27
28
29
30
 * Return the arguments array; construct one if we haven't already. The
 * array is cached in a global, safely named variable so that arguments
 * are only constructed once per run.
 */
31
function _views_get_arguments($titles = false) {
32
  static $views_arguments;
33
  global $locale;
merlinofchaos's avatar
merlinofchaos committed
34

35
  if (!$views_arguments) {
36
    $data = cache_get("views_arguments:$locale");
37
38
    $cache = unserialize($data->data);
    if (is_array($cache)) {
39
      $views_arguments = $cache;
40
41
    }
    else {
42
      $arguments = module_invoke_all('views_arguments');
43
      foreach ($arguments as $name => $arg) {
44
45
46
47
48
49
50
51
        if ($arg['option'] && !is_array($arg['option'])) {
          if ($arg['option'] == 'string' || $arg['option'] == 'integer') {
            $arg['option'] = array('#type' => 'textfield', '#size' => 10, '#maxlength' => 255);
          }
          else {
            $arg['option'] = array('#type' => 'select', '#options' => $arg['option']);
          }
        }
52
53
        $views_arguments['base'][$name] = $arg['name'];
        $views_arguments['title'][$name] = $arg;
54
      }
55
      $cache = $views_arguments;
56
      cache_set("views_arguments:$locale", serialize($cache));
merlinofchaos's avatar
merlinofchaos committed
57
58
    }
  }
59
  return ($titles ? $views_arguments['base'] : $views_arguments['title']);
merlinofchaos's avatar
merlinofchaos committed
60
}
61

62
/**
63
64
65
 * Constructs the full table information array. Caches it into a global array
 * so that it will only be called once per run.
 */
66
function _views_get_tables($full = false) {
67
  static $views_tables;
68
  global $locale;
merlinofchaos's avatar
merlinofchaos committed
69

70
  if (!$views_tables) {
71
    $data = cache_get("views_tables:$locale");
72
    $cache = unserialize($data->data);
merlinofchaos's avatar
merlinofchaos committed
73

74
    if (is_array($cache)) {
75
      $views_tables = $cache;
merlinofchaos's avatar
merlinofchaos committed
76
    }
77
    else {
78
79
      $table_data = module_invoke_all('views_tables');
      $views_tables['tables'] = $table_data;
80
81
82

      foreach ($table_data as $name => $table) {
        if (is_array($table['filters'])) {
83
          foreach ($table['filters'] as $filter => $data) {
84
            $data['table'] = $name;
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
            // translate for deprecated APIs...
            if ($data['option'] && !is_array($data['option'])) {
              if ($data['option'] == 'string' || $data['option'] == 'integer') {
                $data['option'] = array('#type' => 'textfield', '#size' => 10, '#maxlength' => 255);
              }
              else {
                $data['option'] = array('#type' => 'select', '#options' => $data['option']);
              }
            }
            if ($data['list']) {
              $data['value'] = array('#type' => 'select', '#options' => $data['list']);
              if ($data['list-type'] != 'select') {
                $data['value']['#multiple'] = TRUE;
              }
            }
            else if (!$data['value']) {
              $data['value'] = array('#type' => 'textfield', '#size' => 10, '#maxlength' => 255);
            }
103
104
            $views_tables['filters']['titles']["$name.$filter"] = $data['name'];
            $views_tables['filters']['base']["$name.$filter"] = $data;
105
106
107
          }
        }
        if (is_array($table['fields'])) {
108
          foreach ($table['fields'] as $field => $data) {
109
110
111
112
113
114
115
116
            if ($data['option'] && !is_array($data['option'])) {
              if ($data['option'] == 'string' || $data['option'] == 'integer') {
                $data['option'] = array('#type' => 'textfield', '#size' => 10, '#maxlength' => 255);
              }
              else {
                $data['option'] = array('#type' => 'select', '#options' => $data['option']);
              }
            }
117
            $data['table'] = $name;
118
119
            $views_tables['fields']['titles']["$name.$field"] = $data['name'];
            $views_tables['fields']['base']["$name.$field"] = $data;
120
121
122
          }
        }
        if (is_array($table['sorts'])) {
123
          foreach ($table['sorts'] as $field => $data) {
124
            $data['table'] = $name;
125
126
127
128
129
130
131
132
            if ($data['option'] && !is_array($data['option'])) {
              if ($data['option'] == 'string' || $data['option'] == 'integer') {
                $data['option'] = array('#type' => 'textfield', '#size' => 10, '#maxlength' => 255);
              }
              else {
                $data['option'] = array('#type' => 'select', '#options' => $data['option']);
              }
            }
133
134
            $views_tables['sorts']['titles']["$name.$field"] = $data['name'];
            $views_tables['sorts']['base']["$name.$field"] = $data;
135
136
          }
        }
merlinofchaos's avatar
merlinofchaos committed
137
      }
138
      cache_set("views_tables:$locale", serialize($views_tables));
merlinofchaos's avatar
merlinofchaos committed
139
140
    }
  }
141
  return ($full ? $views_tables : $views_tables['tables']);
merlinofchaos's avatar
merlinofchaos committed
142
143
}

144
/**
145
146
147
 * Gets the filter information; if it doesn't exist, call the function
 * that constructs all that.
 */
merlinofchaos's avatar
merlinofchaos committed
148
function _views_get_filters($titles = false) {
149
150
  $table_data = _views_get_tables(true);
  return ($titles ? $table_data['filters']['titles'] : $table_data['filters']['base']);
merlinofchaos's avatar
merlinofchaos committed
151
152
}

153
/**
154
155
156
 * Gets the field information; if it doesn't exist, call the function
 * that constructs all that.
 */
merlinofchaos's avatar
merlinofchaos committed
157
function _views_get_fields($titles = false) {
158
159
  $table_data = _views_get_tables(true);
  return ($titles ? $table_data['fields']['titles'] : $table_data['fields']['base']);
merlinofchaos's avatar
merlinofchaos committed
160
161
}

162
/**
163
164
165
 * Gets the sort information; if it doesn't exist, call the function
 * that constructs all that.
 */
merlinofchaos's avatar
merlinofchaos committed
166
function _views_get_sorts($titles = false) {
167
168
169
  $table_data = _views_get_tables(true);
  return ($titles ? $table_data['sorts']['titles'] : $table_data['sorts']['base']);
}
merlinofchaos's avatar
merlinofchaos committed
170

171
/**
172
173
174
175
 * Invalidate the views cache, forcing a rebuild on the next grab of table data.
 */
function views_invalidate_cache() {
  cache_clear_all('views_', true);
merlinofchaos's avatar
merlinofchaos committed
176
177
}

178
/**
179
180
181
182
 * Ensures that views have legitimate information; a bit more is stored on
 * the $view object than is in the database, and this isn't necessarily
 * set when a view is constructed externally.
 */
183
184
function _views_sanitize_view(&$view) {
  _views_check_arrays($view); // so reference works.
185
186
187
  foreach ($view->field as $i => $field) {
    $view->field[$i]['id'] = $view->field[$i]['fullname'] = "$field[tablename].$field[field]";
    $view->field[$i]['queryname'] = "$field[tablename]_$field[field]";
188
  }
189
190
191

  foreach ($view->filter as $i => $filter) {
    $view->filter[$i]['id'] = $view->filter[$i]['field'] = "$filter[tablename].$filter[field]";
192
  }
193
194
195

  foreach ($view->exposed_filter as $i => $exposed_filter) {
    $view->exposed_filter[$i]['id'] = $view->exposed_filter[$i]['field'] = "$exposed_filter[tablename].$exposed_filter[field]";
196
  }
197
198
199
200
201
202
203
204

  foreach ($view->sort as $i => $sort) {
    $view->sort[$i]['id'] = $view->sort[$i]['field'] = "$sort[tablename].$sort[field]";
  }

  foreach ($view->argument as $i => $argument) {
    $view->argument[$i]['id'] = $view->argument[$i]['type'];
  }
205
206
}

207
/**
208
209
 * Build default view information from all modules and cache it.
 */
merlinofchaos's avatar
merlinofchaos committed
210
function _views_get_default_views() {
211
  static $views_default_views;
212
  global $locale;
merlinofchaos's avatar
merlinofchaos committed
213

214
  if (!$views_default_views) {
215
    $data = cache_get("views_default_views:$locale");
216
217
218
    $cache = unserialize($data->data);

    if (is_array($cache)) {
219
      $views_default_views = $cache;
220
221
    }
    else {
222
223
      // We have to make sure table data is built in order to be sure about providers.
      $tables = array_keys(_views_get_tables());
224

225
226
      $views = module_invoke_all('views_default_views');
      $views_default_views = array();
227
228
      foreach ($views as $i => $view) {
        if (!is_array($view->requires) || !array_diff($view->requires, $tables)) {
merlinofchaos's avatar
merlinofchaos committed
229
          _views_sanitize_view($view);
230
          $views_default_views[$i] = $view;
231
        }
232
      }
233
      cache_set("views_default_views:$locale", serialize($views_default_views));
merlinofchaos's avatar
merlinofchaos committed
234
235
    }
  }
236
  return $views_default_views;
merlinofchaos's avatar
merlinofchaos committed
237
}
238

239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/**
 * Return the style plugins; construct one if we haven't already. The
 * array is cached in a static variable so that arguments
 * are only constructed once per run.
 */
function _views_get_style_plugins($titles = false) {
  static $views_style_plugins;
  global $locale;

  if (!$views_style_plugins) {
    $data = cache_get("views_style_plugins:$locale");
    $cache = unserialize($data->data);
    if (is_array($cache)) {
      $views_style_plugins = $cache;
    }
    else {
      $arguments = module_invoke_all('views_style_plugins');
      foreach ($arguments as $name => $arg) {
        $views_style_plugins['title'][$name] = $arg['name'];
        $views_style_plugins['base'][$name] = $arg;
      }
      $cache = $views_style_plugins;
      cache_set("views_style_plugins:$locale", serialize($cache));
    }
  }
  return ($titles ? $views_style_plugins['title'] : $views_style_plugins['base']);
}

267
268
269
// ---------------------------------------------------------------------------
// Drupal Hooks

270
/**
271
272
 * Implementation of hook_help()
 */
273
274
function views_help($section) {
  switch ($section) {
275
    case 'admin/help#views':
276
    case 'admin/modules#description':
277
      return t('The views module creates customized views of node lists.');
278
  }
279
280
}

281
/**
282
283
 * Implementation of hook_menu()
 */
284
285
function views_menu($may_cache) {
  $items = array();
286
  global $locale;
287
288

  if ($may_cache) {
289
290
    // Invalidate the views cache to ensure that views data gets rebuilt.
    // This is the best way to tell that module configuration has changed.
291
    if (arg(0) == 'admin' && arg(1) == 'modules') {
292
293
      views_invalidate_cache();
    }
294

295
    $result = db_query("SELECT * FROM {view_view} WHERE page = 1");
296
297
    $views_with_inline_args = array();

298
    while ($view = db_fetch_object($result)) {
299
300
      // unpack the array
      $view->access = ($view->access ? explode(', ', $view->access) : array());
301
302
303
304

      // 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
305

306
      if (strrpos($view->url, '$arg')) {
307
308
309
        $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.
310
        $views_with_inline_args[$view->name] = $view;
311
312
        continue;
      }
313
      _views_create_menu_item($items, $view, $view->url, array($view->name));
314
    }
315
    $default_views = _views_get_default_views();
316
317
    $views_status = variable_get('views_defaults', array());

318
    foreach ($default_views as $name => $view) {
merlinofchaos's avatar
merlinofchaos committed
319
      if ($view->page && !$used[$name] &&
320
       ($views_status[$name] == 'enabled' || (!$view->disabled && $views_status[$name] != 'disabled'))) {
321

322
        if (strrpos($view->url, '$arg')) {
323
          $views_with_inline_args[$view->name] = $view;
324
325
          continue;
        }
326
327

        _views_create_menu_item($items, $view, $view->url, array($view->name));
328
329
      }
    }
330
    cache_set("views_with_inline_args:$locale", serialize($views_with_inline_args));
331
332
  }
  else {
333
    $data = cache_get("views_with_inline_args:$locale");
334
335
336
337
    $views = unserialize($data->data);
    if (is_array($views)) {
      foreach ($views as $view) {
        // Do substitution on args.
338
339
        $view_args = array($view->name);
        $menu_path = array();
340
341
342
343
        foreach (explode('/', $view->url) as $num => $element) {
          if ($element == '$arg') {
            $menu_path[] = arg($num);
            $view_args[] = arg($num);
344
            $view->args[] = arg($num);
345
346
          }
          else {
347
            $menu_path[] = $element;
348
349
350
          }
        }
        $path = implode('/', $menu_path);
351
        _views_create_menu_item($items, $view, $path, $view_args, MENU_CALLBACK);
352
      }
353
    }
354
355
356
357
  }
  return $items;
}

358
359
360
/**
 * Helper function to add a menu item for a view.
 */
361
function _views_create_menu_item(&$items, $view, $path, $args, $local_task_type = MENU_NORMAL_ITEM) {
362
363
364
365
366
367
368
  static $roles = NULL;
  if ($roles == NULL) {
    global $user;
    $roles = array_keys($user->roles);
  }
  $title = views_get_title($view, 'menu');
  $type = _views_menu_type($view);
369
370
371
  if ($type == MENU_LOCAL_TASK || $type == MENU_DEFAULT_LOCAL_TASK) {
    $weight = $view->menu_tab_weight;
  }
372
  $access = !$view->access || array_intersect($view->access, $roles);
373
  $items[] = _views_menu_item($path, $title, $args, $access, $type, $weight);
merlinofchaos's avatar
merlinofchaos committed
374

375
  if ($type == MENU_DEFAULT_LOCAL_TASK) {
376
    $items[] = _views_menu_item(dirname($path), $title, $args, $access, $local_task_type, $weight);
377
378
379
  }
}

380
381
382
/**
 * Helper function to create a menu item for a view.
 */
383
384
function _views_menu_item($path, $title, $args, $access, $type, $weight = NULL) {
  $retval = array('path' => $path,
385
386
387
388
389
390
    'title' => $title,
    'callback' => 'views_view_page',
    'callback arguments' => $args,
    'access' => $access,
    'type' => $type,
  );
391
392
393
394
  if ($weight !== NULL) {
    $retval['weight'] = $weight;
  }
  return $retval;
395
396
}

397
398
399
/**
 * Determine what menu type a view needs to use.
 */
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
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;
}
417

418
/**
419
420
 * Implementation of hook_block()
 */
421
422
423
function views_block($op = 'list', $delta = 0) {
  $block = array();
  if ($op == 'list') {
424
    // Grab views from the database and provide them as blocks.
425
    $result = db_query("SELECT vid, block_title, page_title, name FROM {view_view} WHERE block = 1");
426
    while ($view = db_fetch_object($result)) {
427
      $block[$view->name]['info'] = views_get_title($view, 'block-info');
428
    }
429

430
    $default_views = _views_get_default_views();
431
432
    $views_status = variable_get('views_defaults', array());

433
    foreach ($default_views as $name => $view) {
434
435
436
      if (!isset($block[$name]) && $view->block &&
        ($views_status[$name] == 'enabled' || (!$view->disabled && $views_status[$name] != 'disabled'))) {
        $block[$name]['info'] = views_get_title($view, 'block');
437
      }
438
439
440
441
442
443
444
445
    }
    return $block;
  }
  else if ($op == 'view') {
    return views_view_block($delta);
  }
}

446
447
448
// ---------------------------------------------------------------------------
// View Construction

449
/**
450
451
452
 * 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
453
function _views_check_arrays(&$view) {
merlinofchaos's avatar
merlinofchaos committed
454
  $fields = array('field', 'sort', 'argument', 'filter', 'exposed_filter', 'access');
455
456
457
458
459

  foreach($fields as $field) {
    if (!is_array($view->$field)) {
      $view->$field = array();
    }
460
461
  }
  return $view;
462
463
}

464
/**
465
466
467
 * This function loads a view by name or vid; if not found in db, it looks
 * for a default view by that name.
 */
468
469
function views_get_view($view_name) {
  $view = _views_load_view($view_name);
470
  if ($view) {
merlinofchaos's avatar
merlinofchaos committed
471
    return $view;
472
  }
merlinofchaos's avatar
merlinofchaos committed
473

474
475
  if (is_int($view_name)) {
    return; // don't bother looking if view_name is an int!
476
  }
merlinofchaos's avatar
merlinofchaos committed
477

478
  $default_views = _views_get_default_views();
merlinofchaos's avatar
merlinofchaos committed
479

480
481
  if (isset($default_views[$view_name])) {
    return $default_views[$view_name];
482
  }
merlinofchaos's avatar
merlinofchaos committed
483
484
}

485
/**
486
487
 * This views a view by page, and should only be used as a callback.
 */
488
489
function views_view_page() {
  $args = func_get_args();
490
491
  // FIXME: Most of this code is unnecessary now that we add our
  // $view info as a callback argument via the menu hook.
492
  while ($next = array_shift($args)) {
493
494
    if (!$view_name) {
      $view_name = $next;
495
496
    }
    else {
497
      $view_name .= "/$next";
498
499
    }

500
    if ($view = views_get_view($view_name)) {
501
      break;
502
    }
503
  }
504
505
506
507
  if (!$view) {
    drupal_not_found();
    exit;
  }
508

509
  $output = views_build_view('page', $view, $args, $view->use_pager, $view->nodes_per_page);
510
  if ($output === FALSE) {
511
512
513
514
    drupal_not_found();
    exit;
  }

515
  return $output;
516
517
}

518
/**
519
520
 * This views a view by block. Can be used as a callback or programmatically.
 */
521
function views_view_block($vid) {
522
  $view = views_get_view($vid);
523

524
  if (!$view || !$view->block) {
525
    return NULL;
526
  }
527

528
  global $user;
529
530
531
532
  if (!$user->roles) {
    return NULL;
  }

533
534
535
536
537
  $roles = array_keys($user->roles);
  if ($view->access && !array_intersect($roles, $view->access)) {
    return NULL;
  }

538
  $content = views_build_view('block', $view, array(), false, $view->nodes_per_block);
539
540
541
542
543
544
545
546
  if ($content) {
    $block['content'] = $content;
    $block['subject'] = views_get_title($view, 'block');
    return $block;
  }
  else {
    return NULL;
  }
547
548
}

549
/**
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
 * 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
574
575
 *   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.
576
577
578
579
580
581
582
 * @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!
583
584
585
586
 * @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.
587
*/
588
function views_build_view($type, &$view, $args = array(), $use_pager = false, $limit = 0, $page = 0) {
589
590
591
592
593
  // Fix a number of annoying whines when NULL is passed in..
  if ($args == NULL) {
    $args = array();
  }

594
  $GLOBALS['current_view'] = &$view;
merlinofchaos's avatar
merlinofchaos committed
595

596
597
  $view->build_type = $type;
  $view->type = ($type == 'block' ? $view->block_type : $view->page_type);
598

599
600
  if ($view->view_args_php) {
    ob_start();
601
602
603
604
    $result = eval($view->view_args_php);
    if (is_array($result)) {
      $args = $result;
    }
605
606
607
    ob_end_clean();
  }

608
  $plugins = _views_get_style_plugins();
609
610
611
  if ($view->query) {
    $info['query'] = $view->query;
    $info['countquery'] = $view->countquery;
merlinofchaos's avatar
merlinofchaos committed
612

613
    if ($plugins[$view->type]['needs_table_header']) {
merlinofchaos's avatar
merlinofchaos committed
614
      $view->table_header = _views_construct_header($view, $fields);
615
    }
merlinofchaos's avatar
merlinofchaos committed
616
617
  }
  else {
618
619
620
    $path = drupal_get_path('module', 'views');
    require_once("$path/views_query.inc");

merlinofchaos's avatar
merlinofchaos committed
621
    $info = _views_build_query($view, $args);
622
    if ($info['fail']) {
623
      return FALSE;
624
    }
merlinofchaos's avatar
merlinofchaos committed
625
626
  }

627
  // Run-time replacement so we can do cacheing
628
629
630
631
  $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']);
632

633
634
635
636
    if (is_array($info['args'])) {
      foreach ($info['args'] as $id => $arg) {
        $info['args'][$id] = str_replace($src, $dest, $arg);
      }
637
    }
638
  }
639

640
641
  $query = db_rewrite_sql($info['query'], 'node');

642
  if ($use_pager) {
643
    $cquery = db_rewrite_sql($info['countquery'], 'node', 'nid', $info['rewrite_args']);
644
    $result = pager_query($query, $limit, $use_pager - 1, $cquery, $info['args']);
645
646
  }
  else {
647
    $result = ($limit ? db_query_range($query, $info['args'], $page * $limit, $limit) : db_query($query, $info['args']));
648
649
650
651
  }

  if ($type == 'result') {
    $info['result'] = $result;
merlinofchaos's avatar
merlinofchaos committed
652
    return $info;
653
654
  }

merlinofchaos's avatar
merlinofchaos committed
655
  $items = array();
656
657
658
  while ($item = db_fetch_object($result)) {
    $items[] = $item;
  }
659
660
661
662
663

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

665
666
667
  // 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';
668
    $output .= $function($view, $items);
669
670
  }

671
672
  $view->real_url = views_get_url($view, $args);

673
  $view->use_pager = $use_pager;
674
  $view->pager_limit = $limit;
675
  $output .= views_theme('views_view', $view, $type, $items, $info['level'], $args);
merlinofchaos's avatar
merlinofchaos committed
676

677
678
679
  // 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';
680
    $output .= $function($view, $items, $output);
681
682
  }

merlinofchaos's avatar
merlinofchaos committed
683
  return $output;
684
685
}

686
687
688
// ---------------------------------------------------------------------------
// Utility

merlinofchaos's avatar
merlinofchaos committed
689
/**
690
 * Easily theme any item to a view.
merlinofchaos's avatar
merlinofchaos committed
691
 * @param $function
692
693
694
695
696
697
698
 *   The name of the function to call.
 * @param $view
 *   The view being themed.
 */
function views_theme() {
  $args = func_get_args();
  $function = array_shift($args);
699
  $view = $args[0];
700
701
702
703
704
705
706
707
708
709

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

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

710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
/**
 * 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);

  if (!($func = theme_get_function($function . "_" . $field_name))) {
    $func = theme_get_function($function);
  }

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

734
/**
735
 * Figure out what timezone we're in; needed for some date manipulations.
736
 */
737
738
739
740
741
742
743
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
744
745
  }

746
747
748
749
  // 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
750
      if ($GLOBALS['db_type'] == 'mysqli' || version_compare(mysql_get_server_info(), '4.1.3', '>=')) {
751
752
753
754
755
756
        db_query("SET @@session.time_zone = '+00:00'");
      } 
      $already_set = true;
    }
  }

757
  return $timezone;
758
}
759
760
761
762
763
764
765

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

766
767
768
769
770
  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.
771
      if ($where && $where = strpos($url, '$arg')) {
772
773
774
775
776
        $url = substr_replace($url, $arg, $where, 4);
      }
      else {
        $url .= "/$arg";
      }
777
778
779
780
781
782
    }
  }

  return $url;
}

783
/**
784
785
 * Figure out what the title of a view should be.
 */
786
787
788
789
function views_get_title($view, $context = 'menu', $args = NULL) {
  if ($context == 'menu' && $view->menu_title)
    return $view->menu_title;

790
791
792
793
  if ($context == 'block-info') {
    return $view->description ? $view->description : $view->name;
  }

794
  if ($args === NULL) {
795
    $args = $view->args;
796
  }
797
798
799
800
801
802
803
804
805
806
  // 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;
      }
    }
807
  }
808

809
810
811
812
  if (!$title && $context == 'menu') {
    $title = $view->block_title;
  }

813
  if (!$title) {
814
815
816
817
818
819
    if ($context == 'block' && $view->block_title) {
      $title = $view->block_title;
    }
    else {
      $title = $view->page_title;
    }
820
821
  }

822
823
824
825
  if (!$view->argument) {
    return $title;
  }

826
827
  $arginfo = _views_get_arguments();
  foreach ($view->argument as $i => $arg) {
828
829
830
    if (!isset($args[$i])) {
      break;
    }
831
    $argtype = $arg['type'];
832
833
834
835
    if ($arg['wildcard'] == $args[$i] && $arg['wildcard_substitution'] != '') {
      $title = str_replace("%" . ($i + 1), $arg['wildcard_substitution'], $title);
    }
    else if (function_exists($arginfo[$argtype]['handler'])) {
836
      // call the handler
837
      $rep = $arginfo[$argtype]['handler']('title', $args[$i], $argtype);
838
839
840
841
842
843
      $title = str_replace("%" . ($i + 1), $rep, $title);
    }
  }
  return $title;
}

844
/**
845
846
 * 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
847
 * that need to restrict to the 'current' user, or any views that require
848
849
 * arguments or allow click-sorting are not cacheable.
 */
merlinofchaos's avatar
merlinofchaos committed
850
851
function _views_is_cacheable(&$view) {
  // views with arguments are immediately not cacheable.
852
  if (!empty($view->argument) || !empty($view->exposed_filter)) {
merlinofchaos's avatar
merlinofchaos committed
853
    return false;
854
  }
merlinofchaos's avatar
merlinofchaos committed
855
856
857
858

  $filters = _views_get_filters();

  foreach ($view->filter as $i => $filter) {
859
    if ($filters[$filter['field']]['cacheable'] == 'no')  {
merlinofchaos's avatar
merlinofchaos committed
860
      return false;
861
    }
merlinofchaos's avatar
merlinofchaos committed
862
  }
863

864
  foreach ($view->field as $i => $field) {
865
    if ($field['sortable']) {
866
      return false;
867
868
    }
  }
merlinofchaos's avatar
merlinofchaos committed
869
870
871
  return true;
}

872
873
874
// ---------------------------------------------------------------------------
// Database functions

875
/**
876
877
 * Provide all the fields in a view.
 */
878
function _views_view_fields() {
879
  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');
880
881
}

882
/**
883
884
 * Delete a view from the database.
 */
885
886
function _views_delete_view($view) {
  $view->vid = intval($view->vid);
887
  if (!$view->vid) {
888
    return;
889
  }
890

merlinofchaos's avatar
merlinofchaos committed
891
892
893
894
  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);
895
896
}

897
898
899
900
901
902
903
/**
 * Load a view from the database -- public version of the function.
 */
function views_load_view($arg) {
  return _views_load_view($arg);
}

904
/**
905
 * Load a view from the database.
906
 * (deprecated; use views_load_view in favor of this function).
907
 */
908
function _views_load_view($arg) {
909
910
911
912
913
  static $cache = array();
  $which = is_numeric($arg) ? 'vid' : 'name';
  if (isset($cache[$which][$arg])) {
    return $cache[$which][$arg];
  }
914

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

918
  if (!$view->name) {
919
    return NULL;
920
  }
921

922
923
  $view->access = ($view->access ? explode(', ', $view->access) : array());

924
925
926
  // 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
927
928
  $view->sort = array();
  while ($sort = db_fetch_array($result)) {
929
930
931
    if (substr($sort['field'], 0, 2) == 'n.') {
      $sort['field'] = 'node' . substr($sort['field'], 1);
    }
932
    $sort['id'] = $sort['field'];
merlinofchaos's avatar
merlinofchaos committed
933
    $view->sort[] = $sort;
934
935
  }

936
  $result = db_query("SELECT * FROM {view_argument} WHERE vid = $view->vid ORDER BY position ASC");
937
938
939

  $view->argument = array();
  while ($arg = db_fetch_array($result)) {
940
    $arg['id'] = $arg['type'];
941
942
943
    $view->argument[] = $arg;
  }

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

946
  $view->field = array();
947
  while ($arg = db_fetch_array($result)) {
948
949
950
    if ($arg['tablename'] == 'n') {
      $arg['tablename'] = 'node';
    }
951
    $arg['id'] = $arg['fullname'] = "$arg[tablename].$arg[field]";
952
953
    $arg['queryname'] = "$arg[tablename]_$arg[field]";
    $view->field[] = $arg;
954
955
  }

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

958
  $filters = _views_get_filters();
959
  $view->filter = array();
960
961
962
  while ($filter = db_fetch_array($result)) {
    if (substr($filter['field'], 0, 2) == 'n.') {
      $filter['field'] = 'node' . substr($filter['field'], 1);
963
    }
964

merlinofchaos's avatar
merlinofchaos committed
965
966
967
    if ($filter['operator'] == 'AND' ||
        $filter['operator'] == 'OR' ||
        $filter['operator'] == 'NOR' ||
968
969
970
971
972
973
974
        $filters[$filter['field']]['value-type'] == 'array' ) {
      if ($filter['value'] !== NULL && $filter['value'] !== '') {
        $filter['value'] = explode(',', $filter['value']);
      }
      else {
        $filter['value'] = array();
      }
975
    }
976
    $filter['id'] = $filter['field'];
977
    $view->filter[] = $filter;
978
979
  }