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

Contents of /contributions/modules/spaces/spaces.module

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


Revision 1.28 - (show annotations) (download) (as text)
Mon Oct 6 21:56:40 2008 UTC (13 months, 3 weeks ago) by yhahn
Branch: MAIN
CVS Tags: HEAD
Changes since 1.27: +1058 -1083 lines
File MIME type: text/x-php
Beginning merge of Spaces 2 to DRUPAL-6 branch
1 <?php
2 // $Id: spaces.module,v 1.25.2.2.2.4 2008/10/01 06:00:58 yhahn Exp $
3
4 // include_once(drupal_get_path('module', 'spaces') .'/spaces_views.inc');
5
6 define('SPACES_ARCHIVE_TIMESTAMP', 60*60*24*14); // 2 weeks
7 define('SPACES_FEATURE_DISABLED', 0);
8
9 /**
10 * Implementation of hook_init().
11 */
12 function spaces_init() {
13 spaces_router('menu');
14 if ((strpos($_GET['q'], 'admin/build/spaces') === 0)) {
15 include_once(drupal_get_path('module', 'spaces') .'/spaces_admin.inc');
16 }
17 }
18
19 /**
20 * Implementation of hook_perm().
21 */
22 function spaces_perm() {
23 return array('administer spaces');
24 }
25
26 /**
27 * Implementation of hook_context_prefix_provider().
28 */
29 function spaces_context_prefix_provider() {
30 $items = array();
31 foreach(spaces_types() as $type => $info) {
32 $items['spaces_'. $type] = array(
33 'name' => $info['title'],
34 'description' => t('Sets a spaces context.'),
35 'callback' => 'spaces_init_context',
36 'callback arguments' => array($type),
37 'example' => 'my-space',
38 );
39 }
40 return $items;
41 }
42
43 /**
44 * Context prefix provider callback.
45 */
46 function spaces_init_context($type, $sid) {
47 static $once;
48 if (!isset($once)) {
49 $once = FALSE;
50 }
51
52 if (!$once) {
53 // @TODO: remove the hardcoded space type here and attempt to
54 // discover through the context prefix param : (
55 context_set('spaces', 'sid', $sid);
56 $space = spaces_load($type, $sid, TRUE);
57 spaces_set_space($space);
58 $once = TRUE;
59 }
60 }
61
62 /**
63 * Implementation of hook_menu().
64 */
65 function spaces_menu() {
66 $items = array();
67 $items['admin/build/spaces'] = array(
68 'title' => t('Spaces presets'),
69 'description' => t('Create and configure spaces for your site.'),
70 'page callback' => 'drupal_get_form',
71 'page arguments' => array('spaces_preset_default_form'),
72 'access callback' => 'user_access',
73 'access arguments' => array('administer spaces'),
74 'file' => 'spaces_admin.inc',
75 'type' => MENU_NORMAL_ITEM,
76 );
77 $items['admin/build/spaces/presets'] = array(
78 'title' => t('Presets'),
79 'page callback' => 'drupal_get_form',
80 'page arguments' => array('spaces_preset_default_form'),
81 'type' => MENU_DEFAULT_LOCAL_TASK,
82 'weight' => -1,
83 );
84 $items['admin/build/spaces/presets/add'] = array(
85 'title' => t('Add'),
86 'page callback' => 'drupal_get_form',
87 'page arguments' => array('spaces_preset_form', 'add'),
88 'type' => MENU_CALLBACK,
89 );
90 $items['admin/build/spaces/presets/edit'] = array(
91 'page callback' => 'drupal_get_form',
92 'page arguments' => array('spaces_preset_form', 'edit'),
93 'type' => MENU_CALLBACK,
94 );
95 $items['admin/build/spaces/presets/delete'] = array(
96 'page callback' => 'drupal_get_form',
97 'age arguments' => array('spaces_preset_delete_form'),
98 'type' => MENU_CALLBACK,
99 );
100 $items['admin/build/spaces/presets/disable'] = array(
101 'page callback' => '_spaces_preset_disable_page',
102 'type' => PAGE_CALLBACK,
103 );
104 $items['admin/build/spaces/presets/enable'] = array(
105 'page callback' => '_spaces_preset_enable_page',
106 'type' => PAGE_CALLBACK,
107 );
108 return $items;
109 }
110
111 /**
112 * Implementation of hook_theme().
113 */
114 function spaces_theme() {
115 $items = array();
116 $items['spaces_form'] = array();
117 $items['spaces_features_form'] = array();
118 $items['spaces_customize_item'] = array();
119 $items['spaces_preset_default_form'] = array();
120 $items['spaces_form_presets'] = array();
121 $items['spaces_node_links'] = array();
122 return $items;
123 }
124
125 /**
126 * Implementation of hook_user().
127 */
128 function spaces_user($op, &$edit, &$account, $category = NULL) {
129 if (in_array($op, array('view', 'form'))) {
130 spaces_router('user view', $account);
131 }
132 }
133
134 /**
135 * Implementation of hook_nodeapi().
136 */
137 function spaces_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
138 switch ($op) {
139 case 'view':
140 if ($page && !$teaser && arg(0) == 'node' && arg(1) == $node->nid) {
141 spaces_router('node view', $node);
142 }
143 break;
144 }
145 }
146
147 /**
148 * Implementation of hook_form_alter().
149 */
150 function spaces_form_alter(&$form, $form_state, $form_id) {
151 if ($form['#id'] == 'node-form' && (arg(0) .'/'. arg(1) != 'admin/content')) {
152 spaces_router('node form', $form['#node']);
153 }
154 }
155
156 /**
157 * Implementation of hook_block()
158 */
159 function spaces_block($op = 'list', $delta = 0) {
160 if ($op == 'list') {
161 $blocks[1]['info'] = t('Spaces: Contextual Tools');
162 $blocks[2]['info'] = t('Spaces: Navigation');
163 $blocks[3]['info'] = t('Spaces: Utility links');
164 return $blocks;
165 }
166 else if ($op == 'view') {
167 switch ($delta) {
168 case 1:
169 return _spaces_block_tools();
170 case 2:
171 return _spaces_block_nav();
172 case 3:
173 return _spaces_block_utility_links();
174 }
175 }
176 }
177
178 /**
179 * Implementation of hook_context_define()
180 *
181 * hook_context_define provides a central method to define contextual behavior. The spaces
182 * module extends this hook in the "spaces" key namespace. Available attributes are:
183 * 'label', 'description', 'options', 'options_function', '#weight'
184 *
185 * @return
186 * Keyed array which defines features.
187 */
188 function spaces_context_define() {
189 $items = array();
190 return $items;
191 }
192
193 /**
194 * Implementation of hook_spaces_settings().
195 */
196 function spaces_spaces_settings() {
197 return array(
198 'home' => new space_setting_home(),
199 );
200 }
201
202 /**
203 * Implementation of hook_spaces_customizers().
204 */
205 function spaces_spaces_customizers() {
206 return array(
207 'menu' => new space_customizer_menu(),
208 'views' => new space_customizer_views(),
209 );
210 }
211
212 /**
213 * Implementation of hook_views_pre_query().
214 */
215 function spaces_views_pre_query(&$view) {
216 // Make spaces filter'd views non-cacheable
217 foreach ($view->filter as $filter) {
218 if ($filter['field'] == 'spaces.type') {
219 $view->is_cacheable = false;
220 }
221 }
222 if ($view->build_type == 'page' && $space = spaces_get_space()) {
223 if ($feature = context_get('spaces', 'feature')) {
224 $view = space_customizer_views::customize($space, $feature, $view);
225 }
226 }
227 }
228
229 /**
230 * SPACES API =========================================================
231 */
232
233 /**
234 * Interface for space objects.
235 */
236 interface space {
237 /**
238 * Core API-related functions
239 */
240 function __construct($type, $sid = NULL, $is_active = FALSE);
241 function save();
242 function delete();
243
244 /**
245 * Method that defines the feature options available for this space
246 * type.
247 *
248 * @return array
249 * A FormAPI suitable options array of feature options.
250 */
251 function feature_options();
252
253 /**
254 * Method that allows the space type to add to/modify the utility
255 * "links" provided to visitors in a space.
256 *
257 * @param $links
258 * The current space utility links (for use with theme_links) array
259 * passed by reference.
260 */
261 function links(&$links);
262
263 /**
264 * Method that provides a space-type specific check for whether the
265 * provided feature is accessible by the current user.
266 *
267 * @param $feature
268 * The feature identifier string.
269 *
270 * @return bool
271 * TRUE if the user has access to this feature. FALSE if not.
272 */
273 function feature_access($feature = NULL);
274
275 /**
276 * Method that provides a space-type specific check for whether the
277 * the space can be administered by the current user.
278 *
279 * @return bool
280 * TRUE if the user has admin access to this space. FALSE if not.
281 */
282 function admin_access();
283
284 /**
285 * Master router method that allows the space type to define routing
286 * workflow rules. Currently called at the following hooks:
287 *
288 * hook_menu(), where $may_cache == FALSE
289 * hook_nodeapi(), where $op == 'view'
290 * hook_form_alter(), when editing a node
291 * hook_user(), where $op == 'view'
292 *
293 * @param $op
294 * The current hook from which the router is being called.
295 * Can be one of the following: 'menu', 'node view', 'node form',
296 * 'user view'
297 * @param $object
298 * The object relevant to the current $op called. e.g. when
299 * $op == 'node view', $object is the node object.
300 * @param $is_active
301 * Boolean for whether this router has been called as part of a
302 * fully instantiated space object. If FALSE, the router should
303 * not assume the space has been fully constructed and should take
304 * the appropriate actions as necessary.
305 *
306 * @return bool
307 * TRUE to allow the user to pass through. FALSE to return a
308 * drupal_access_denied() page to the user.
309 */
310 function router($op, $object = NULL, $is_active = TRUE);
311
312 /**
313 * Redirect handler that abstracts redirect logic and allows space
314 * types to define their own definitions of various spaces concepts.
315 *
316 * @param $op
317 * The page being redirected to. Currently only $op value is 'home'
318 */
319 function redirect($op = 'home');
320
321 /**
322 * Allows the space type to implement custom form options on the
323 * feature/preset form.
324 *
325 * @return array
326 * A FormAPI element array. It will be included as a child element
327 * of the master space feature form.
328 */
329 function form();
330
331 /**
332 * Validate handler for the space type's custom form.
333 *
334 * @param $values
335 * The submitted values.
336 */
337 function validate($values);
338
339 /**
340 * Custom submit handler for the space type's custom form. For
341 * example, allows the space type to process values in preparation
342 * for spaces_save().
343 *
344 * @param $values
345 * The submitted values.
346 *
347 * @return array
348 * An array of values.
349 */
350 function submit($values);
351
352 /**
353 * Views filter callback that allows the space type to filter views
354 * when it is the current active space.
355 *
356 * @param $query
357 * The views query object.
358 */
359 function views_filter($is_active, &$query);
360
361 /**
362 * Allows the space type to take additional action when enforcing a
363 * preset against the current space.
364 *
365 * @param $preset
366 * A spaces preset definition.
367 */
368 function preset_enforce($preset);
369 }
370
371 /**
372 * Interface for space settings.
373 */
374 interface space_setting {
375 function form($space, $value = array());
376 function validate($space, $value);
377 function submit($space, $value);
378 }
379
380 /**
381 * Interface for space customizers.
382 */
383 interface space_customizer {
384 function form($space, $feature);
385 function validate($space, $feature, $value);
386 function submit($space, $feature, $value);
387 function customize($space, $feature, $object = NULL);
388 }
389
390 /**
391 * Provides a homepage setting for each space.
392 */
393 class space_setting_home implements space_setting {
394 var $id = 'home';
395
396 function form($space, $value = array()) {
397 $options = array(0 => '---');
398 foreach (spaces_features($space->type) as $f => $feature) {
399 if ($feature->spaces['menu'] && $space->features[$f] != SPACES_FEATURE_DISABLED) {
400 $options[$f] = $feature->spaces['label'];
401 }
402 }
403 $form = array(
404 '#title' => t('Homepage'),
405 '#description' => t('The default page for this space.'),
406 '#type' => 'select',
407 '#options' => $options,
408 '#validate' => array('spaces_setting_validate' => array($this->id)),
409 '#default_value' => $value ? $value : 0,
410 );
411 return $form;
412 }
413
414 function validate($space, $value) {
415 // Exclude space "prototypes" (like that used for the preset form)
416 if ($space->sid) {
417 if (!$value && is_array($space->features) && (array_sum($space->features) != 0)) {
418 form_set_error('settings]['. $this->id, t('You must select a homepage for this space.'));
419 }
420 }
421 }
422
423 function submit($space, $value) {
424 return $value;
425 }
426 }
427
428 /**
429 * Customizer for feature menus.
430 */
431 class space_customizer_menu implements space_customizer {
432
433 var $name = 'Menu';
434
435 function form($space, $feature) {
436 $features = spaces_features();
437 $f = $features[$feature];
438 // Customize menus
439 if (isset($f->spaces['menu']) && count($f->spaces['menu'])) {
440 // Get customized values
441 $feature_menu = $f->spaces['menu'];
442 $feature_menu = $this->customize($space, $feature, $feature_menu);
443
444 $form = array();
445 foreach ($feature_menu as $path => $item) {
446 $form[$path] = array(
447 '#title' => $path,
448 '#type' => 'fieldset',
449 '#tree' => TRUE,
450 );
451 $form[$path]['title'] = array(
452 '#title' => t('Title'),
453 '#type' => 'textfield',
454 '#size' => 40,
455 '#maxlength' => 255,
456 '#default_value' => $item['title'],
457 );
458 }
459 }
460 return $form;
461 }
462
463 function validate($space, $feature, $value) {
464 return;
465 }
466
467 function submit($space, $feature, $value) {
468 $features = spaces_features();
469 $feature_menu = $features[$feature]->spaces['menu'];
470 foreach ($value as $path => $item) {
471 if ($item == $feature_menu[$path]) {
472 unset($value[$path]);
473 }
474 }
475 return $value;
476 }
477
478 function customize($space, $feature, $menu = NULL) {
479 if (isset($space->customizer[$feature]['menu'])) {
480 $custom_menu = array();
481 $customizer = $space->customizer[$feature]['menu'];
482 foreach ($menu as $path => $item) {
483 if (isset($customizer[$path])) {
484 $menu[$path] = $customizer[$path];
485 }
486 }
487 }
488 return $menu;
489 }
490 }
491
492 /**
493 * Customizer for views titles and headers.
494 */
495 class space_customizer_views implements space_customizer {
496
497 var $name = 'Views';
498
499 function form($space, $feature) {
500 $features = spaces_features();
501 $f = $features[$feature];
502
503 $form = array();
504 foreach ($f->views as $view_name) {
505 $view = views_get_view($view_name);
506 $view = $this->customize($space, $feature, $view);
507 // Only allow customization of page views for now
508 // @TODO: add input format checking on headers/footers/etc.
509 if ($view && $view->page == TRUE) {
510 $form[$view_name] = array(
511 '#title' => $view_name,
512 '#type' => 'fieldset',
513 '#tree' => TRUE,
514 );
515 // $default_page_title = isset($customizer['views'][$view_name]['page_title']) ? $customizer['views'][$view_name]['page_title'] : $view->page_title;
516 $form[$view_name]['page_title'] = array(
517 '#title' => t('Page title'),
518 '#type' => 'textfield',
519 '#size' => 40,
520 '#default_value' => $view->page_title,
521 );
522 // $default_page_header = isset($customizer['views'][$view_name]['page_header']) ? $customizer['views'][$view_name]['page_header'] : $view->page_header;
523 $form[$view_name]['page_header'] = array(
524 '#title' => t('Page header'),
525 '#type' => 'textarea',
526 '#rows' => 2,
527 '#cols' => 40,
528 '#default_value' => $view->page_header,
529 );
530 }
531 }
532 return $form;
533 }
534
535 function validate($space, $feature, $value) {
536 return;
537 }
538
539 function submit($space, $feature, $value) {
540 // Would like to prune unset values, but it appears that views
541 // caching makes it unusable...
542 return $value;
543 }
544
545 function customize($space, $feature, $view = NULL) {
546 if (isset($space->customizer[$feature]['views'][$view->name])) {
547 // Apply customizations to the view object
548 $customizer = $space->customizer[$feature]['views'][$view->name];
549 foreach ($customizer as $property => $value) {
550 $view->{$property} = $value;
551 }
552 }
553 return $view;
554 }
555 }
556
557 /**
558 * Load a space.
559 *
560 * @param $type
561 * The type of the space to be loaded. Must be one of the keys in the
562 * array returned by spaces_types().
563 * @param $sid
564 * The id of the space to be loaded. If omitted, a "prototype" space
565 * will be constructed.
566 * @param $is_active
567 * Optional boolean flag for whether this space is active or not.
568 * Defaults to FALSE.
569 *
570 * @return
571 * The requested space object or FALSE if something went wrong.
572 */
573 function spaces_load($type, $sid = NULL, $is_active = FALSE) {
574 $types = spaces_types();
575 if (isset($types[$type])) {
576 $class = $types[$type]['class'];
577
578 // Create a new space object
579 $space = new $class($type, $sid, $is_active);
580
581 // Initialize various space variables
582 $space->type = $type;
583 $space->features = array();
584 $space->settings = array();
585 $space->customizer = array();
586
587 // Initialize space specific settings if $sid is provided
588 if ($sid) {
589 $space->sid = $sid;
590 if ($prefix = context_prefix_api('load', array('provider' => 'spaces_'. $type, 'id' => $sid))) {
591 $space->prefix = $prefix['prefix'];
592 }
593
594 // Load features
595 $result = db_query('SELECT id, value FROM {spaces_features} WHERE sid = %d AND type = "%s"', $sid, $type);
596 while ($row = db_fetch_object($result)) {
597 $space->features[$row->id] = $row->value;
598 }
599
600 // Load settings
601 $result = db_query('SELECT id, value FROM {spaces_settings} WHERE sid = %d AND type = "%s"', $sid, $type);
602 while ($row = db_fetch_object($result)) {
603 $space->settings[$row->id] = unserialize($row->value);
604 }
605
606 // Load customizer & preset
607 $row = db_fetch_object(db_query("SELECT customizer, preset FROM {spaces} WHERE sid = %d AND type = '%s'", $space->sid, $space->type));
608 $space->customizer = $row->customizer ? unserialize($row->customizer) : array();
609
610 // Enforce preset or use default if not found
611 $default_presets = variable_get('spaces_default_presets', array());
612 if ($row->preset) {
613 $space->preset = $row->preset;
614 }
615 else if ($space->preset == NULL && isset($default_presets[$type])) {
616 $space->preset = $default_presets[$type];
617 spaces_preset_enforce($space);
618 }
619 }
620
621 return $space;
622 }
623 return false;
624 }
625
626 /**
627 * Saves a space object's feature/setting values.
628 *
629 * @param $space
630 * The space object to save.
631 *
632 * @return
633 * Returns TRUE on success, FALSE on failure.
634 */
635 function spaces_save($space) {
636 if ($space->sid) {
637 // Enforce the preset
638 spaces_preset_enforce($space);
639
640 // Update features
641 db_query("DELETE FROM {spaces_features} WHERE sid = %d AND type = '%s'", $space->sid, $space->type);
642 $valid = spaces_features($space->type);
643 foreach ($space->features as $feature => $value) {
644 if (isset($valid[$feature])) {
645 $values = array($space->sid, $space->type, $feature, $value);
646 db_query('INSERT INTO {spaces_features} (sid, type, id, value) VALUES (%d, "%s", "%s", "%s")', $values);
647 }
648 }
649
650 // Update settings
651 db_query("DELETE FROM {spaces_settings} WHERE sid = %d AND type = '%s'", $space->sid, $space->type);
652 $valid = spaces_settings();
653 foreach ($space->settings as $setting => $value) {
654 if (isset($valid[$setting])) {
655 $value = serialize($value);
656 $values = array($space->sid, $space->type, $setting, $value);
657 db_query('INSERT INTO {spaces_settings} (sid, type, id, value) VALUES (%d, "%s", "%s", "%s")', $values);
658 }
659 }
660
661 // Update preset & customizer
662 $exists = db_result(db_query("SELECT count(sid) FROM {spaces} WHERE sid = %d AND type = '%s'", $space->sid, $space->type));
663 if ($exists) {
664 db_query("UPDATE {spaces} SET preset = '%s', customizer = '%s' WHERE sid = %d AND type = '%s'", $space->preset, serialize($space->customizer), $space->sid, $space->type);
665 }
666 else {
667 db_query("INSERT INTO {spaces} (sid, type, preset, customizer) VALUES(%d, '%s', '%s', '%s')", $space->sid, $space->type, $space->preset, serialize($space->customizer));
668 }
669
670 // Save context prefix if space type allows prefix customization
671 $types = spaces_types();
672 $save_prefix = isset($types[$space->type]['custom prefixes']) && $types[$space->type]['custom prefixes'];
673 if ($space->prefix && $save_prefix) {
674 // We need to concatenate the type/sid so that collisions between
675 // space types do not occur.
676 $prefix = array(
677 'provider' => 'spaces_'. $space->type,
678 'id' => $space->sid,
679 );
680 context_prefix_api('delete', $prefix);
681 $prefix['prefix'] = $space->prefix;
682 context_prefix_api('insert', $prefix);
683 }
684
685 // Allow space type to do its own saving
686 $space->save();
687
688 return true;
689 }
690 return false;
691 }
692
693 /**
694 * Deletes a space object's records in the database.
695 *
696 * @param $space
697 * The space object to delete.
698 *
699 * @return
700 * Returns TRUE for now.
701 */
702 function spaces_delete($space) {
703 // Remove all features and settings
704 db_query("DELETE FROM {spaces} WHERE sid = %d AND type = '%s'", $space->sid, $space->type);
705 db_query("DELETE FROM {spaces_features} WHERE sid = %d AND type = '%s'", $space->sid, $space->type);
706 db_query("DELETE FROM {spaces_settings} WHERE sid = %d AND type = '%s'", $space->sid, $space->type);
707
708 // Clear the prefix path from the context_prefix table
709 $prefix = array('provider' => 'spaces_'. $space->type, 'id' => $space->sid);
710 context_prefix_api('delete', $prefix);
711
712 // Allow space type to do its own deleting
713 $space->delete();
714
715 return true;
716 }
717
718 /**
719 * Enforces a spaces preset.
720 */
721 function spaces_preset_enforce(&$space) {
722 $presets = spaces_presets($space->type);
723 if (isset($space->preset) && isset($presets[$space->preset])) {
724 $preset = $presets[$space->preset]['preset'];
725
726 // Enforce features
727 if (is_array($preset['features'])) {
728 foreach ($preset['features'] as $feature => $value) {
729 if ($preset['locked']['features'][$feature] || !isset($space->features[$feature])) {
730 $space->features[$feature] = $value;
731 }
732 }
733 }
734
735 // Enforce settings
736 if (is_array($preset['settings'])) {
737 foreach ($preset['settings'] as $setting => $value) {
738 if ($preset['locked']['settings'][$setting] || !isset($space->features[$setting])) {
739 $space->settings[$setting] = $value;
740 }
741 }
742 }
743
744 // Type-specific presets
745 $space->preset_enforce($preset);
746 }
747 }
748
749 /**
750 * Invokes hook_spaces_types() to gather an array of space types and
751 * associated classes.
752 *
753 * Implementing modules should provide an associate array in
754 * hook_spaces_types() of the following format:
755 *
756 * return array(
757 * $id => array(
758 * 'class' => $space_class,
759 * 'title' => $space_type_name,
760 * 'custom prefixes' => $bool,
761 * ),
762 * );
763 *
764 * Where:
765 *
766 * $id: A unique string identifier for this space type. e.g. "og"
767 * $space_class: The PHP class to construct for this space. e.g. "spaces_og"
768 * $space_type_name: The human-readable name of your space type. e.g. "Group space"
769 * $bool: TRUE or FALSE for whether spaces should provide a UI for
770 * users to provide a custom prefix path for each space. If FALSE,
771 * the implementing module should implement
772 * hook_context_prefix_prefixes() in order to provide prefixes
773 * programmatically.
774 *
775 * @param $reset
776 * Optional reset flag for clearing the static cache.
777 *
778 * @return
779 * An array of space types where $key => $value corresponds to the space type => space class.
780 */
781 function spaces_types($reset = false) {
782 static $spaces_types;
783 if (!isset($spaces_types) || $reset) {
784 $spaces_types = module_invoke_all('spaces_types');
785 }
786 return $spaces_types;
787 }
788
789 /**
790 * Gather an array of spaces presets from the DB.
791 *
792 * @param $type
793 * Optional space type to filter results by.
794 * @param $include_disabled
795 * Optional flag to return all presets, including disabled ones.
796 * @param $reset
797 * Optional reset flag for clearing the static cache.
798 *
799 * @return
800 * An array of space types where $key => $value corresponds to the space type => space class.
801 */
802 function spaces_presets($type = NULL, $include_disabled = FALSE, $reset = FALSE) {
803 static $presets;
804 if (!isset($presets)) {
805 $presets = array();
806 $result = db_query("SELECT * FROM {spaces_presets}");
807 $disabled = variable_get('spaces_disabled_presets', array());
808 while ($row = db_fetch_object($result)) {
809 $presets[$row->type][$row->id] = array(
810 'name' => $row->name,
811 'description' => $row->description,
812 'preset' => unserialize($row->value),
813 'disabled' => isset($disabled[$row->type][$row->id]),
814 );
815 }
816
817 // Collect presets provided by modules in code
818 foreach(module_implements('spaces_presets') as $module) {
819 $items = call_user_func($module .'_spaces_presets');
820 foreach($items as $id => $preset) {
821 $presets[$preset['type']][$id] = array(
822 'name' => $preset['name'],
823 'description' => $preset['description'],
824 'preset' => $preset['preset'],
825 'disabled' => isset($disabled[$preset['type']][$id]),
826 'module' => $module,
827 );
828 }
829 }
830 }
831 // Move filtering outside main condition in order to hit the DB only once
832 $return = $presets;
833 if (!$include_disabled) {
834 foreach (array_keys($return) as $preset_type) {
835 foreach ($return[$preset_type] as $id => $preset) {
836 if ($preset['disabled']) {
837 unset($return[$preset_type][$id]);
838 }
839 }
840 }
841 }
842
843 if ($type) {
844 return isset($return[$type]) ? $return[$type] : array();
845 }
846 return $return;
847 }
848
849 /**
850 * Wrapper function around spaces_set_space(). Retrieves the current
851 * active space.
852 */
853 function spaces_get_space() {
854 return spaces_set_space();
855 }
856
857 /**
858 * Sets the specified space as the current active space. Returns the
859 * active space if no space is provided.
860 *
861 * @param $space
862 * The space object to set as the active space. Optional.
863 * @param $reset
864 * Optional flag to reset the static cache.
865 *
866 * @return
867 * The active space object or FALSE if there is no active space.
868 */
869 function spaces_set_space($space = NULL, $reset = FALSE) {
870 static $current_space;
871 if (!isset($current_space) || $reset) {
872 $current_space = $space;
873 }
874 return $current_space ? $current_space : FALSE;
875 }
876
877 /**
878 * Wrapper around implementations of $space->router. Provides
879 * additional intelligence, including a global killswitch and routing
880 * when no spaces are active.
881 *
882 * @param $op
883 * The current "hook" or "hook op" identifier for the $space->router
884 * to act on.
885 * @param $object
886 * Optional object to pass to the $space->router.
887 */
888 function spaces_router($op, $object = NULL) {
889 // Check global killswitch
890 if (spaces_router_get()) {
891 $access = true;
892 $types = spaces_types();
893 // Run the router for the active space
894 if ($space = spaces_get_space()) {
895 $access = $access && $space->router($op, $object);
896 unset($types[$space->type]);
897 }
898 // Run each non-active space type's router
899 foreach ($types as $type => $info) {
900 $access = $access && call_user_func(array($info['class'], 'router'), $op, $object, FALSE);
901 }
902 if (!$access && !user_access('administer spaces')) {
903 drupal_access_denied();
904 exit;
905 }
906 }
907 }
908
909 /**
910 * Wrapper around spaces_router_get().
911 */
912 function spaces_router_set($status) {
913 return spaces_router_get($status, TRUE);
914 }
915
916 /**
917 * Sets a static variable that is used to disable spaces routing
918 * altogether -- e.g. for install/update scripts, migrations, etc.
919 *
920 * @param $enabled
921 * Optional boolean for enabling/disabling spaces routing.
922 * @param $reset
923 * Optional boolean for resetting the static cache.
924 *
925 * @return
926 * Returns a boolean for whether routing is enabled/disabled.
927 */
928 function spaces_router_get($enabled = 1, $reset = FALSE) {
929 static $status;
930 if (!isset($status) || $reset) {
931 $status = $enabled;
932 }
933 return $status;
934 }
935
936 /**
937 * Retrieve all available features.
938 *
939 * @param $type
940 * Optional type flag by which to filter available features.
941 * @param $reset
942 * Optional boolean flag for resetting the static cache.
943 *
944 * @return
945 * Keyed array of potential features.
946 */
947 function spaces_features($type = NULL, $reset = FALSE) {
948 static $spaces_features;
949 if (!isset($spaces_features) || $reset) {
950 $spaces_features = array();
951 foreach (context_ui_defaults('spaces') as $feature) {
952 if ($feature->spaces) {
953 // If type is specified, perform additional checks
954 if ($type) {
955 if (isset($feature->spaces['types']) && in_array($type, $feature->spaces['types'])) {
956 $spaces_features[$feature->value] = $feature;
957 }
958 else if (!isset($feature->spaces['types']) || !$feature->spaces['types']) {
959 $spaces_features[$feature->value] = $feature;
960 }
961 }
962 else {
963 $spaces_features[$feature->value] = $feature;
964 }
965 }
966 }
967 }
968 return $spaces_features;
969 }
970
971 /**
972 * Retrieve all available settings.
973 *
974 * @param $reset
975 * Optional boolean flag for resetting the static cache.
976 *
977 * @return
978 * Keyed array of potential settings.
979 */
980 function spaces_settings($reset = FALSE) {
981 static $settings;
982 if (!isset($settings) || $reset) {
983 $settings = array();
984 foreach (module_implements('spaces_settings') as $module) {
985 $function = $module .'_spaces_settings';
986 $settings = array_merge($settings, $function());
987 }
988 }
989 return $settings;
990 }
991
992 /**
993 * Retrieve all available customizers.
994 *
995 * @param $reset
996 * Optional boolean flag for resetting the static cache.
997 *
998 * @return
999 * Keyed array of customizers.
1000 */
1001 function spaces_customizers($reset = FALSE) {
1002 static $customizers;
1003 if (!isset($customizers) || $reset) {
1004 $customizers = array();
1005 $customizers = module_invoke_all('spaces_customizers');
1006 }
1007 return $customizers;
1008 }
1009
1010 /**
1011 * Returns a content type => features map.
1012 *
1013 * @param $reset
1014 * Optional boolean flag for resetting the static cache.
1015 *
1016 * @return
1017 * Keyed array where $nodetype => $feature.
1018 */
1019 function spaces_content_types($reset = FALSE) {
1020 static $map;
1021 if (!isset($map) || $reset) {
1022 $map = array();
1023 $features = spaces_features();
1024 foreach ($features as $id => $feature) {
1025 if (is_array($feature->node)) {
1026 foreach ($feature->node as $type) {
1027 $map[$type] = $id;
1028 }
1029 }
1030 }
1031 }
1032 return $map;
1033 }
1034
1035 /**
1036 * Returns a links array in the theme_links() format of the current
1037 * space's menu items for features accessible to the current user. Each
1038 * item has a keyed array of children items if applicable.
1039 *
1040 * @return
1041 * Array of links.
1042 */
1043 function spaces_features_menu() {
1044 static $menu;
1045 if (!isset($menu)) {
1046 $menu = array();
1047 $space = spaces_get_space();
1048 if ($space) {
1049 $features = spaces_features($space->type);
1050 $active_feature = context_get('spaces', 'feature');
1051
1052 $depth = 1;
1053
1054 // Loop once through features to build menu set
1055 foreach ($space->features as $feature => $value) {
1056 if (($value != SPACES_FEATURE_DISABLED) && $space->feature_access($feature) && isset($features[$feature]->spaces['menu'])) {
1057 // Customize the menu
1058 $feature_menu = $features[$feature]->spaces['menu'];
1059 $feature_menu = space_customizer_menu::customize($space, $feature, $feature_menu);
1060
1061 // Collect menu items
1062 foreach ($feature_menu as $path => $item) {
1063 $args = explode('/', $path);
1064 $item['href'] = $path;
1065 $item['children'] = array();
1066 $item['depth'] = count($args);
1067 $item['args'] = $args;
1068
1069 if (($item['depth'] == 1) && ($feature == $active_feature)) {
1070 $item['attributes'] = array(
1071 'class' => 'active',
1072 );
1073 }
1074 else if ($item['depth'] > $depth) {
1075 $depth = $item['depth'];
1076 }
1077
1078 $menu[$path] = $item;
1079 }
1080 }
1081 }
1082
1083 // Second loop to tree the menu
1084 while ($depth >= 1) {
1085 foreach ($menu as $path => $item) {
1086 if ($item['depth'] == $depth) {
1087 $args = implode('/', array_slice($item['args'], 0, ($depth-1)));
1088 if ($depth > 1) {
1089 if (isset($menu[$args])) {
1090 unset($item['depth']);
1091 unset($item['args']);
1092 $menu[$args]['children'][$path] = $item;
1093 }
1094 unset($menu[$path]);
1095 }
1096 else {
1097 unset($menu[$path]['args']);
1098 unset($menu[$path]['depth']);
1099 }
1100 }
1101 }
1102 $depth--;
1103 }
1104 }
1105 }
1106 return $menu;
1107 }
1108
1109 /**
1110 * Preset options form that can be reused by implementing modules.
1111 *
1112 * @param $space
1113 * A space object.
1114 *
1115 * @return
1116 * A FormAPI array structure.
1117 */
1118 function spaces_form_presets($space) {
1119 $default_presets = variable_get('spaces_default_presets', array());
1120 if (isset($space->preset)) {
1121 $default_preset = $space->preset;
1122 }
1123 else if (isset($default_presets[$space->type])) {
1124 $default_preset = $default_presets[$space->type];
1125 }
1126 else {
1127 $default_preset = NULL;
1128 }
1129
1130 // Radios for presets
1131 $form = array(
1132 '#tree' => false,
1133 '#theme' => 'spaces_form_presets',
1134 );
1135 $form['preset'] = array(
1136 '#title' => t('Preset'),
1137 '#type' => 'radios',
1138 '#required' => true,
1139 '#options' => array(),
1140 '#default_value' => $default_preset,
1141 );
1142 $form['info'] = array();
1143 foreach (spaces_presets($space->type) as $id => $preset) {
1144 $form['preset']['#options'][$id] = $preset['name'];
1145 $form['info'][$id] = array(
1146 '#type' => 'item',
1147 '#title' => $preset['name'],
1148 '#description' => $preset['description'],
1149 );
1150 }
1151 return $form;
1152 }
1153
1154 /**
1155 * Menu admin access wrapper.
1156 */
1157 function spaces_admin_access($type = NULL) {
1158 if ($space = spaces_get_space()) {
1159 if (isset($type)) {
1160 if ($space->type == $type && $space->admin_access()) {
1161 return true;
1162 }
1163 }
1164 else if ($space->admin_access()) {
1165 return true;
1166 }
1167 }
1168 return false;
1169 }
1170
1171 /**
1172 * A mild abstraction of hook_menu() items that can be used by
1173 * implementing modules to embed/graft relevant spaces items into the
1174 * menu tree. Should only be used when the $may_cache argument of
1175 * hook_menu() is false.
1176 *
1177 * @param $space
1178 * A space object.
1179 * @param $local_tasks
1180 * Optional boolean flag for whether the items are fully rendered as
1181 * local tasks.
1182 * @param $path_prefix
1183 * A path to prefix the menu item paths by.
1184 *
1185 * @return
1186 * An array of menu items.
1187 */
1188 function spaces_active_space_menu($type, $local_tasks = FALSE, $path_prefix = '') {
1189 $types = spaces_types();
1190 $path_prefix = !empty($path_prefix) ? $path_prefix .'/' : '';
1191
1192 if ($local_tasks == FALSE) {
1193 $items[$path_prefix. 'spaces'] = array(
1194 'title' => t('!space_type settings', array('!space_type' => $types[$space->type]['title'])),
1195 'page callback' => 'drupal_get_form',
1196 'page arguments' => array('spaces_basic_form'),
1197 'access callback' => 'spaces_admin_access',
1198 'access arguments' => array($type),
1199 'type' => MENU_NORMAL_ITEM,
1200 'file' => 'spaces_admin.inc',
1201 );
1202 }
1203 else {
1204 $items[$path_prefix. 'spaces'] = array(
1205 'title' => t('Spaces'),
1206 'page callback' => 'drupal_get_form',
1207 'page arguments' => array('spaces_basic_form'),
1208 'access callback' => 'spaces_admin_access',
1209 'access arguments' => array($type),
1210 'type' => MENU_LOCAL_TASK,
1211 'file' => 'spaces_admin.inc',
1212 'weight' => 1,
1213 );
1214 }
1215 $items[$path_prefix. 'spaces/setup'] = array(
1216 'title' => t('Basic setup'),
1217 'page callback' => 'drupal_get_form',
1218 'page arguments' => array('spaces_basic_form'),
1219 'access callback' => 'spaces_admin_access',
1220 'access arguments' => array($type),
1221 'type' => MENU_DEFAULT_LOCAL_TASK,
1222 'file' => 'spaces_admin.inc',
1223 'weight' => 0,
1224 );
1225 $items[$path_prefix. 'spaces/features'] = array(
1226 'title' => t('Features'),
1227 'page callback' => 'drupal_get_form',
1228 'page arguments' => array('spaces_features_form'),
1229 'access callback' => 'spaces_admin_access',
1230 'access arguments' => array($type),
1231 'type' => MENU_LOCAL_TASK,
1232 'file' => 'spaces_admin.inc',
1233 'weight' => 1,
1234 );
1235 $items[$path_prefix. 'spaces/customize'] = array(
1236 'title' => t('Customize'),
1237 'page callback' => 'spaces_customize',
1238 'page arguments' => array(),
1239 'access callback' => 'spaces_admin_access',
1240 'access arguments' => array($type),
1241 'type' => MENU_LOCAL_TASK,
1242 'file' => 'spaces_admin.inc',
1243 'weight' => 2,
1244 );
1245 return $items;
1246 }
1247
1248 /**
1249 * THEME FUNCTIONS ====================================================
1250 */
1251
1252 /**
1253 * Generates an array of utility links for the current space suitable
1254 * for use in theme_links().
1255 */
1256 function spaces_space_links() {
1257 $links = array();
1258 if ($space = spaces_get_space()) {
1259 $links = array();
1260 $space->links($links);
1261 }
1262 return $links;
1263 }
1264
1265