1
|
<?php
|
2
|
|
3
|
/**
|
4
|
* @file
|
5
|
* Import/Export functionality provided by Flag module.
|
6
|
*/
|
7
|
|
8
|
/**
|
9
|
* Export a flag to code.
|
10
|
*
|
11
|
* @param array $flags
|
12
|
* An array of flag objects, or flag name.
|
13
|
* @param string $module
|
14
|
* Optional. The name of the module that will be created if exporting to use
|
15
|
* in hook_flag_default_flags().
|
16
|
*/
|
17
|
function flag_export_flags($flags = array(), $module = '', $indent = '') {
|
18
|
// For features_var_export() (optional).
|
19
|
module_load_include('inc', 'features', 'features.export');
|
20
|
$output = $indent . '$flags = array();' . "\n";
|
21
|
foreach ($flags as $item) {
|
22
|
if (is_object($item)) {
|
23
|
$flag = $item;
|
24
|
}
|
25
|
else {
|
26
|
// We got just the flag name, for example from the features
|
27
|
// implementation.
|
28
|
if (!($flag = flag_load($item, TRUE))) {
|
29
|
continue;
|
30
|
}
|
31
|
}
|
32
|
if (!$flag->is_compatible()) {
|
33
|
drupal_set_message(t('Could not export flag %flag-name: Your flag was created by a different version of the Flag module than is now being used.', array('%flag-name' => $flag->name)), 'error');
|
34
|
continue;
|
35
|
}
|
36
|
|
37
|
$flag->api_version = FLAG_API_VERSION;
|
38
|
$new_flag = (array) $flag;
|
39
|
|
40
|
if (!empty($module)) {
|
41
|
// Even though Flag adds the module name itself later, we add the module
|
42
|
// name here for reference by other modules (such as Features).
|
43
|
$new_flag['module'] = $module;
|
44
|
// Lock the flag name, as is normally desired by modules using
|
45
|
// hook_flag_default_flags(), and needed by Features.
|
46
|
$new_flag['locked'] = array('name');
|
47
|
}
|
48
|
// Allow other modules to change the exported flag.
|
49
|
drupal_alter('flag_export', $new_flag);
|
50
|
|
51
|
// Remove properties we don't export.
|
52
|
$unset_properties = array(
|
53
|
// Remove the flag ID.
|
54
|
'fid',
|
55
|
// The name is emitted as the key for the array.
|
56
|
'name',
|
57
|
// The entity info is just used as helper data.
|
58
|
'entity_info',
|
59
|
// Remove roles.
|
60
|
'roles',
|
61
|
// Remove errors.
|
62
|
'errors',
|
63
|
);
|
64
|
foreach ($unset_properties as $property) {
|
65
|
unset($new_flag[$property]);
|
66
|
}
|
67
|
|
68
|
$output .= $indent . '// Exported flag: "' . check_plain($flag->get_title()) . '"' . ".\n";
|
69
|
$output .= $indent . '$flags[\'' . $flag->name . '\'] = ' . (function_exists('features_var_export') ? features_var_export($new_flag, $indent) : var_export($new_flag, TRUE)) . ";\n";
|
70
|
}
|
71
|
$output .= $indent . 'return $flags;' . "\n";
|
72
|
return $output;
|
73
|
}
|
74
|
|
75
|
/**
|
76
|
* Form to import a flag.
|
77
|
*/
|
78
|
function flag_import_form() {
|
79
|
$form = array();
|
80
|
|
81
|
$form['import'] = array(
|
82
|
'#title' => t('Flag import code'),
|
83
|
'#type' => 'textarea',
|
84
|
'#default_value' => '',
|
85
|
'#rows' => 15,
|
86
|
'#required' => TRUE,
|
87
|
'#description' => t('Paste the code from a <a href="@export-url">flag export</a> here to import it into you site. Flags imported with the same name will update existing flags. Flags with a new name will be created.', array('@export-url' => url(FLAG_ADMIN_PATH . '/export'))),
|
88
|
);
|
89
|
$form['submit'] = array(
|
90
|
'#value' => t('Import'),
|
91
|
'#type' => 'submit',
|
92
|
);
|
93
|
|
94
|
return $form;
|
95
|
}
|
96
|
|
97
|
/**
|
98
|
* Validate handler; Import a flag.
|
99
|
*/
|
100
|
function flag_import_form_validate($form, &$form_state) {
|
101
|
$flags = array();
|
102
|
ob_start();
|
103
|
eval($form_state['values']['import']);
|
104
|
ob_end_clean();
|
105
|
|
106
|
if (!isset($flags) || !is_array($flags)) {
|
107
|
form_set_error('import', t('A valid list of flags could not be found in the import code.'));
|
108
|
return;
|
109
|
}
|
110
|
|
111
|
// Create the flag object.
|
112
|
foreach ($flags as $flag_name => $flag_info) {
|
113
|
// Backward compatibility: old exported flags have their names in $flag_info
|
114
|
// instead, so we use the += operator to not overwrite it.
|
115
|
$flag_info += array(
|
116
|
'name' => $flag_name,
|
117
|
);
|
118
|
$new_flag = flag_flag::factory_by_array($flag_info);
|
119
|
|
120
|
// Give new flags with the same name a matching FID, which tells Flag to
|
121
|
// update the existing flag, rather than creating a new one.
|
122
|
if ($existing_flag = flag_get_flag($new_flag->name)) {
|
123
|
$new_flag->fid = $existing_flag->fid;
|
124
|
}
|
125
|
|
126
|
if ($errors = $new_flag->validate()) {
|
127
|
$message = t('The import of the %flag flag failed because the following errors were encountered during the import:', array('%flag' => $new_flag->name));
|
128
|
$message_errors = array();
|
129
|
foreach ($errors as $field => $field_errors) {
|
130
|
foreach ($field_errors as $error) {
|
131
|
$message_errors[] = $error['message'];
|
132
|
}
|
133
|
}
|
134
|
form_set_error('import', $message . theme('item_list', array('items' => $message_errors)));
|
135
|
}
|
136
|
else {
|
137
|
// Save the new flag for the submit handler.
|
138
|
$form_state['flags'][] = $new_flag;
|
139
|
}
|
140
|
}
|
141
|
}
|
142
|
|
143
|
/**
|
144
|
* Submit handler; Import a flag.
|
145
|
*/
|
146
|
function flag_import_form_submit($form, &$form_state) {
|
147
|
module_load_include('inc', 'flag', 'includes/flag.admin');
|
148
|
|
149
|
// Build up values for the cache clear.
|
150
|
$entity_types = array();
|
151
|
$new = FALSE;
|
152
|
|
153
|
foreach ($form_state['flags'] as $flag) {
|
154
|
$flag->save();
|
155
|
if (!empty($flag->status)) {
|
156
|
$flag->enable();
|
157
|
}
|
158
|
if ($flag->is_new) {
|
159
|
drupal_set_message(t('Flag @name has been imported.', array('@name' => $flag->name)));
|
160
|
$new = TRUE;
|
161
|
}
|
162
|
else {
|
163
|
drupal_set_message(t('Flag @name has been updated.', array('@name' => $flag->name)));
|
164
|
}
|
165
|
$entity_types[] = $flag->entity_type;
|
166
|
}
|
167
|
_flag_clear_cache($entity_types, $new);
|
168
|
|
169
|
$form_state['redirect'] = FLAG_ADMIN_PATH;
|
170
|
}
|
171
|
|
172
|
/**
|
173
|
* Export a flag and display it in a form.
|
174
|
*/
|
175
|
function flag_export_form($form, &$form_state, $flag = NULL) {
|
176
|
// If we were passed a flag, use it as the list of flags to export.
|
177
|
if ($flag) {
|
178
|
$flags = array($flag);
|
179
|
}
|
180
|
|
181
|
// Display a list of flags to export.
|
182
|
if (!isset($flags)) {
|
183
|
if (isset($form_state['values']['flags'])) {
|
184
|
$flags = array();
|
185
|
foreach ($form_state['values']['flags'] as $flag_name) {
|
186
|
if ($flag_name && $flag = flag_get_flag($flag_name)) {
|
187
|
$flags[] = $flag;
|
188
|
}
|
189
|
}
|
190
|
}
|
191
|
else {
|
192
|
$form['flags'] = array(
|
193
|
'#type' => 'checkboxes',
|
194
|
'#title' => t('Flags to export'),
|
195
|
'#options' => drupal_map_assoc(array_keys(flag_get_flags())),
|
196
|
'#description' => t('Exporting your flags is useful for moving flags from one site to another, or when including your flag definitions in a module.'),
|
197
|
);
|
198
|
$form['submit'] = array(
|
199
|
'#type' => 'submit',
|
200
|
'#value' => t('Export'),
|
201
|
);
|
202
|
}
|
203
|
}
|
204
|
|
205
|
if (isset($flags)) {
|
206
|
$code = flag_export_flags($flags);
|
207
|
|
208
|
// Link to the Features page if module is present, otherwise link to the
|
209
|
// Drupal project page.
|
210
|
$features_link = module_exists('features') ? url('admin/build/features') : url('http://drupal.org/project/features');
|
211
|
|
212
|
$form['export'] = array(
|
213
|
'#type' => 'textarea',
|
214
|
'#title' => t('Flag exports'),
|
215
|
'#description' => t('Use the exported code to later <a href="@import-flag">import</a> it. Exports can be included in modules using <a href="http://drupal.org/node/305086#default-flags">hook_flag_default_flags()</a> or using the <a href="@features-url">Features</a> module.', array('@import-flag' => url(FLAG_ADMIN_PATH . '/import'), '@features-url' => $features_link)),
|
216
|
'#value' => $code,
|
217
|
'#rows' => 15,
|
218
|
);
|
219
|
}
|
220
|
|
221
|
return $form;
|
222
|
}
|
223
|
|
224
|
/**
|
225
|
* Submit handler; Rebuild the export form after the list of flags has been set.
|
226
|
*/
|
227
|
function flag_export_form_submit($form, &$form_state) {
|
228
|
$form_state['rebuild'] = TRUE;
|
229
|
}
|
230
|
|
231
|
/**
|
232
|
* Page for displaying an upgrade message and export form for Flag 1.x flags.
|
233
|
*/
|
234
|
function flag_update_page($flag) {
|
235
|
if ($flag->is_compatible()) {
|
236
|
drupal_set_message(t('The flag %name is already up-to-date with the latest Flag API and does not need upgrading.', array('%name' => $flag->name)));
|
237
|
drupal_goto(FLAG_ADMIN_PATH);
|
238
|
}
|
239
|
|
240
|
drupal_set_message(t('The flag %name is currently using the Flag API version @version, which is not compatible with the current version of Flag. You can upgrade this flag by pasting the below code into <em>@module_flag_default_flags()</em> function in the @module.module file.', array('%name' => $flag->name, '@version' => $flag->api_version, '@module' => $flag->module)), 'warning');
|
241
|
|
242
|
flag_update_export($flag);
|
243
|
|
244
|
return drupal_get_form('flag_export_form', $flag);
|
245
|
}
|
246
|
|
247
|
/**
|
248
|
* Update a flag before export.
|
249
|
*
|
250
|
* @param flag_flag $flag
|
251
|
* The flag object passed by reference.
|
252
|
*/
|
253
|
function flag_update_export(&$flag) {
|
254
|
// Set the API version to 1 by default: version 1 did not explicitly define
|
255
|
// the API version.
|
256
|
if (empty($flag->api_version)) {
|
257
|
$flag->api_version = 1;
|
258
|
}
|
259
|
|
260
|
// Get all our update classes.
|
261
|
// This is not terribly graceful, but the alternative is declaring our classes
|
262
|
// explicitly, or registering them with the Drupal autoloader and then running
|
263
|
// a database query, which seems a waste of space given we only ever need
|
264
|
// these here.
|
265
|
$classes = get_declared_classes();
|
266
|
$update_handlers = array();
|
267
|
foreach ($classes as $class) {
|
268
|
// Any class whose name is of the form 'FlagUpdate_foo' is one of ours, we
|
269
|
// assume. Should this prove problematic, we can add use of reflection here.
|
270
|
if (substr($class, 0, 11) == 'FlagUpdate_') {
|
271
|
// @todo: change this to work with the static class when we drop support
|
272
|
// for PHP 5.2: see commit d5b517.
|
273
|
$update_handler = new $class();
|
274
|
// Cast to string, as decimals as array keys seem to be rounded down to
|
275
|
// ints, WTF PHP?
|
276
|
$version = (string) $update_handler->old_api_version;
|
277
|
|
278
|
$update_handlers[$version] = $update_handler;
|
279
|
}
|
280
|
}
|
281
|
// Sort the classes by old version number.
|
282
|
uksort($update_handlers, 'version_compare');
|
283
|
|
284
|
// Work through each update handler.
|
285
|
foreach ($update_handlers as $old_api_version => $update_handler) {
|
286
|
// Skip update classes that are older than our current flag.
|
287
|
if (version_compare($old_api_version, $flag->api_version, '<')) {
|
288
|
continue;
|
289
|
}
|
290
|
|
291
|
// Run the update and change the API version on the flag.
|
292
|
$update_handler->update($flag);
|
293
|
$flag->api_version = $update_handler->new_api_version;
|
294
|
}
|
295
|
}
|
296
|
|
297
|
/**
|
298
|
* Flag update class for API 1 flags -> API 2.
|
299
|
*
|
300
|
* The class name after the prefix is immaterial, though we follow the Drupal
|
301
|
* system update convention whereby the number here is what we update to.
|
302
|
*/
|
303
|
class FlagUpdate_2 {
|
304
|
|
305
|
/**
|
306
|
* The API version this class updates a flag from.
|
307
|
*
|
308
|
* @todo: Change this to a class constant when we drop support for PHP 5.2.
|
309
|
*/
|
310
|
public $old_api_version = 1;
|
311
|
|
312
|
/**
|
313
|
* The API version this class updates a flag to.
|
314
|
*/
|
315
|
public $new_api_version = 2;
|
316
|
|
317
|
/**
|
318
|
* The update function for the flag.
|
319
|
*/
|
320
|
static function update(&$flag) {
|
321
|
if (isset($flag->roles) && !isset($flag->roles['flag'])) {
|
322
|
$flag->roles = array(
|
323
|
'flag' => $flag->roles,
|
324
|
'unflag' => $flag->roles,
|
325
|
);
|
326
|
}
|
327
|
}
|
328
|
}
|
329
|
|
330
|
/**
|
331
|
* Flag update class for API 2 flags -> API 3.
|
332
|
*/
|
333
|
class FlagUpdate_3 {
|
334
|
|
335
|
public $old_api_version = 2;
|
336
|
public $new_api_version = 3;
|
337
|
|
338
|
static function update(&$flag) {
|
339
|
// Change the content_type property to entity_type.
|
340
|
if (isset($flag->content_type)) {
|
341
|
$flag->entity_type = $flag->content_type;
|
342
|
unset($flag->content_type);
|
343
|
}
|
344
|
|
345
|
// We can't convert the flag roles data to user permissions at this point
|
346
|
// because the flag is disabled and hence hook_permission() doesn't see it
|
347
|
// to define its permissions.
|
348
|
// Instead, we copy it to import_roles, which the flag add form will handle
|
349
|
// on new flags (which this flag will behave as when it is re-enabled).
|
350
|
// @see flag_form()
|
351
|
if (isset($flag->roles)) {
|
352
|
$flag->import_roles = $flag->roles;
|
353
|
}
|
354
|
|
355
|
// Update show_on_teaser property to use new view mode settings.
|
356
|
if (!empty($flag->show_on_teaser)) {
|
357
|
$flag->show_in_links['teaser'] = TRUE;
|
358
|
unset($flag->show_on_teaser);
|
359
|
}
|
360
|
|
361
|
// Update show_on_page property to use new view mode settings.
|
362
|
if (!empty($flag->show_on_page)) {
|
363
|
$flag->show_in_links['full'] = TRUE;
|
364
|
unset($flag->show_on_page);
|
365
|
}
|
366
|
|
367
|
// Update show_on_comment and show_on_entity properties to use new view
|
368
|
// mode settings. Since the old logic was to show on all view modes, do
|
369
|
// that.
|
370
|
if (!empty($flag->show_on_entity) || !empty($flag->show_on_comment)) {
|
371
|
if ($entity_info = entity_get_info($flag->entity_type)) {
|
372
|
foreach ($entity_info['view modes'] as $view_mode => $value) {
|
373
|
$flag->show_in_links[$view_mode] = TRUE;
|
374
|
}
|
375
|
}
|
376
|
|
377
|
unset($flag->show_on_entity, $flag->show_on_comment);
|
378
|
}
|
379
|
}
|
380
|
}
|