/[drupal]/contributions/modules/views_fusion/views_fusion.module
ViewVC logotype

Contents of /contributions/modules/views_fusion/views_fusion.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.14 - (show annotations) (download) (as text)
Sat May 12 13:34:46 2007 UTC (2 years, 6 months ago) by fago
Branch: MAIN
CVS Tags: HEAD
Changes since 1.13: +2 -2 lines
File MIME type: text/x-php
#140699: fixed external filters won't filter child view in fused view for views 1.6
1 <?php
2 // $Id: views_fusion.module,v 1.13 2007/04/07 13:55:08 fago Exp $
3
4 /**
5 * @file
6 * Provides fusing of multiple views.
7 */
8
9 /**
10 * Implementation of hook_help().
11 */
12 function views_fusion_help($section) {
13 switch ($section) {
14 case 'admin/build/views/fusion':
15 return '<p>'. t('This screen allows the fusion of two views. Select the main view you want to use and then select a second view you would like to "fuse" with the first view. Fields, filters and sorts from the fused view will be intelligently merged into the primary view.') .'</p>';
16 }
17 }
18
19 /**
20 * Implementation of hook_menu().
21 */
22 function views_fusion_menu($may_cache) {
23 $items = array();
24
25 if ($may_cache) {
26 $items[] = array(
27 'path' => 'admin/build/views/fusion',
28 'title' => t('Fusion'),
29 'callback' => 'drupal_get_form',
30 'callback arguments' => array('views_fusion_admin'),
31 'access' => user_access('administer views'),
32 'type' => MENU_LOCAL_TASK,
33 );
34 }
35 return $items;
36 }
37
38
39 function views_fusion_admin() {
40
41 $tables = _views_fusion_get_tables();
42
43 if (!$tables['fields']) {
44 return array('msg' => array('#value' => t('If you want to use views fusion you have to activate an appropriate node relation module, e.g. the nodefamily module.')));
45 }
46 $form = array();
47 $form['overview'] = array('#type' => 'markup', '#value' => views_fusion_overview());
48
49 $form['add'] = array('#type' => 'fieldset', '#title' => t('Add another views fusion'));
50 $form['add']['vid'] = array(
51 '#type' => 'select',
52 '#title' => t('primary view'),
53 '#options' => _views_fusion_get_views_list(),
54 '#required' => true,
55 );
56 $form['add']['mvid'] = array(
57 '#type' => 'select',
58 '#title' => t('fuse with view'),
59 '#options' => _views_fusion_get_views_list(),
60 '#required' => true,
61 );
62 $form['add']['uses'] = array(
63 '#type' => 'select',
64 '#title' => t('using'),
65 '#options' => $tables['titles'],
66 '#required' => true,
67 );
68 $form['add']['submit'] = array(
69 '#type' => 'submit',
70 '#value' => t('Add fusion'),
71 '#weight' => 10,
72 );
73
74 return $form;
75 }
76
77
78 function _views_fusion_get_views_list() {
79
80 $result = db_query("SELECT vid, name FROM {view_view} ORDER BY name");
81
82 $views = array();
83 while ($row = db_fetch_object($result)) {
84 $views[$row->vid] = $row->name;
85 }
86 return $views;
87 }
88
89 /*
90 * Return an array of all tables that can be used for fusing views.
91 *
92 * To get their tables listed other modules have to implement hook_views_fusion(), which has to
93 * return an array where the key is the table name and the value is an array containing the values
94 * for a title and the field, which is used to join to the fused view's node table.
95 *
96 * These tables must be described through hook_views_tables(), so that it's possible to join them.
97 */
98
99 function _views_fusion_get_tables($sort_by_table = false) {
100
101 if ($sort_by_table) {
102 static $by_table = array();
103
104 if ($by_table) {
105 return $by_table;
106 }
107 $by_table = module_invoke_all('views_fusion');
108 return $by_table;
109 }
110
111 static $tables = array();
112
113 if ($tables) {
114 return $tables;
115 }
116
117 foreach (module_invoke_all('views_fusion') as $table => $array) {
118 $titles[$table] = $array['title'];
119 $fields[$table] = $array['field'];
120 }
121 $tables = array('titles' => $titles, 'fields' => $fields);
122
123 return $tables;
124 }
125
126
127
128 function views_fusion_admin_validate($form_id, &$form_values, &$form) {
129
130 if ($form_values['vid'] == $form_values['mvid']) {
131 form_set_error('mvid', t('You have to select two different views.'));
132 }
133
134 $fusions = _views_fusion_load(0);
135
136 if ($fusions[$form_values['mvid']]) {
137 foreach ($fusions[$form_values['mvid']] as $fusion) {
138 if ($fusion->vid == $form_values['vid']) {
139 form_set_error('mvid', 'This fusion already exists.');
140 }
141 }
142 }
143 views_fusion_check_cycle($form_values['vid'], $form_values['mvid'], $fusions);
144
145 }
146
147 /*
148 * Determins if adding the fusion would result in a cycle
149 * @param $vid The vid
150 * @param $mvid The mvid
151 * @param $fusions A array of all fusions sorted by mvid
152 */
153 function views_fusion_check_cycle($vid, $mvid, &$fusions) {
154
155 if ($fusions[$vid]) {
156 foreach($fusions[$vid] as $fusion) {
157 if ($mvid == $fusion->vid) {
158 form_set_error('vid', t('You must not add this fusion as it would result in a loop.'));
159 }
160 else {
161 views_fusion_check_cycle($fusion->vid, $mvid, $fusions);
162 }
163 }
164 }
165 }
166
167 function views_fusion_admin_submit($form_id, &$form_values) {
168 db_query("INSERT INTO {views_fusion} (vid, mvid, uses) VALUES(%d, %d, '%s')",
169 $form_values['vid'], $form_values['mvid'], $form_values['uses']);
170
171 //reset caches
172 views_invalidate_cache();
173 _views_fusion_load(0, TRUE);
174
175 //we need to prevent caching of the primary view
176 $view1 = views_get_view($form_values['vid']);
177 $view1->no_cache = TRUE;
178 _views_save_view($view1);
179 }
180
181
182 /*
183 * Displays existing fusions in a table
184 */
185 function views_fusion_overview() {
186
187 $result = db_query("SELECT vf.*, v1.name, v2.name as mname FROM {views_fusion} vf ".
188 "LEFT JOIN {view_view} v1 ON vf.vid = v1.vid ".
189 "LEFT JOIN {view_view} v2 ON vf.mvid = v2.vid");
190
191 if (!db_num_rows($result)) {
192 return '';
193 }
194
195 if (arg(4) && arg(5)) {
196 //delete the fusion
197 db_query("DELETE FROM {views_fusion} WHERE vid=%d AND mvid=%d", arg(4), arg(5));
198 views_invalidate_cache();
199 drupal_set_message('Your fusion has been deleted.');
200 drupal_goto('admin/build/views/fusion');
201 }
202
203 $header = array(t('primary view'), t('fused view'), t('using'), '');
204 $rows = array();
205 $tables = _views_fusion_get_tables();
206
207 while ($fusion = db_fetch_object($result)) {
208 $rows[] = array($fusion->name, $fusion->mname, $tables['titles'][$fusion->uses],
209 l(t('delete'),'admin/build/views/fusion/' . $fusion->vid .'/'. $fusion->mvid));
210 }
211
212 return theme('table', $header, $rows, array('class' => 'views_fusion'));
213 }
214
215 /*
216 * Returns a array of all fusions sorted by vids ($vid = 1) or by mvids ($vid = 0)
217 */
218 function _views_fusion_load($vid = 1, $reset = FALSE) {
219 static $fusions = array();
220
221 if ($reset) {
222 $fusions = array();
223 }
224
225 if(!$fusions[$vid]) {
226 $result = db_query("SELECT * FROM {views_fusion}");
227
228 $fusions[$vid] = array();
229 while ($fusion = db_fetch_object($result)) {
230 if ($vid) {
231 $fusions[$vid][$fusion->vid][] = $fusion;
232 }
233 else {
234 $fusions[$vid][$fusion->mvid][] = $fusion;
235 }
236 }
237 }
238 return $fusions[$vid];
239 }
240
241 /*
242 * returnes a table alias prefix to use
243 *
244 * @param $fused If the alias 'll be used for aliasing the fused view
245 */
246 function _views_fusion_get_alias(&$fusion, $fused = true) {
247 if ($fused) {
248 return 'v' . $fusion->mvid;
249 }
250 else {
251 return 'v' . $fusion->vid;
252 }
253 }
254
255 /*
256 * Implementation of hook_views_tables()
257 */
258 function views_fusion_views_tables() {
259
260 $fusion_tables = _views_fusion_get_tables();
261 $tables = array();
262
263 $fusions = _views_fusion_load(0);
264
265 foreach($fusions as $array) {
266 foreach($array as $fusion) {
267 $tables[_views_fusion_get_alias($fusion) .'node'] = array(
268 'name' => 'node',
269 'provider' => 'internal', // won't show up in external list.
270 'join' => array(
271 'left' => array(
272 'table' => $fusion->uses,
273 'field' => $fusion_tables['fields'][$fusion->uses],
274 'alias' => $fusions[$fusion->vid] ? _views_fusion_get_alias($fusion, false) : '',
275 ),
276 'right' => array(
277 'field' => 'nid'
278 ),
279 )
280 );
281 }
282 }
283 return $tables;
284 }
285
286
287 /*
288 * Implementation of hook_views_query_alter()
289 */
290 function views_fusion_views_query_alter(&$query, &$view, &$summary, &$level) {
291
292 $table_data = _views_get_tables();
293 $fusions = _views_fusion_load();
294
295 //care for a appropriate filter offset for all fused views
296 static $filter_offset = -1;
297 static $arg_offset = 0;
298 $arg_offset += count($view->argument);
299
300 if ($fusions[$view->vid]) {
301 if ($filter_offset == -1) { //init
302 $view->display = TRUE; //remember, that this view is the view, which is going to be built primary
303 $filter_offset = is_array($view->exposed_filter) ? count($view->exposed_filter) : 0;
304 }
305
306 foreach ($fusions[$view->vid] as $fusion) {
307
308 $fuse_view = views_load_view($fusion->mvid);
309 $fuse_view->use_alias_prefix = _views_fusion_get_alias($fusion);
310
311 //take care of the filter offsets
312 $fuse_view->exposed_filter_offset = $filter_offset;
313 if (is_array($fuse_view->exposed_filter)) {
314 $filter_offset += count($fuse_view->exposed_filter);
315 }
316
317 //set page type to table view, so that the fields get built
318 $fuse_view->page = 1;
319 $fuse_view->page_type = 'table';
320
321 // call this hook for the fused view and set the query object reference
322 $info = _views_build_query($fuse_view, $view->args, views_get_filter_values());
323 $fuse_query = &$fuse_view->the_query;
324
325 //remember if a view wants to display a summary
326 if ($info['summary'] && !$summary) {
327 $summary = $info['summary'];
328 $level = $arg_offset - count($fuse_view->argument) + $info['level'];
329 }
330
331 //clear the alias_prefix for the add_table calls, we have to care for each prefix ourself
332 $saved_alias_prefix = $query->use_alias_prefix;
333 $query->use_alias_prefix = '';
334
335 //add the node relation table
336 $joininfo = $table_data[$fusion->uses]['join'];
337 $joininfo['left']['alias'] = $view->use_alias_prefix;
338 //remember how often this table has been added
339 $query->tables[$fusion->uses] += $fuse_query->tables[$fusion->uses];
340 $num = $query->add_table($fusion->uses, false, 1, $joininfo);
341
342 //join new node table for the fused view
343 $joininfo = $table_data[$fuse_view->use_alias_prefix . 'node']['join'];
344 if ($num > 1) {
345 $joininfo['left']['table'] = $joininfo['left']['table'] . $num;
346 }
347 $joininfo['left']['alias'] = '';
348
349 $query->add_table($fuse_view->use_alias_prefix . 'node', false, 1, $joininfo);
350
351 //restore the alias_prefix
352 $query->use_alias_prefix = $saved_alias_prefix;
353
354 //merge query and fields
355 views_fusion_query_merge($query, $fuse_query);
356 views_fusion_views_merge($view, $fuse_view);
357
358 if (is_array($view->table_header) && is_array($fuse_view->table_header)) {
359 //reconstruct table headers with the merged field information
360 $view->table_header = _views_construct_header($view, _views_get_fields());
361 $query->set_header($view->table_header);
362 }
363 }
364 if ($view->display) {
365 $filter_offset = -1; //reset offsets for the next (new) view
366 $arg_offset = 0;
367 }
368 }
369
370 if ($filter_offset != -1) {
371 //this view will be fused
372 $query->set_header(array()); //clear header, so that no custom orderby clause is going to be generated for fused views
373 $view->the_query = &$query; //save query object reference
374 //prevent the application of a possible distinct to the query, this has to do the primary query
375 $query->no_distinct = TRUE;
376 }
377 }
378
379 /*
380 * Merges two views query objects
381 *
382 * The second object will be merged into the first.
383 * Table aliases have to be unique for both queries and the second query object mustn't contain
384 * any group by clauses.
385 */
386 function views_fusion_query_merge(&$query1, &$query2) {
387
388 if ($query2->groupby && !$query1->groupby) {
389 $query1->groupby = $query2->groupby;
390 }
391 else {
392 //add an alias to the nid field
393 $query2->fields[0] .= ' AS '. $query2->use_alias_prefix . 'node_nid';
394 }
395
396 //if query2 is set to be distinct, set query1 to distinct
397 if ($query2->distinct) {
398 $query1->set_distinct();
399 }
400
401 $query1->tablequeue = array_merge($query1->tablequeue, $query2->tablequeue);
402 $query1->joins = array_merge($query1->joins, $query2->joins);
403 $query1->fields = array_merge($query1->fields, $query2->fields);
404 $query1->where = array_merge($query1->where, $query2->where);
405 $query1->where_args = array_merge($query1->where_args, $query2->where_args);
406 $query1->orderby = array_merge($query1->orderby, $query2->orderby);
407 }
408
409 /*
410 * Merges the fields and exposed filters of the two views, so that view1 contains all
411 */
412 function views_fusion_views_merge(&$view1, &$view2) {
413
414 foreach ($view2->field as $field) {
415 //each field must be prefixed once appropriate to its view
416 if ($view2->use_alias_prefix && !$field['prefixed']) {
417 //we don't prefix the fullname, as it is needed unprefixed for retrieving the fieldinfo
418 $field['id'] = $view2->use_alias_prefix . $field['id'];
419 $field['queryname'] = $view2->use_alias_prefix . $field['queryname'];
420 $field['tablename'] = $view2->use_alias_prefix . $field['tablename'];
421 $field['prefixed'] = TRUE;
422 //save the prefix so that field handler can use it
423 $field['alias_prefix'] = $view2->use_alias_prefix;
424 }
425 $view1->field[] = $field;
426 }
427
428 $view1->exposed_filter = array_merge($view1->exposed_filter, $view2->exposed_filter);
429 $view1->argument = array_merge($view1->argument, $view2->argument);
430 }
431
432
433
434
435
436 /*
437 * Implementation of hook_form_alter().
438 */
439 function views_fusion_form_alter($form_id, &$form) {
440
441 if ($form_id == 'views_edit_view') {
442 $vid = $form['vid']['#value'];
443
444 if (!views_fusion_check_cacheable($vid)) {
445 //prevent caching
446 $form['basic-info']['no_cache'] = array('#type' => 'hidden', '#default_value' => 1);
447 }
448 }
449 if ($form_id == 'views_delete_confirm') {
450 $form['#submit']['views_fusion_delete_submit'] = array();
451 }
452 }
453
454
455 /*
456 * The view gets deleted, so delete all according fusions
457 */
458 function views_fusion_delete_submit($form_id, &$form_values) {
459
460 $vid = $form_values['vid'];
461
462 db_query("DELETE FROM {views_fusion} WHERE vid=%d OR mvid=%d", $vid, $vid);
463 views_invalidate_cache();
464 }
465
466
467 /*
468 * Determines if the the view is fused with views that are not cacheable
469 */
470 function views_fusion_check_cacheable($vid) {
471
472 $fusions = _views_fusion_load();
473
474 if ($fusions[$vid]) {
475 foreach ($fusions[$vid] as $fusion) {
476 //is fused view cacheable?
477 $view = views_get_view($fusion->mvid);
478 if (!$view->is_cacheable) {
479 return false;
480 }
481 else if (!views_fusion_check_cacheable($fusion->mvid)) {
482 return false;
483 }
484 }
485 }
486 return true;
487 }

  ViewVC Help
Powered by ViewVC 1.1.2