summaryrefslogtreecommitdiffstats
path: root/README.txt
blob: 8e7e88d4fc66c1017ab8a57b98a62ea5beb5b4fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
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
267
268
269
270
271
272
273
274
275
276
277
278
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