-
Notifications
You must be signed in to change notification settings - Fork 1
/
readonlymode.module
executable file
·440 lines (406 loc) · 15.1 KB
/
readonlymode.module
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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
<?php
/**
* @file
* The Read Ony Mode main module file.
*
* Read Only Mode provides an alternate to the built in 'Maintenance Mode' in
* Backdrop. Instead of displaying a static text file to users while the site
* is in maintenance mode, Read Only Mode will allow access (reading) of
* content while preventing the addition of new content (posting / submitting
* forms / etc).
*
* This allows the site to remain functional while maintenance is performed.
* This module also provides messaging to users and administrators to indicate
* that the site is in maintenance mode.
*/
/**
* Indicate the form has been blocked by readonlymode.
*/
define('READONLYMODE_FORM_DENIED', 0);
/**
* Indicate the form has been made view only by readonlymode.
*/
define('READONLYMODE_FORM_VIEWONLY', 1);
/**
* Indicate the form has allowed by readonlymode.
*/
define('READONLYMODE_FORM_ALLOWED', 2);
/**
* Implements hook_config_info().
*/
function readonlymode_config_info() {
return array(
'readonlymode.settings' => array(
'label' => t('Read Only Mode settings'),
'group' => t('Configuration'),
),
);
}
/**
* Implements hook_module_implements_alter().
*
* Make sure our form alter runs last.
*/
function readonlymode_module_implements_alter(&$implementations, $hook) {
if ($hook == 'form_alter') {
$group = $implementations['readonlymode'];
unset($implementations['readonlymode']);
$implementations['readonlymode'] = $group;
}
}
/**
* Implements hook_permission().
*
* Create permission to allow access to forms while in read only mode
*/
function readonlymode_permission() {
return array(
'readonlymode access forms' => array(
'title' => t("Access all forms while in 'Read Only Mode'"),
),
);
}
/**
* Implements hook_form_alter().
*
* Permit posting of content.
*/
function readonlymode_form_alter(&$form, $form_state, $form_id) {
$mode = _readonlymode_form_check($form_id);
if ($mode != READONLYMODE_FORM_ALLOWED) {
// Check whether the form is a fresh form being served to the user.
if (!isset($form_state['input']['form_token'])) {
// If a redirect URL is set, then we redirect to it.
if (!empty($url = config_get('readonlymode.settings', 'site_readonly_url'))) {
backdrop_goto($url);
}
else {
switch ($mode) {
case READONLYMODE_FORM_VIEWONLY:
// Replace FAPI #after_build handlers with our readonly form.
$form['#after_build'] = array('readonlymode_readonly_form');
break;
case READONLYMODE_FORM_DENIED:
// Replace FAPI #after_build handlers with our block form.
$form['#after_build'] = array('readonlymode_block_form');
break;
}
}
}
}
$path = current_path();
if (user_access('readonlymode access forms') && state_get('site_readonly', FALSE) && $path != 'admin/config/development/maintenance') {
backdrop_set_message(t('The site is currently set to <a href="@link">read-only</a>. Content creation and editing is disabled for all users without the "Access all forms while in Read Only Mode" permission.', array('@link' => url('admin/config/development/maintenance'))), 'warning', FALSE);
}
$form['#validate'][] = 'readonlymode_check_form_validate';
}
/**
* After build callback to block the form.
*
* @see readonlymode_form_alter()
*/
function readonlymode_block_form($form) {
// Remove all elements of the form.
foreach (element_children($form) as $key) {
if (!in_array($key, array('form_id', 'form_token', 'form_build_id'))) {
$form[$key]['#access'] = FALSE;
}
}
$form['readonly_notice'] = array(
'#markup' => _readonlymode_notice(),
'#prefix' => '<div class="messages warning">',
'#suffix' => '</div>',
);
return $form;
}
/**
* After build callback to render read only forms.
*/
function readonlymode_readonly_form($element) {
if (isset($element['#type'])) {
if (in_array($element['#type'], array('button', 'image_button', 'submit'))) {
$element['#access'] = FALSE;
}
else {
$element['#attributes']['readonly'] = 'readonly';
$element['#attributes']['disabled'] = 'disabled';
}
}
// Recurse for any child elements.
foreach (element_children($element) as $key) {
$element[$key] = readonlymode_readonly_form($element[$key]);
}
return $element;
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Alter the Site Maintenance form
*/
function readonlymode_form_system_site_maintenance_mode_alter(&$form, &$form_state) {
$form['read_only'] = array(
'#title' => t('Read Only Mode'),
'#type' => 'fieldset',
'#weight' => -100,
'#config' => 'readonlymode.settings',
'#collapsible' => TRUE,
'#collapsed' => state_get('site_readonly', FALSE) ? FALSE : TRUE,
);
$form['read_only']['site_readonly'] = array(
'#type' => 'checkbox',
'#title' => t('Read only mode'),
'#description' => t('Put the site in read-only mode. When set to "Read-only", all content moderation (add/edit) will be impossible.'),
'#weight' => 0,
'#default_value' => state_get('site_readonly', FALSE),
);
// Message configuration is in a collapsed fieldset
// so that it doesn't clutter the display.
$form['read_only']['messages'] = array(
'#title' => t('Messages and redirects'),
'#type' => 'fieldset',
'#description' => t('Configure the redirect URL and messages to display to users while the site is in Read Only Mode.'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['read_only']['messages']['site_readonly_message'] = array(
'#type' => 'textarea',
'#title' => t('Read Only Mode warning'),
'#description' => t('This warning will be displayed when viewing a page that has a blocked form while in Read Only Mode.'),
'#default_value' => _readonlymode_notice(),
'#rows' => 3,
'#required' => TRUE,
);
$form['read_only']['messages']['site_readonly_message_form_not_saved'] = array(
'#type' => 'textarea',
'#title' => t('Form submission error'),
'#description' => t('This error will be displayed when a blocked form is submitted while in Read Only Mode. This scenario occurs when a user starts filling out a form during normal site operation and then attempts to submit the form after Read Only Mode has been enabled.'),
'#default_value' => _readonlymode_notice_form_not_saved(),
'#rows' => 3,
'#required' => TRUE,
);
$form['read_only']['messages']['site_readonly_url'] = array(
'#type' => 'textfield',
'#title' => t('Redirect path'),
'#description' => t('When given, Backdrop will redirect the user to this URL when a user tries to add/edit content instead of displaying the message above.'),
'#default_value' => config_get('readonlymode.settings', 'site_readonly_url'),
);
// Allowed forms configuration is in a collapsed fieldset
// so that it doesn't clutter the display.
$form['read_only']['forms'] = array(
'#title' => t('Allowed forms'),
'#type' => 'fieldset',
'#description' => t('Configure which forms will be exempt from restriction when in read-only mode.'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['read_only']['forms']['site_readonly_forms_allowed'] = array(
'#type' => 'textarea',
'#title' => t('Forms that can be submitted'),
'#description' => t("These forms are not restricted when in read only mode. Enter one form id per line. You may use the wildcard character '*' to use loose matches. For example: webform* will match all webforms. Note that the following forms will always be allowed: %allowed_forms.", array(
'%allowed_forms' => implode(', ', readonlymode_default_forms_allowed()))),
'#default_value' => config_get('readonlymode.settings', 'site_readonly_forms_allowed'),
);
$form['read_only']['forms']['site_readonly_forms_viewonly'] = array(
'#type' => 'textarea',
'#title' => t('Forms that can be viewed'),
'#description' => t("These forms are allowed to be viewed but will not accept form submissions. Enter one form id per line. You may use the wildcard character '*' to use loose matches. For example: webform* will match all webforms. Note that the following forms will always be allowed: %allowed_forms.", array(
'%allowed_forms' => implode(', ', readonlymode_default_forms_viewonly()))),
'#default_value' => config_get('readonlymode.settings', 'site_readonly_forms_viewonly'),
);
$form['#validate'][] = 'readonlymode_form_validate_url';
$form['#submit'][] = 'read_only_form_submit';
}
/**
* Implements hook_form_validate().
*/
function readonlymode_form_validate_url(&$form, $form_state) {
if ($path = $form_state['values']['site_readonly_url']) {
$item = menu_get_item($path);
if (!$item || !$item['access']) {
form_set_error('site_readonly_url', t('The path %link_path is either invalid or you do not have access to it.', array('%link_path' => $path)));
}
}
}
/**
* Additional form submit function for read_only form.
*/
function read_only_form_submit($form, &$form_state) {
state_set('site_readonly', (bool) $form_state['values']['site_readonly']);
$config = config('readonlymode.settings');
$config->set('site_readonly_forms_allowed', $form_state['values']['site_readonly_forms_allowed']);
$config->set('site_readonly_forms_viewonly', $form_state['values']['site_readonly_forms_viewonly']);
$config->set('site_readonly_message_form_not_saved', $form_state['values']['site_readonly_message_form_not_saved']);
$config->set('site_readonly_message', $form_state['values']['site_readonly_message']);
$config->set('site_readonly_url', $form_state['values']['site_readonly_url']);
$config->save();
}
/**
* Returns the custom readonlymode warning.
*
* Used when a page with a blocked form is viewed.
*/
function _readonlymode_notice() {
$configured_message = config_get('readonlymode.settings', 'site_readonly_message');
if (empty($configured_message)) {
$configured_message = t('@site_name is currently in maintenance. During maintenance it is not possible to change site content (like comments, pages and users).', array(
'@site_name' => config_get('system.core', 'site_name'),
));
}
return $configured_message;
}
/**
* Return the custom readonlymode error.
*
* Used when a user attempts to submit a blocked form.
*/
function _readonlymode_notice_form_not_saved() {
$configured_message = config_get('readonlymode.settings', 'site_readonly_message_form_not_saved');
if (empty($configured_message)) {
$configured_message = t('Data not saved. @site_name is currently in maintenance. During maintenance it is not possible to change content. Please make a note of your changes and try again later.', array(
'@site_name' => config_get('system.core', 'site_name'),
));
}
return $configured_message;
}
/**
* Internal handler to check whether this form is to be restricted.
*
* @param int $form_id
* The ID of the form we want to check.
*
* @return int
* - READONLYMODE_FORM_DENIED: The form has been blocked.
* - READONLYMODE_FORM_VIEWONLY: The form should be view only.
* - READONLYMODE_FORM_ALLOWED: The form has been allowed.
*/
function _readonlymode_form_check($form_id) {
// Get hold of our cache.
$cache = &backdrop_static(__FUNCTION__, NULL);
// If NULL, we need to check the non-form specific options.
if (!isset($cache)) {
// If not in read-only mode, allow the form.
if (!state_get('site_readonly', FALSE)) {
$cache = READONLYMODE_FORM_ALLOWED;
}
// Admins can access all forms.
elseif (user_access('readonlymode access forms')) {
$cache = READONLYMODE_FORM_ALLOWED;
}
// Indicate that we have a more complicated answer.
else {
$cache = array();
}
}
// If we have a scalar value, we don't need to check specific forms.
if (!is_array($cache)) {
return $cache;
}
// Look at form specific level if required.
if (!isset($cache[$form_id])) {
// Is the form in the list of default forms? Then allow access.
if (in_array($form_id, readonlymode_default_forms_allowed())) {
$cache[$form_id] = READONLYMODE_FORM_ALLOWED;
}
// Is the form in the list of read-only forms? Then allow access.
elseif (in_array($form_id, readonlymode_default_forms_viewonly())) {
$cache[$form_id] = READONLYMODE_FORM_VIEWONLY;
}
// Check if the form is in the custom list of allowed forms. If so, allow.
elseif (_readonlymode_form_list_check($form_id, config_get('readonlymode.settings', 'site_readonly_forms_allowed'))) {
$cache[$form_id] = READONLYMODE_FORM_ALLOWED;
}
// Check if the form is in the custom list of allowed read-only forms.
elseif (_readonlymode_form_list_check($form_id, config_get('readonlymode.settings', 'site_readonly_forms_viewonly'))) {
$cache[$form_id] = READONLYMODE_FORM_VIEWONLY;
}
// Otherwise we need to deny access.
else {
$cache[$form_id] = READONLYMODE_FORM_DENIED;
}
}
return $cache[$form_id];
}
/**
* Check for form_id is a given list.
*
* $list is a string of form ids separated by newlines.
* Returns TRUE is matched, FALSE otherwise.
*/
function _readonlymode_form_list_check($form_id, $list) {
$l = preg_split('/(\r\n|\n|\r)/', $list);
foreach ($l as $word) {
// Skip empty words.
if (empty($word)) {
continue;
}
$word = str_replace('*', '.*', $word);
if (preg_match('/^' . $word . '$/', $form_id) === 1) {
return TRUE;
}
}
return FALSE;
}
/**
* Our base forms allowed during read-only mode.
*/
function readonlymode_default_forms_allowed() {
$default_forms_allowed = array(
'read_only',
'user_pass',
'user_login',
'user_login_block',
'search_form',
'search_block_form',
'system_modules',
'system_site_maintenance_mode',
'views_exposed_form',
);
backdrop_alter('readonlymode_default_forms_allowed', $default_forms_allowed);
return $default_forms_allowed;
}
/**
* Our base forms allowed for view during read-only mode.
*/
function readonlymode_default_forms_viewonly() {
$default_forms_viewonly = array(
'comment_admin_overview',
'views_form_node_admin_content_page',
'views_form_user_admin_page',
);
backdrop_alter('readonlymode_default_forms_viewonly', $default_forms_viewonly);
return $default_forms_viewonly;
}
/**
* Validate handler for checking whether the form submission is occurring.
*
* Sets an form error when read-only mode is enabled.
*/
function readonlymode_check_form_validate($form, &$form_state) {
// Check for read-only mode, whether we are allowed this form.
if (_readonlymode_form_check($form['form_id']['#value']) != READONLYMODE_FORM_ALLOWED) {
form_set_error('', _readonlymode_notice_form_not_saved());
}
}
/**
* Implements hook_block_info().
*/
function readonlymode_block_info() {
$blocks = array(
'readonlymode_block' => array(
'info' => t('Read only mode block'),
),
);
return $blocks;
}
/**
* Implements hook_block_view().
*/
function readonlymode_block_view($delta = '') {
// Block will be displayed only site is in readonly mode.
if ($delta == 'readonlymode_block' && state_get('site_readonly', FALSE) == TRUE) {
$block = array();
$block['subject'] = t('Read only mode block');
$block['content'] = _readonlymode_notice();
return $block;
}
}