-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathflag_form.module
540 lines (480 loc) · 19.1 KB
/
flag_form.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
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
<?php
/**
* @file
* Provides forms for editing and deleting a flagging.
*
* Random notes:
*
* - Although the module is named Flag Form, we use the prefix "flag_flagging_"
* for the form functions.
*
* - We use CRUD terminology wherever possible (e.g., 'create' and 'delete'
* instead of 'flag' and 'unflag'), because we're dealing with entities. For
* flag labels (e.g., the setting names in flag_form_flag_link_types()), we
* use the old 'flag'/'unflag' terminology.
*/
/**
* Implements hook_flag_link_types().
*/
function flag_form_flag_link_types() {
return array(
'form' => array(
'title' => t('Form'),
'description' => t('The user will be taken to a form to confirm his decision and possibly edit fields attached to the flag.'),
'options' => array(
'form_flagging_help' => '',
'form_flagging_button' => '',
'form_flagging_delete_button' => '',
'form_unflagging_help' => '',
'form_unflagging_button' => '',
// @todo: Discuss: should the following checkbox be on by default?
'form_unflag_link_leads_to_edit' => FALSE,
'form_interaction' => 'default',
),
),
);
}
/**
* Implements hook_flag_form_interactions().
*/
function flag_form_flag_form_interactions() {
return array(
'default' => array(
'title' => t('Default'),
'description' => t('Forms are displayed on their own page.'),
'weight' => -20,
)
);
}
/**
* Returns all the available interactions.
*/
function flag_form_interactions() {
$list = &drupal_static(__FUNCTION__, NULL);
if (!isset($list)) {
$list = module_invoke_all('flag_form_interactions');
uasort($list, 'drupal_sort_weight');
}
return $list;
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Injects out settings to the flag admin form.
*/
function flag_form_form_flag_form_alter(&$form) {
$flag = $form['#flag'];
// In some help texts we embed a link to the 'manage fields' tab. But we
// can only do this for flags which are already saved.
if (isset($flag->fid) && module_exists('field_ui')) {
$manage_fields_url = url('admin/structure/flags/manage/' . $flag->name . '/fields');
}
elseif (module_exists('help')) {
$manage_fields_url = url('admin/help/field');
}
else {
$manage_fields_url = url('admin/structure/flags');
}
$settings = array(
'#type' => 'fieldset',
'#title' => t('Options for the "Form" link type'),
'#description' => t('There are two forms: one used for flagging an item and editing possible <a href="@manage-fields-url">attached fields</a>, and one used for unflagging the item. You may configure the two forms below.', array('@manage-fields-url' => $manage_fields_url)),
'#id' => 'link-options-form',
'#weight' => 22,
);
// To prevent typos, we do this mechanically.
$labels = array(
'form_flagging_help' => array(
'#title' => t('Flagging guidelines'),
'#description' => t('This short help text will be displayed at the top of the form. For example, you may use it to instruct the user in filling out the various fields possibly attached to the flag. Or you may form it as a question: "Are you sure you want to flag this content?"'),
'#type' => 'textarea',
'#rows' => 3,
),
'form_flagging_button' => array(
'#title' => t('Confirmation button'),
'#description' => t('The label to appear on the submit button. Leave empty to use the "Flag link" text. You may change it here; e.g., to "Save", or "Yes" (the latter if your guidelines are worded as a question).'),
),
'form_flagging_delete_button' => array(
'#title' => t('Button leading to the unflagging form'),
'#description' => t('The label to appear on the button leading to the unflagging form. Leave empty to use the "Unflag link" text. You may change it here; e.g., to "Delete".'),
),
'form_unflagging_help' => array(
'#title' => t('Unflagging guidelines'),
'#description' => t('This short help text will be displayed at the top of the form. For example, you may form it as a question: "Are you sure you want to unflag this content?"'),
'#type' => 'textarea',
'#rows' => 3,
),
'form_unflagging_button' => array(
'#title' => t('Confirmation button'),
'#description' => t('The label to appear on the submit button. Leave empty to use the "Unflag link" text. You may change it here; e.g., to "Delete", or "Yes" (the later if your guidelines are worded as a question).'),
),
);
foreach ($labels as $name => $element) {
$fields[$name] = $element + array(
'#type' => 'textfield',
'#default_value' => isset($flag->$name) ? $flag->$name : '',
'#access' => empty($flag->locked[$name]),
);
}
$settings['forms'] = array(
'#type' => 'vertical_tabs',
'flagging_form' => array(
'#type' => 'fieldset',
'#title' => t('Flagging form'),
),
'unflagging_form' => array(
'#type' => 'fieldset',
'#title' => t('Unflagging form'),
),
);
foreach (array('form_flagging_help', 'form_flagging_button', 'form_flagging_delete_button') as $field) {
$settings['forms']['flagging_form'][$field] = $fields[$field];
}
foreach (array('form_unflagging_help', 'form_unflagging_button') as $field) {
$settings['forms']['unflagging_form'][$field] = $fields[$field];
}
$tokens_help = array(
'#markup' => '<div class="description">' . t('You may embed in these texts any of the tokens listed above.') . '</div>',
);
$settings['forms']['flagging_form']['tokens_help'] = $settings['forms']['unflagging_form']['tokens_help'] = $tokens_help;
$settings['form_unflag_link_leads_to_edit'] = array(
'#type' => 'checkbox',
'#title' => t('"Unflag this" link to lead to the flagging form.'),
'#default_value' => isset($flag->form_unflag_link_leads_to_edit) ? $flag->form_unflag_link_leads_to_edit : FALSE,
'#description' => t('Usually, the "unflag this" link leads to the unflagging form, where the user can hit a button to unflag the item. However, if we <a href="@manage-fields-url">attach fields</a> to the flag we also want some means to edit them at a later time. By ticking this checkbox the "unflag this" link will instead lead to the <em>flagging</em> form, where the user can either edit the fields or hit a button that leads to the <em>unflagging</em> form. In this case you\'ll also want to modify the "Unflag link text" to reflect the status of the flag (good example: "Bookmarked") instead of one possible action behind the link (bad example: "Unbookmark this item").', array('@manage-fields-url' => $manage_fields_url)),
'#access' => empty($flag->locked['form_unflag_link_leads_to_edit']),
);
$interactions = array();
// @todo: We have several places where we need descriptions for radios /
// checkboxes. Ideally we should add a preprocess to the radios type to
// process #descriptive_options. However, D6 doesn't have hook_hook_info()
// so this will lead to code bloat right now.
foreach (flag_form_interactions() as $interaction => $info) {
$interactions[$interaction] = $info['title'] . ' <div class="description">' . $info['description'] . '</div>';
}
$settings['form_interaction'] = array(
'#type' => 'radios',
'#title' => t('Forms interaction'),
'#options' => $interactions,
'#default_value' => isset($flag->form_interaction) && isset($interactions[$flag->form_interaction]) ? $flag->form_interaction : 'default',
'#description' => t('How users are to interact with the forms. Additional interaction modes are available by installing additional modules.'),
'#access' => empty($flag->locked['form_interaction']),
);
$form['display']['link_options_form'] = $settings;
}
/**
* Implements hook_flag_link().
*
* Returns the URL for the flag links.
*/
function flag_form_flag_link($flag, $action, $content_id) {
if ($action == 'flag') {
$op = 'create';
}
else {
$op = $flag->form_unflag_link_leads_to_edit ? 'edit' : 'delete';
}
$link = array(
'href' => 'flag/flagging/' . $flag->name . '/' . $content_id . '/' . $op,
'query' => drupal_get_destination(),
);
// Let form interaction modules alter the link.
drupal_alter('flag_form_link', $link, $flag, $action, $content_id);
return $link;
}
/**
* Implements hook_menu().
*
* We provide two sets of CRUD links. The first set is for casual use by
* end-users:
*
* flag/flagging/%flag/%content_id/edit
* flag/flagging/%flag/%content_id/create
* flag/flagging/%flag/%content_id/delete
*
* However, this set doesn't let us edit and delete specific flaggings.
* Therefore we provide another set, for privileged users only, to operate on
* specific flaggings:
*
* flag/privileged-flagging/%flagging/edit
* flag/privileged-flagging/%flagging/delete
*
* (This last set is patterned after 'node/%node/edit', or 'user/%user/edit'.)
*
* Why do we need two sets? Why not follow the 'node/%node/edit' example, which,
* after all, works for both end-users and admins? Answer: mainly because
* flagging objects, in contrast to node objects (and users), have a transient
* nature; having the flagging ID embedded in a link might invalidate the link
* once a similar ajax link on that page is clicked. Another reason: admin
* links usually require a different interaction mode (e.g., they don't need
* ajax; e.g., could use the Overlay module) and having a separate namespace
* for them can be useful.
*/
function flag_form_menu() {
// Links for end-users.
// Editing a flagging.
$items['flag/flagging/%flag/%content_id/edit'] = array(
'page callback' => 'flag_flagging_edit_page',
'page arguments' => array(2, 3),
'title callback' => '_flag_menu_title',
'title arguments' => array(2),
'access callback' => '_flag_flagging_access',
'access arguments' => array('update', 2, 3),
);
// Creating a flagging.
$items['flag/flagging/%flag/%content_id/create'] = array(
// It's effectively identical to the 'edit' callback.
'page callback' => 'flag_flagging_edit_page',
'page arguments' => array(2, 3),
'title callback' => '_flag_menu_title',
'title arguments' => array(2),
'access callback' => '_flag_flagging_access',
'access arguments' => array('create', 2, 3),
);
// Deleting a flagging.
$items['flag/flagging/%flag/%content_id/delete'] = array(
'page callback' => 'flag_flagging_delete_page',
'page arguments' => array(2, 3),
'title callback' => '_flag_menu_title',
'title arguments' => array(2),
'access callback' => '_flag_flagging_access',
'access arguments' => array('delete', 2, 3),
);
// Links for privileged users.
// Editing a flagging.
$items['flag/privileged-flagging/%flagging/edit'] = array(
'page callback' => 'drupal_get_form',
'page arguments' => array('flag_flagging_form', 2, TRUE),
'title callback' => '_flag_flagging_menu_title',
'title arguments' => array(2),
// For now we simply check for the 'administer flags' permission, but it
// could make sense to introduce a separate permission for this.
// (Incidentally, this is why we avoid the word "admin" in discussing this
// and instead use the more elastic one "privileged".)
'access arguments' => array('administer flags'),
);
// Deleting a flagging.
$items['flag/privileged-flagging/%flagging/delete'] = array(
'page callback' => 'drupal_get_form',
'page arguments' => array('flag_flagging_delete_form', 2, TRUE),
'title callback' => '_flag_flagging_menu_title',
'title arguments' => array(2),
'access arguments' => array('administer flags'),
);
return $items;
}
/**
* Menu loader for '%content_id' arguments.
*
* It serves solely as documentation. Will be removed eventually.
*/
function content_id_load($content_id) {
return $content_id;
}
/**
* Menu access callback.
*
* param $op
* One of 'update', 'create', 'delete'.
*/
function _flag_flagging_access($op, $flag, $content_id) {
return $flag->access($content_id, $op == 'delete' ? 'unflag' : 'flag');
}
/**
* Menu title callback.
*/
function _flag_flagging_menu_title($flagging) {
return flag_get_flag($flagging->flag_name)->get_title();
}
/**
* Gets a label for use on a form.
*
* It's a wrapper around $flag->get_label() that handles defaults.
*/
function _flag_form_label($flag, $label, $content_id) {
static $defaults = array(
'form_flagging_button' => 'flag_short',
'form_flagging_delete_button' => 'unflag_short',
'form_unflagging_button' => 'unflag_short',
);
$label = 'form_' . $label;
if (empty($flag->$label) && isset($defaults[$label])) {
$label = $defaults[$label];
}
return $flag->get_label($label, $content_id);
}
// -----------------------------------------------------------------------
// Menu callbacks for the forms.
function flag_flagging_edit_page($flag, $content_id) {
$flagging = $flag->get_flagging($content_id);
if (!$flagging) {
// New flagging.
$flagging = $flag->new_flagging($content_id);
}
return drupal_get_form('flag_flagging_form', $flagging);
}
function flag_flagging_delete_page($flag, $content_id) {
$flagging = $flag->get_flagging($content_id);
if (!$flagging) {
// The item isn't flagged. The form function will deal with this error.
$flagging = $flag->new_flagging($content_id);
}
return drupal_get_form('flag_flagging_delete_form', $flagging);
}
// -----------------------------------------------------------------------
// The flagging edit form.
/**
* Returns a form for editing a flagging.
*
* @param $flagging
* The flagging entity. Either loaded form disk or a blank one.
* @param $is_privileged
* Operate in "privileged" mode (to be expanded in the future).
*/
function flag_flagging_form($form, &$form_state, $flagging, $is_privileged = FALSE) {
$form_state['flagging'] = $flagging;
$form_state['flag_is_privileged'] = $is_privileged;
$flag = flag_get_flag($flagging->flag_name);
$is_new = empty($flagging->fcid);
if (($help = _flag_form_label($flag, 'flagging_help', $flagging->content_id))) {
$form['help'] = array(
'#markup' => '<p class="flag-help">' . $help . '</p>',
'#weight' => -20,
);
}
// Add the buttons.
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => _flag_form_label($flag, 'flagging_button', $flagging->content_id),
'#submit' => array('flag_flagging_form_submit'),
);
if (!$is_new) {
$form['actions']['delete'] = array(
'#type' => 'submit',
'#value' => _flag_form_label($flag, 'flagging_delete_button', $flagging->content_id),
'#submit' => array('flag_flagging_form_delete_submit'),
'#access' => $flag->access($flagging->content_id, 'unflag'),
);
}
$form['actions']['cancel'] = array(
'#type' => 'submit',
'#value' => t('Cancel'),
'#submit' => array('flag_flagging_cancel_submit'),
);
$form['actions']['#weight'] = 999;
field_attach_form('flagging', $flagging, $form, $form_state);
// Debugging message.
if (0) {
drupal_set_message($is_privileged
? t('You are operating in "privileged" mode.')
: t('<a href="@url">Switch to privileged mode.</a>', array('@url' => url('flag/privileged-flagging/' . $flagging->fcid . '/edit'))));
}
return $form;
}
function flag_flagging_form_validate($form, &$form_state) {
field_attach_form_validate('flagging', $form_state['flagging'], $form, $form_state);
}
function flag_flagging_form_submit($form, &$form_state) {
$flagging = $form_state['flagging'];
$flag = flag_get_flag($flagging->flag_name);
$is_new = empty($flagging->fcid);
// Add the fields from the form.
field_attach_submit('flagging', $flagging, $form, $form_state);
if ($flag->flag('flag', $flagging->content_id, empty($flagging->uid) ? NULL : user_load($flagging->uid), FALSE, $flagging)) {
if ($is_new) {
if (empty($form_state['flag_suppress_messages'])) {
drupal_set_message($flag->get_label('flag_message', $flagging->content_id));
}
$form_state['flag_status_has_changed'] = TRUE;
}
else {
// @todo: Note that we don't have a message to print when !$is_new (that is,
// when updating an existing flagging). Should we have such a message?
}
}
else {
// Flagging failed.
// We're unlikely to arrive here, as there's an access check on the menu router.
}
}
/**
* Button submit function: handle the 'Delete' button on the flagging form.
*/
function flag_flagging_form_delete_submit($form, &$form_state) {
$is_privileged = $form_state['flag_is_privileged'];
$destination = array();
if (isset($_GET['destination'])) {
$destination = drupal_get_destination();
unset($_GET['destination']);
}
if ($is_privileged) {
$target = 'flag/privileged-flagging/' . $form_state['flagging']->fcid . '/delete';
}
else {
$target = 'flag/flagging/' . $form_state['flagging']->flag_name . '/' . $form_state['flagging']->content_id . '/delete';
}
$form_state['redirect'] = array($target, array('query' => $destination));
}
// -----------------------------------------------------------------------
// The flagging delete form.
/**
* Returns a form for deleting a flagging.
*/
function flag_flagging_delete_form($form, &$form_state, $flagging, $is_privileged = FALSE) {
$form_state['flagging'] = $flagging;
$form_state['flag_is_privileged'] = $is_privileged;
$flag = flag_get_flag($flagging->flag_name);
$form['actions'] = array('#type' => 'actions');
if (!$flagging->fcid) {
// This scenario might happen when there are several 'unflag!' links on the page.
$form['error'] = array(
'#markup' => '<p>' . t('The item is not flagged.') . '</p>',
);
}
else {
if (($help = _flag_form_label($flag, 'unflagging_help', $flagging->content_id))) {
$form['help'] = array(
'#markup' => '<p class="flag-help">' . $help . '</p>',
'#weight' => -20,
);
}
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => _flag_form_label($flag, 'unflagging_button', $flagging->content_id),
'#submit' => array('flag_flagging_delete_form_submit'),
);
}
$form['actions']['cancel'] = array(
'#type' => 'submit',
'#value' => t('Cancel'),
'#submit' => array('flag_flagging_cancel_submit'),
);
$form['actions']['#weight'] = 999;
// Debugging message.
if (0) {
drupal_set_message($is_privileged ? t('You are operating in "privileged" mode.') : '');
}
return $form;
}
function flag_flagging_delete_form_submit($form, &$form_state) {
$flagging = $form_state['flagging'];
$flag = flag_get_flag($flagging->flag_name);
// @todo: Privileged users don't have a way to delete flaggings of anonymous
// users because $flag->flag() doesn't accept a SID. Solution: make it look
// inside $flagging.
if ($flag->flag('unflag', $flagging->content_id, $flag->global ? NULL : user_load($flagging->uid))) {
if (empty($form_state['flag_suppress_messages'])) {
drupal_set_message($flag->get_label('unflag_message', $flagging->content_id));
}
$form_state['flag_status_has_changed'] = TRUE;
}
else {
// Unflagging failed.
// We're unlikely to arrive here, as there's an access check on the menu router.
}
$form_state['redirect'] = '<front>';
}
function flag_flagging_cancel_submit($form, &$form_state) {
$form_state['redirect'] = '<front>';
}