Spaces 3.x
----------
Spaces is an API module intended to make configuration options generally
avaliable only at the sitewide level to be configurable and overridden by
individual "spaces" on a Drupal site. It has been described as:
- A way to make one Drupal site act like several sites
- A way to provide much more configurable, full-feature Organic Groups or user
homepages
- A generalized API for contextual configuration
Requirements & compatibility
---------------------------
Use of spaces requires
- CTools: http://drupal.org/project/ctools
- Features: http://drupal.org/project/features
However, you will want the following modules in order to build or use reasonably
cool features with Spaces and take advantage of most space types:
- PURL: http://drupal.org/project/purl
- Context 3.x: http://drupal.org/project/context
- Strongarm 2.x: http://drupal.org/project/strongarm
- Views 2.x: http://drupal.org/project/views
Out of the box, Spaces provides integration modules for:
- User: `spaces_user`
- Taxonomy: `spaces_taxonomy`
- OG: `spaces_og`
Coming from Spaces 2.x?
-----------------------
The Spaces 3.x branch makes significant departures from many of the concepts
in the 2.x branch. Here is a non-exhaustive list of important changes:
- Removed strict PURL dependency. Spaces can now be made active through means
other than a PURL provider (see `spaces_user_init()`).
- Usage of CTools plugins API and export API.
- `spaces_customizers()` and `spaces_settings()` have been replaced with a
general configuration override system using object controllers (more on this
below).
If you are upgrading from Spaces 2.x, prepare for a rocky ride. Update scripts
are included to migrate as cleanly as possible from 2.x to 3.x but any custom
settings you have created will need to be managed manually. The update scripts
leave the `spaces_settings` table intact for this reason.
Here is a rough list of steps to consider when upgrading from 2.x to 3.x:
1. Backup everything.
2. Run `update.php`.
3. Upgrade your features to Context 3, Strongarm 2, etc.
4. Have you implemented any custom 2.x space types? You need to migrate them,
see `API.txt` and `spaces.api.php`.
5. Have you implemented any custom 2.x spaces settings? You need to migrate
them to use standard Drupal variables and `system_settings_form()` (see
the `features_test` module for an example).
6. Have you implemented any custom 2.x spaces customizers? The concept may not
transfer cleanly to Spaces 3, or if it does, it will probably make the most
sense as a custom controller.
Core concept behind Spaces 3
----------------------------
Spaces 3 has been built outwards from the basic idea that it should be possible
for a "space" to override the values of a Drupal object that would otherwise
have a single, sitewide value. For our purposes
A space is a configuration environment that is triggered or made active by
some condition. For example, a "user space" might be made active when
viewing a user's profile. Once that user space is active, any customization
that user has made override sitewide values.
An object is a Drupal site building or configuration structure. Examples
include variables, contexts and views. Not included: nodes, users, taxonomy
terms, other "content".
Let's first look at storage of overridden values. Spaces stores all of its
overrides in the `spaces_overrides` table. Here is a sample row:
+------+----+-------------+------------------+------------------------+
| type | id | object_type | object_id | value |
+------+----+-------------+------------------+------------------------+
| og | 14 | variable | spaces_preset_og | s:13:"private_group"; |
+------+----+-------------+------------------+------------------------+
This row describes an overridde when a certain Organic Group (node 14) is
active. In particular, the variable `spaces_preset_og` has the value
`private_group` when this space is active. More generally, `spaces_overrides`
can store any value to override the default of an object, described by
(`object_type`, `object_id`), for any space, described by (`type`, `id`).
In practice, this means that when node 14 is active
variable_get('spaces_preset_og', NULL);
// returns "private_group"
While when node 14 is not active
variable_get('spaces_preset_og', NULL);
// returns NULL or sitewide value
Controllers & contextuality
---------------------------
The example above shows that when a space is active you need to change some
basic assumptions about how Drupal works. In particular, spaces introduces
contextuality to settings and configuration.
Per-space overrides are handled by controllers. Controllers are CTools plugins
that manage retrieval and storage of overrides for a given object type. For
example, spaces comes with a variable controller and context controller. Each
controller should interface with its object's API at a retrieval point and at
a storage or save point.
+-------------------------------------+-----------------------------------+
|Drupal integration point |Controller method |
+-------------------------------------+-----------------------------------+
|hook_context_reaction_fetch_alter() |$space->controllers->context->get()|
|spaces_form_context_ui_editor_alter()|$space->controllers->context->set()|
+-------------------------------------+-----------------------------------+
Whenever a context's reaction value is fetched, the context controller's `get()`
method is invoked retrieving a value specific to the active space.
The controller's save integration point is triggered through a custom submit
handler to the context editor form through a `hook_form_alter()`.
Currently, our rule of thumb is that while retrieval may be contextual, actual
save API functions should not be overridden. In general, you should always be
able to retrieve and save the original values of an object in addition to
manipulating space overrides from the API.
Presets, levels of configuration
--------------------------------
Spaces presets are sets of configuration that apply to whole sets of spaces. For
example you may not want to make the same set of customizations for every new
user you add to the site. You can use a preset like "member" or "guest" to
capture a variety of settings and have new users use one of the presets.
With presets in the picture, `variable_get('foo', NULL)` can actually return one
of three possible values when a space is active:
1. `space`: is the override value for the active space. If the active space has
saved an override for `foo`, this is what you will get.
2. `preset` is the override value for the preset. If the active space has not
saved a value for `foo` the variable controller will fall back to the preset
if it has a value for `foo`.
3. `original` is the sitewide, canonical value for `foo`. If neither the space
nor the preset have an override for `foo`, you will get the sitewide value
like a call to `variable_get()` when no spaces are active.
This cascading of values applies to all object types with a spaces controller.
**Aside**
This architecture *strongly* implies that it could or should be possible to
stack configuration overrides n levels rather than the current fixed number. In
such a scenario, presets would themselves become stacked spaces, and the picture
would become even simpler:
- Fixed stacking model (where > can be read as "inherits from"):
space > preset > original
- Arbitrary stacking model (where space 0 is the preset space)
space n > space n-1 > ... > space 1 > space 0 > original
This model is very attractive but requires some serious study before it can be
realized. Spaces 3 currently implements the fixed stacking model.
Managing and editing presets
----------------------------
The Spaces UI module allow provides the facility to manage presets. This
includes creating new presets, reverting overrides and editing preset metadata.
There is not a single UI for creating and editing all the elements of a
spaces preset. Presets can contain many settings about which the spaces module
actually knows nearly nothing, making it impossible to provide a useful
interface. Presets are meant to be edited through actual instance of a space.
For example, to change the features a preset has enabled you would:
1. Goto, or create, a space which uses the preset you wish to change.
2. Set the space to have the desired set of enabled feature.
3. Goto the "overrides" page (generally rendered as a tab) for the space.
4. Click the checkbox for the `space_menu_items` variable.
5. Click "Save to preset".
At this point any new groups created with this same preset will have the
configuration you've just specified. If the preset you've edited is provided
in code the interface at "admin > site building > spaces" (`admin/build/space`)
will show them as overriden.
Other functionality
-------------------
There is quite a bit of functionality in Spaces that does not fit neatly into
the picture of each space as a "configuration override environemnt." This
functionality has survived to support the users and code implemented around
existing user stories that spaces currently serves. In the future the
functionality may be further abstracted out so that Spaces can play a much more
minimal and possibly flexible role.
1. Features can be set to a state per space (defaults to enabled/disabled, but
overridable by extending classes) that determine their behavior within a space.
In particular, features tend to hide or show menu items, alter access to parts
of the menu tree, etc.
2. Access to a space has several levels - space types can control degrees of
admin access, feature access and basic access to a space.
3. Spaces can determine routing of certain pages - for example, some nodes may
only be viewed when a certain space is active or a certain administrative page
may drop all active spaces.
Note that none of these user stories are necessarily implied by the
configuration override framework introduced above.
Features
--------
Spaces integrates with the Features built using the Features module. Features
that are compatible with spaces must declare themselves as such by including the
`spaces[types]` key in their `.info` file.
For example a "MyBlog" module may include the following snippet in
`myblog.info` to declare that is is spaces-enabled;
name = "MyBlog"
package = "Features"
...
spaces[types] = "all"
`spaces[types]` may be declared to be one of the following values:
- `all` indicates that this feature is compatible with all space types.
- an array of space types where this feature may be enabled. Note that you may
also include the faux space type `site` in this array, indicating that this
feature may be enabled even and/or possibly only when no spaces are active.
For example, if my feature `my_cool_gallery` should only be available when
outside of any spaces, I would use the following entry:
spaces[types][] = "site"
If it can be enabled both outside of any spaces and inside of user spaces, but
not group spaces, I could use:
spaces[types][] = "site"
spaces[types][] = "user"
Settings
--------
Settings can be defined for spaces features simply as Drupal variables by
implementing a `system_settings_form()` at `features/[my-feature]`. If spaces
finds such a page defined by your feature, it will expose it as a link in
the features form for any of the space types where the feature is enabled.
Creating your own space types or extending existing ones
--------------------------------------------------------
Please see `API.txt` for instructions on how you can create your own space types
or use your own class to extend or replace one of the existing classes.
Maintainers
-----------
- alex_b (Alex Barth)
- jmiccolis (Jeff Miccolis)
- yhahn (Young Hahn)
- Ian Ward
View source
-
- Spaces 3.x
- ----------
- Spaces is an API module intended to make configuration options generally
- avaliable only at the sitewide level to be configurable and overridden by
- individual "spaces" on a Drupal site. It has been described as:
-
- - A way to make one Drupal site act like several sites
- - A way to provide much more configurable, full-feature Organic Groups or user
- homepages
- - A generalized API for contextual configuration
-
-
- Requirements & compatibility
- ---------------------------
- Use of spaces requires
-
- - CTools: http://drupal.org/project/ctools
- - Features: http://drupal.org/project/features
-
- However, you will want the following modules in order to build or use reasonably
- cool features with Spaces and take advantage of most space types:
-
- - PURL: http://drupal.org/project/purl
- - Context 3.x: http://drupal.org/project/context
- - Strongarm 2.x: http://drupal.org/project/strongarm
- - Views 2.x: http://drupal.org/project/views
-
- Out of the box, Spaces provides integration modules for:
-
- - User: `spaces_user`
- - Taxonomy: `spaces_taxonomy`
- - OG: `spaces_og`
-
-
- Coming from Spaces 2.x?
- -----------------------
- The Spaces 3.x branch makes significant departures from many of the concepts
- in the 2.x branch. Here is a non-exhaustive list of important changes:
-
- - Removed strict PURL dependency. Spaces can now be made active through means
- other than a PURL provider (see `spaces_user_init()`).
- - Usage of CTools plugins API and export API.
- - `spaces_customizers()` and `spaces_settings()` have been replaced with a
- general configuration override system using object controllers (more on this
- below).
-
- If you are upgrading from Spaces 2.x, prepare for a rocky ride. Update scripts
- are included to migrate as cleanly as possible from 2.x to 3.x but any custom
- settings you have created will need to be managed manually. The update scripts
- leave the `spaces_settings` table intact for this reason.
-
- Here is a rough list of steps to consider when upgrading from 2.x to 3.x:
-
- 1. Backup everything.
- 2. Run `update.php`.
- 3. Upgrade your features to Context 3, Strongarm 2, etc.
- 4. Have you implemented any custom 2.x space types? You need to migrate them,
- see `API.txt` and `spaces.api.php`.
- 5. Have you implemented any custom 2.x spaces settings? You need to migrate
- them to use standard Drupal variables and `system_settings_form()` (see
- the `features_test` module for an example).
- 6. Have you implemented any custom 2.x spaces customizers? The concept may not
- transfer cleanly to Spaces 3, or if it does, it will probably make the most
- sense as a custom controller.
-
-
- Core concept behind Spaces 3
- ----------------------------
- Spaces 3 has been built outwards from the basic idea that it should be possible
- for a "space" to override the values of a Drupal object that would otherwise
- have a single, sitewide value. For our purposes
-
- A space is a configuration environment that is triggered or made active by
- some condition. For example, a "user space" might be made active when
- viewing a user's profile. Once that user space is active, any customization
- that user has made override sitewide values.
-
- An object is a Drupal site building or configuration structure. Examples
- include variables, contexts and views. Not included: nodes, users, taxonomy
- terms, other "content".
-
- Let's first look at storage of overridden values. Spaces stores all of its
- overrides in the `spaces_overrides` table. Here is a sample row:
-
- +------+----+-------------+------------------+------------------------+
- | type | id | object_type | object_id | value |
- +------+----+-------------+------------------+------------------------+
- | og | 14 | variable | spaces_preset_og | s:13:"private_group"; |
- +------+----+-------------+------------------+------------------------+
-
- This row describes an overridde when a certain Organic Group (node 14) is
- active. In particular, the variable `spaces_preset_og` has the value
- `private_group` when this space is active. More generally, `spaces_overrides`
- can store any value to override the default of an object, described by
- (`object_type`, `object_id`), for any space, described by (`type`, `id`).
-
- In practice, this means that when node 14 is active
-
- variable_get('spaces_preset_og', NULL);
- // returns "private_group"
-
- While when node 14 is not active
-
- variable_get('spaces_preset_og', NULL);
- // returns NULL or sitewide value
-
-
- Controllers & contextuality
- ---------------------------
- The example above shows that when a space is active you need to change some
- basic assumptions about how Drupal works. In particular, spaces introduces
- contextuality to settings and configuration.
-
- Per-space overrides are handled by controllers. Controllers are CTools plugins
- that manage retrieval and storage of overrides for a given object type. For
- example, spaces comes with a variable controller and context controller. Each
- controller should interface with its object's API at a retrieval point and at
- a storage or save point.
-
- +-------------------------------------+-----------------------------------+
- |Drupal integration point |Controller method |
- +-------------------------------------+-----------------------------------+
- |hook_context_reaction_fetch_alter() |$space->controllers->context->get()|
- |spaces_form_context_ui_editor_alter()|$space->controllers->context->set()|
- +-------------------------------------+-----------------------------------+
-
- Whenever a context's reaction value is fetched, the context controller's `get()`
- method is invoked retrieving a value specific to the active space.
-
- The controller's save integration point is triggered through a custom submit
- handler to the context editor form through a `hook_form_alter()`.
-
- Currently, our rule of thumb is that while retrieval may be contextual, actual
- save API functions should not be overridden. In general, you should always be
- able to retrieve and save the original values of an object in addition to
- manipulating space overrides from the API.
-
-
- Presets, levels of configuration
- --------------------------------
- Spaces presets are sets of configuration that apply to whole sets of spaces. For
- example you may not want to make the same set of customizations for every new
- user you add to the site. You can use a preset like "member" or "guest" to
- capture a variety of settings and have new users use one of the presets.
-
- With presets in the picture, `variable_get('foo', NULL)` can actually return one
- of three possible values when a space is active:
-
- 1. `space`: is the override value for the active space. If the active space has
- saved an override for `foo`, this is what you will get.
- 2. `preset` is the override value for the preset. If the active space has not
- saved a value for `foo` the variable controller will fall back to the preset
- if it has a value for `foo`.
- 3. `original` is the sitewide, canonical value for `foo`. If neither the space
- nor the preset have an override for `foo`, you will get the sitewide value
- like a call to `variable_get()` when no spaces are active.
-
- This cascading of values applies to all object types with a spaces controller.
-
- **Aside**
-
- This architecture *strongly* implies that it could or should be possible to
- stack configuration overrides n levels rather than the current fixed number. In
- such a scenario, presets would themselves become stacked spaces, and the picture
- would become even simpler:
-
- - Fixed stacking model (where > can be read as "inherits from"):
-
- space > preset > original
-
- - Arbitrary stacking model (where space 0 is the preset space)
-
- space n > space n-1 > ... > space 1 > space 0 > original
-
- This model is very attractive but requires some serious study before it can be
- realized. Spaces 3 currently implements the fixed stacking model.
-
-
- Managing and editing presets
- ----------------------------
- The Spaces UI module allow provides the facility to manage presets. This
- includes creating new presets, reverting overrides and editing preset metadata.
- There is not a single UI for creating and editing all the elements of a
- spaces preset. Presets can contain many settings about which the spaces module
- actually knows nearly nothing, making it impossible to provide a useful
- interface. Presets are meant to be edited through actual instance of a space.
-
- For example, to change the features a preset has enabled you would:
-
- 1. Goto, or create, a space which uses the preset you wish to change.
- 2. Set the space to have the desired set of enabled feature.
- 3. Goto the "overrides" page (generally rendered as a tab) for the space.
- 4. Click the checkbox for the `space_menu_items` variable.
- 5. Click "Save to preset".
-
- At this point any new groups created with this same preset will have the
- configuration you've just specified. If the preset you've edited is provided
- in code the interface at "admin > site building > spaces" (`admin/build/space`)
- will show them as overriden.
-
-
- Other functionality
- -------------------
- There is quite a bit of functionality in Spaces that does not fit neatly into
- the picture of each space as a "configuration override environemnt." This
- functionality has survived to support the users and code implemented around
- existing user stories that spaces currently serves. In the future the
- functionality may be further abstracted out so that Spaces can play a much more
- minimal and possibly flexible role.
-
- 1. Features can be set to a state per space (defaults to enabled/disabled, but
- overridable by extending classes) that determine their behavior within a space.
- In particular, features tend to hide or show menu items, alter access to parts
- of the menu tree, etc.
- 2. Access to a space has several levels - space types can control degrees of
- admin access, feature access and basic access to a space.
- 3. Spaces can determine routing of certain pages - for example, some nodes may
- only be viewed when a certain space is active or a certain administrative page
- may drop all active spaces.
-
- Note that none of these user stories are necessarily implied by the
- configuration override framework introduced above.
-
-
- Features
- --------
- Spaces integrates with the Features built using the Features module. Features
- that are compatible with spaces must declare themselves as such by including the
- `spaces[types]` key in their `.info` file.
-
- For example a "MyBlog" module may include the following snippet in
- `myblog.info` to declare that is is spaces-enabled;
-
- name = "MyBlog"
- package = "Features"
- ...
- spaces[types] = "all"
-
- `spaces[types]` may be declared to be one of the following values:
-
- - `all` indicates that this feature is compatible with all space types.
- - an array of space types where this feature may be enabled. Note that you may
- also include the faux space type `site` in this array, indicating that this
- feature may be enabled even and/or possibly only when no spaces are active.
-
- For example, if my feature `my_cool_gallery` should only be available when
- outside of any spaces, I would use the following entry:
-
- spaces[types][] = "site"
-
- If it can be enabled both outside of any spaces and inside of user spaces, but
- not group spaces, I could use:
-
- spaces[types][] = "site"
- spaces[types][] = "user"
-
-
- Settings
- --------
- Settings can be defined for spaces features simply as Drupal variables by
- implementing a `system_settings_form()` at `features/[my-feature]`. If spaces
- finds such a page defined by your feature, it will expose it as a link in
- the features form for any of the space types where the feature is enabled.
-
-
- Creating your own space types or extending existing ones
- --------------------------------------------------------
- Please see `API.txt` for instructions on how you can create your own space types
- or use your own class to extend or replace one of the existing classes.
-
-
- Maintainers
- -----------
- - alex_b (Alex Barth)
- - jmiccolis (Jeff Miccolis)
- - yhahn (Young Hahn)
- - Ian Ward