$Id: README.txt,v 1.1.4.1.2.1.2.3 2009-12-22 21:08:44 yhahn Exp $ Spaces 3.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 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). 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: `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. `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`. `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. Note: 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. Features, access & routing -------------------------- 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.