Projet

Général

Profil

Paste
Télécharger (31,4 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / flag / includes / flag.admin.inc @ d719f12f

1 85ad3d82 Assos Assos
<?php
2
3
/**
4
 * @file
5
 * Contains administrative pages for creating, editing, and deleting flags.
6
 */
7
8
/**
9
 * Flag administration page. Display a list of existing flags.
10
 */
11
function flag_admin_page() {
12
  $flags = flag_get_flags();
13
  $default_flags = flag_get_default_flags(TRUE);
14
  $flag_admin_listing = drupal_get_form('flag_admin_listing', $flags);
15
  return theme('flag_admin_page', array(
16
    'flags' => $flags,
17
    'default_flags' => $default_flags,
18
    'flag_admin_listing' => $flag_admin_listing,
19
  ));
20
}
21
22
/**
23
 * A form for ordering the weights of all the active flags in the system.
24
 */
25
function flag_admin_listing($form, &$form_state, $flags) {
26
  $form['#flags'] = $flags;
27
  $form['#tree'] = TRUE;
28
29
  foreach ($flags as $flag) {
30
    $form['flags'][$flag->name]['weight'] = array(
31
      '#type' => 'weight',
32
      '#delta' => count($flags) + 5,
33
      '#default_value' => $flag->weight,
34
      '#attributes' => array('class' => array('flag-weight')),
35
    );
36
  }
37
38
  $form['actions'] = array(
39
    '#type' => 'actions',
40
  );
41
42
  if (count($flags) == 1) {
43
    // Don't show weights with only one flag.
44
    unset($form['flags'][$flag->name]['weight']);
45
  }
46
  elseif (count($flags) > 1) {
47
    // Only show the form button if there are several flags.
48
    $form['actions']['submit'] = array(
49
      '#type' => 'submit',
50
      '#value' => t('Save flag order'),
51
    );
52
  }
53
54
  return $form;
55
}
56
57
/**
58
 * Submit handler for the flag_admin_listing form. Save flag weight ordering.
59
 */
60
function flag_admin_listing_submit($form, &$form_state) {
61
  foreach ($form['#flags'] as $flag) {
62
    if ($flag->weight != $form_state['values']['flags'][$flag->name]['weight']) {
63
      $flag->weight = $form_state['values']['flags'][$flag->name]['weight'];
64
      $flag->save();
65
    }
66
  }
67
}
68
69
/**
70
 * Theme the output of the normal, database flags into a table.
71
 */
72
function theme_flag_admin_listing($variables) {
73
  $form = $variables['form'];
74
  $flags = $form['#flags'];
75
76
  $output = '';
77
78
  foreach ($flags as $flag) {
79
    $ops = array(
80 76e2e7c3 Assos Assos
      'flags_edit' => array('title' => t('edit'), 'href' => $flag->admin_path('edit')),
81
      'flags_fields' => array('title' => t('manage fields'), 'href' => $flag->admin_path('fields')),
82
      'flags_delete' => array('title' => t('delete'), 'href' => $flag->admin_path('delete')),
83
      'flags_export' => array('title' => t('export'), 'href' => $flag->admin_path('export')),
84 85ad3d82 Assos Assos
    );
85
    if (!module_exists('field_ui')) {
86
      unset($ops['flags_fields']);
87
    }
88
    $permission = "flag $flag->name";
89
    $roles = user_roles(FALSE, $permission);
90
    $row = array();
91
    $row[] = check_plain($flag->title) . ' <small>(' . t('Machine name: @name', array('@name' => $flag->name)) . ')</small>';
92
    if (count($flags) > 1) {
93
      $row[] = drupal_render($form['flags'][$flag->name]['weight']);
94
    }
95
    $row[] = $flag->entity_type;
96
    $row[] = empty($roles) ? '<em>' . t('No roles') . '</em>' : implode(', ', $roles);
97
    $row[] = $flag->types ? implode(', ', $flag->types) : '-';
98
    $row[] = $flag->global ? t('Yes') : t('No');
99
    $row[] = theme('links', array('links' => $ops));
100
101
    $rows[] = array(
102
      'data' => $row,
103
      'class' => array('draggable'),
104
    );
105
  }
106
  if (!$flags) {
107
    $rows[] = array(
108
      array('data' => t('No flags are currently defined.'), 'colspan' => 7),
109
    );
110
  }
111
  elseif (count($flags) > 1) {
112
    drupal_add_tabledrag('flag-admin-listing-table', 'order', 'sibling', 'flag-weight');
113
  }
114
115
  $header = array(t('Flag'));
116
  if (count($flags) > 1) {
117
    $header[] = t('Weight');
118
  }
119 76e2e7c3 Assos Assos
  $header = array_merge($header, array(
120
    t('Flag type'),
121
    t('Roles'),
122
    t('Entity bundles'),
123
    t('Global?'),
124
    t('Operations'),
125
  ));
126 85ad3d82 Assos Assos
  $output .= theme('table', array(
127
    'header' => $header,
128
    'rows' => $rows,
129
    'attributes' => array('id' => 'flag-admin-listing-table'),
130
  ));
131
  $output .= drupal_render_children($form);
132
133
  return $output;
134
}
135
136
/**
137
 * Theme the list of disabled flags into a table.
138
 */
139
function theme_flag_admin_listing_disabled($variables) {
140
  $flags = $variables['flags'];
141
  $default_flags = $variables['default_flags'];
142
  $output = '';
143
144
  // Build a list of disabled, module-based flags.
145
  $rows = array();
146
  foreach ($default_flags as $name => $flag) {
147
    if (!isset($flags[$name])) {
148
      $ops = array();
149
      if (!$flag->is_compatible()) {
150
        $flag_updates_needed = TRUE;
151 76e2e7c3 Assos Assos
        $ops['flags_update'] = array(
152
          'title' => '<strong>' . t('update code') . '</strong>',
153
          'href' => $flag->admin_path('update'),
154
          'html' => TRUE,
155
        );
156 85ad3d82 Assos Assos
      }
157
      else {
158
        $ops['flags_enable'] = array('title' => t('enable'), 'href' => $flag->admin_path('edit'));
159
      }
160
      // $flag->roles['flag'] not exist on older flags.
161
      $roles = array_flip(array_intersect(array_flip(user_roles()), !empty($flag->roles['flag']) ? $flag->roles['flag'] : array()));
162
      $rows[] = array(
163
        $flag->name,
164
        $flag->module,
165
        $flag->entity_type ? $flag->entity_type : t('Unknown'),
166
        theme('links', array('links' => $ops)),
167
      );
168
    }
169
  }
170
171
  if (isset($flag_updates_needed)) {
172
    drupal_set_message(t('Some flags provided by modules need to be updated to a new format before they can be used with this version of Flag. See the disabled flags for a list of flags that need updating.'), 'warning');
173
  }
174
175
  if (!empty($rows)) {
176 76e2e7c3 Assos Assos
    $header = array(
177
      t('Disabled Flags'),
178
      t('Module'),
179
      t('Flag type'),
180
      t('Operations'),
181
    );
182 85ad3d82 Assos Assos
    $output .= theme('table', array('header' => $header, 'rows' => $rows));
183
  }
184
185
  return $output;
186
}
187
188
/**
189
 * Theme the output for the main flag administration page.
190
 */
191
function theme_flag_admin_page($variables) {
192
  $flags = $variables['flags'];
193
  $default_flags = $variables['default_flags'];
194
195
  $output = '';
196
197
  $output .= drupal_render($variables['flag_admin_listing']);
198
  $output .= theme('flag_admin_listing_disabled', array('flags' => $flags, 'default_flags' => $default_flags));
199
200
  if (!module_exists('views')) {
201
    $output .= '<p>' . t('The <a href="@views-url">Views</a> module is not installed, or not enabled. It is recommended that you install the Views module to be able to easily produce lists of flagged content.', array('@views-url' => url('http://drupal.org/project/views'))) . '</p>';
202
  }
203
  else {
204
    $output .= '<p>';
205
    $output .= t('Lists of flagged content can be displayed using views. You can configure these in the <a href="@views-url">Views administration section</a>.', array('@views-url' => url('admin/structure/views')));
206
    if (flag_get_flag('bookmarks')) {
207
      $output .= ' ' . t('Flag module automatically provides a few <a href="@views-url">default views for the <em>bookmarks</em> flag</a>. You can use these as templates by cloning these views and then customizing as desired.', array('@views-url' => url('admin/structure/views', array('query' => array('tag' => 'flag')))));
208
    }
209
    $output .= ' ' . t('The <a href="@flag-handbook-url">Flag module handbook</a> contains extensive <a href="@customize-url">documentation on creating customized views</a> using flags.', array('@flag-handbook-url' => 'http://drupal.org/handbook/modules/flag', '@customize-url' => 'http://drupal.org/node/296954'));
210
    $output .= '</p>';
211
  }
212
213
  if (!module_exists('flag_actions')) {
214
    $output .= '<p>' . t('Flagging an item may trigger <em>actions</em>. However, you don\'t have the <em>Flag actions</em> module <a href="@modules-url">enabled</a>, so you won\'t be able to enjoy this feature.', array('@actions-url' => url(FLAG_ADMIN_PATH . '/actions'), '@modules-url' => url('admin/modules'))) . '</p>';
215
  }
216
  else {
217
    $output .= '<p>' . t('Flagging an item may trigger <a href="@actions-url">actions</a>.', array('@actions-url' => url(FLAG_ADMIN_PATH . '/actions'))) . '</p>';
218
  }
219
220
  if (!module_exists('rules')) {
221
    $output .= '<p>' . t('Flagging an item may trigger <em>rules</em>. However, you don\'t have the <a href="@rules-url">Rules</a> module enabled, so you won\'t be able to enjoy this feature. The Rules module is a more extensive solution than Flag actions.', array('@rules-url' => url('http://drupal.org/node/407070'))) . '</p>';
222
  }
223
  else {
224
    $output .= '<p>' . t('Flagging an item may trigger <a href="@rules-url">rules</a>.', array('@rules-url' => url('admin/config/workflow/rules'))) . '</p>';
225
  }
226
227
  $output .= '<p>' . t('To learn about the various ways to use flags, please check out the <a href="@handbook-url">Flag module handbook</a>.', array('@handbook-url' => 'http://drupal.org/handbook/modules/flag')) . '</p>';
228
229
  return $output;
230
}
231
232
/**
233
 * Menu callback for adding a new flag.
234
 *
235 fd5a8e62 Assos Assos
 * @param string|NULL $entity_type
236 85ad3d82 Assos Assos
 *  The entity type for the new flag, taken from the path argument. If not
237
 *  present (i.e., '/add'), a form showing all possible flag types is shown.
238
 *  Otherwise, this shows a form for adding af flag the given type.
239
 *
240
 * @see flag_add_form()
241
 * @see flag_form()
242
 */
243
function flag_add_page($entity_type = NULL) {
244
  if (isset($entity_type)) {
245
    $flag = flag_flag::factory_by_entity_type($entity_type);
246
    // Mark the flag as new.
247
    $flag->is_new = TRUE;
248
    $type_info = flag_fetch_definition($entity_type);
249
    drupal_set_title(t('Add new @type flag', array('@type' => $type_info['title'])));
250
    return drupal_get_form('flag_form', $flag);
251
  }
252
253
  drupal_set_title(t('Select flag type'));
254
  return drupal_get_form('flag_add_form');
255
}
256
257
/**
258
 * Present a form for creating a new flag, setting the type of flag.
259
 */
260
function flag_add_form($form, &$form_state) {
261
  $types = array();
262
  foreach (flag_fetch_definition() as $type => $info) {
263
    $types[$type] = $info['title'] . '<div class="description">' . $info['description'] . '</div>';
264
  }
265
266
  $form['type'] = array(
267
    '#type' => 'radios',
268
    '#title' => t('Flag type'),
269
    '#default_value' => 'node',
270
    '#description' => t('The type of object this flag will affect. This cannot be changed once the flag is created.'),
271
    '#required' => TRUE,
272
    '#options' => $types,
273
  );
274
275
  $form['actions'] = array(
276
    '#type' => 'actions',
277
  );
278
279
  $form['actions']['submit'] = array(
280
    '#type' => 'submit',
281
    '#value' => t('Continue'),
282
  );
283
284
  return $form;
285
}
286
287
function flag_add_form_validate($form, &$form_state) {
288
  $flag = flag_flag::factory_by_entity_type($form_state['values']['type']);
289
  if (get_class($flag) == 'flag_broken') {
290
    form_set_error('type', t("This flag type, %type, isn't valid.", array('%type' => $form_state['values']['type'])));
291
  }
292
}
293
294
function flag_add_form_submit($form, &$form_state) {
295
  $form_state['redirect'] = FLAG_ADMIN_PATH . '/add/' . $form_state['values']['type'];
296
}
297
298
/**
299
 * Add/Edit flag page.
300
 */
301
function flag_form($form, &$form_state, $flag) {
302
  $form['#flag'] = $flag;
303
  $form['#flag_name'] = $flag->name;
304
305
  $form['title'] = array(
306
    '#type' => 'textfield',
307
    '#title' => t('Title'),
308
    '#default_value' => $flag->title,
309
    '#description' => t('A short, descriptive title for this flag. It will be used in administrative interfaces to refer to this flag, and in page titles and menu items of some <a href="@insite-views-url">views</a> this module provides (theses are customizable, though). Some examples could be <em>Bookmarks</em>, <em>Favorites</em>, or <em>Offensive</em>.', array('@insite-views-url' => url('admin/structure/views'))),
310
    '#maxlength' => 255,
311
    '#required' => TRUE,
312
    '#weight' => -3,
313
  );
314
315
  $form['name'] = array(
316
    '#type' => 'machine_name',
317
    '#title' => t('Machine name'),
318
    '#default_value' => $flag->name,
319
    '#description' => t('The machine-name for this flag. It may be up to 32 characters long and may only contain lowercase letters, underscores, and numbers. It will be used in URLs and in all API calls.'),
320
    '#maxlength' => 32,
321
    '#weight' => -2,
322
    '#machine_name' => array(
323
      'exists' => 'flag_get_flag',
324
      'source' => array('title'),
325
    ),
326
  );
327
328
  $form['global'] = array(
329
    '#type' => 'checkbox',
330
    '#title' => t('Global flag'),
331
    '#default_value' => $flag->global,
332
    '#description' => t('If checked, flag is considered "global" and each entity is either flagged or not. If unchecked, each user has individual flags on entities.'),
333
    '#weight' => -1,
334
  );
335
  // Don't allow the 'global' checkbox to be changed when flaggings exist:
336
  // there are too many unpleasant consequences in either direction.
337
  // @todo: Allow this, but with a confirmation form, assuming anyone actually
338
  // needs this feature.
339
  if (!empty($flag->fid) && flag_get_flag_counts($flag->name)) {
340
    $form['global']['#disabled'] = TRUE;
341
    $form['global']['#description'] .= '<br />' . t('This setting cannot be changed when flaggings exist for this flag.');
342
  }
343
344
  $form['messages'] = array(
345
    '#type' => 'fieldset',
346
    '#title' => t('Messages'),
347
  );
348
349
  $form['messages']['flag_short'] = array(
350
    '#type' => 'textfield',
351
    '#title' => t('Flag link text'),
352
    '#default_value' => !empty($flag->flag_short) ? $flag->flag_short : t('Flag this item'),
353
    '#description' => t('The text for the "flag this" link for this flag.'),
354
    '#required' => TRUE,
355
  );
356
357
  $form['messages']['flag_long'] = array(
358
    '#type' => 'textfield',
359
    '#title' => t('Flag link description'),
360
    '#default_value' => $flag->flag_long,
361
    '#description' => t('The description of the "flag this" link. Usually displayed on mouseover.'),
362
  );
363
364
  $form['messages']['flag_message'] = array(
365
    '#type' => 'textfield',
366
    '#title' => t('Flagged message'),
367
    '#default_value' => $flag->flag_message,
368
    '#description' => t('Message displayed after flagging content. If JavaScript is enabled, it will be displayed below the link. If not, it will be displayed in the message area.'),
369
  );
370
371
  $form['messages']['unflag_short'] = array(
372
    '#type' => 'textfield',
373
    '#title' => t('Unflag link text'),
374
    '#default_value' => !empty($flag->unflag_short) ? $flag->unflag_short : t('Unflag this item'),
375
    '#description' => t('The text for the "unflag this" link for this flag.'),
376
    '#required' => TRUE,
377
  );
378
379
  $form['messages']['unflag_long'] = array(
380
    '#type' => 'textfield',
381
    '#title' => t('Unflag link description'),
382
    '#default_value' => $flag->unflag_long,
383
    '#description' => t('The description of the "unflag this" link. Usually displayed on mouseover.'),
384
  );
385
386
  $form['messages']['unflag_message'] = array(
387
    '#type' => 'textfield',
388
    '#title' => t('Unflagged message'),
389
    '#default_value' => $flag->unflag_message,
390
    '#description' => t('Message displayed after content has been unflagged. If JavaScript is enabled, it will be displayed below the link. If not, it will be displayed in the message area.'),
391
  );
392
393
  $form['messages']['tokens_help'] = array(
394
    '#title' => t('Token replacement'),
395
    '#type' => 'fieldset',
396
    '#description' =>
397 76e2e7c3 Assos Assos
    '<p>' . t('The above six texts may contain any of the tokens listed below. For example, <em>"Flag link text"</em> could be entered as:') . '</p>' .
398
    theme('item_list', array(
399
      'items' => array(
400
        t('Add &lt;em&gt;[node:title]&lt;/em&gt; to your favorites'),
401
        t('Add this [node:type] to your favorites'),
402
        t('Vote for this proposal ([node:flag-vote-count] people have already done so)'),
403
      ),
404
      'attributes' => array('class' => 'token-examples'),
405
    )) .
406
    '<p>' . t('These tokens will be replaced with the appropriate fields from the node (or user, or comment).') . '</p>' .
407
    theme('flag_tokens_browser', array('types' => $flag->get_labels_token_types())),
408 85ad3d82 Assos Assos
    '#collapsible' => TRUE,
409
    '#collapsed' => TRUE,
410
  );
411
412
  $form['access'] = array(
413
    '#type' => 'fieldset',
414
    '#title' => t('Flag access'),
415
    '#tree' => FALSE,
416
    '#weight' => 10,
417
  );
418
419
  // Flag classes will want to override this form element.
420
  $form['access']['types'] = array(
421
    '#type' => 'checkboxes',
422
    '#title' => t('Flaggable types'),
423
    '#options' => array(),
424
    '#default_value' => $flag->types,
425
    '#description' => t('Check any sub-types that this flag may be used on.'),
426
    '#required' => TRUE,
427
    '#weight' => 10,
428
  );
429
430
  // Disabled access breaks checkboxes unless #value is hard coded.
431
  if (!empty($flag->locked['types'])) {
432
    $form['access']['types']['#value'] = $flag->types;
433
  }
434
435
  // Load the user permissions into the flag.
436
  if (isset($flag->fid)) {
437
    $flag->fetch_roles();
438
  }
439
  elseif (isset($flag->import_roles)) {
440
    // Convert the roles data from old API 2 flags that have been run through
441
    // the update system.
442
    // @see FlagUpdate_2::update()
443
    $flag->roles = $flag->import_roles;
444
  }
445
  else {
446
    // For new flags, provide a reasonable default value.
447
    $flag->roles = array(
448
      'flag' => array(DRUPAL_AUTHENTICATED_RID),
449
      'unflag' => array(DRUPAL_AUTHENTICATED_RID),
450
    );
451
  }
452
453
  $form['access']['roles'] = array(
454
    '#title' => t('Roles that may use this flag'),
455
    '#description' => t('Users may only unflag content if they have access to flag the content initially. Checking <em>authenticated user</em> will allow access for all logged-in users.'),
456
    '#theme' => 'flag_form_roles',
457
    '#theme_wrappers' => array('form_element'),
458
    '#weight' => -2,
459
    '#attached' => array(
460
      'js' => array(drupal_get_path('module', 'flag') . '/theme/flag-admin.js'),
461
      'css' => array(drupal_get_path('module', 'flag') . '/theme/flag-admin.css'),
462
    ),
463
  );
464
  if (module_exists('session_api')) {
465
    $form['access']['roles']['#description'] .= ' ' . t('Support for anonymous users is being provided by <a href="http://drupal.org/project/session_api">Session API</a>.');
466
  }
467
  else {
468
    $form['access']['roles']['#description'] .= ' ' . t('Anonymous users may flag content if the <a href="http://drupal.org/project/session_api">Session API</a> module is installed.');
469
  }
470
471
  $form['access']['roles']['flag'] = array(
472
    '#type' => 'checkboxes',
473
    '#options' => user_roles(!module_exists('session_api')),
474
    '#default_value' => $flag->roles['flag'],
475
    '#parents' => array('roles', 'flag'),
476
  );
477
  $form['access']['roles']['unflag'] = array(
478
    '#type' => 'checkboxes',
479
    '#options' => user_roles(!module_exists('session_api')),
480
    '#default_value' => $flag->roles['unflag'],
481
    '#parents' => array('roles', 'unflag'),
482
  );
483
484
  $form['access']['unflag_denied_text'] = array(
485
    '#type' => 'textfield',
486
    '#title' => t('Unflag not allowed text'),
487
    '#default_value' => $flag->unflag_denied_text,
488
    '#description' => t('If a user is allowed to flag but not unflag, this text will be displayed after flagging. Often this is the past-tense of the link text, such as "flagged".'),
489
    '#weight' => -1,
490
  );
491
492
  $form['display'] = array(
493
    '#type' => 'fieldset',
494
    '#title' => t('Display options'),
495
    '#description' => t('Flags are usually controlled through links that allow users to toggle their behavior. You can choose how users interact with flags by changing options here. It is legitimate to have none of the following checkboxes ticked, if, for some reason, you wish <a href="@placement-url">to place the the links on the page yourself</a>.', array('@placement-url' => 'http://drupal.org/node/295383')),
496
    '#tree' => FALSE,
497
    '#weight' => 20,
498
    '#after_build' => array('flag_link_type_options_states'),
499
  );
500
501
  $form['display']['link_type'] = array(
502
    '#type' => 'radios',
503
    '#title' => t('Link type'),
504
    '#options' => _flag_link_type_options(),
505
    '#after_build' => array('flag_check_link_types'),
506
    '#default_value' => $flag->link_type,
507
    // Give this a high weight so additions by the flag classes for entity-
508
    // specific options go above.
509
    '#weight' => 18,
510
    '#attached' => array(
511
      'js' => array(drupal_get_path('module', 'flag') . '/theme/flag-admin.js'),
512
    ),
513
    '#attributes' => array(
514
      'class' => array('flag-link-options'),
515
    ),
516
  );
517
  // Add the descriptions to each ratio button element. These attach to the
518
  // elements when FormAPI expands them.
519
  foreach (_flag_link_type_descriptions() as $key => $description) {
520
    $form['display']['link_type'][$key]['#description'] = $description;
521
  }
522
523
  $form['display']['link_options_intro'] = array(
524
    // This is a hack to allow a markup element to use FormAPI states.
525
    // @see http://www.bywombats.com/blog/06-25-2011/using-containers-states-enabled-markup-form-elements
526
    '#type' => 'container',
527
    '#children' => '<p id="link-options-intro">' . t('The selected link type may require these additional settings:') . '</p>',
528
    '#weight' => 20,
529
  );
530
531
  $form['display']['link_options_confirm'] = array(
532
    '#type' => 'fieldset',
533
    '#title' => t('Options for the "Confirmation form" link type'),
534
    // Any "link type" provider module must put its settings fields inside
535
    // a fieldset whose HTML ID is link-options-LINKTYPE, where LINKTYPE is
536
    // the machine-name of the link type. This is necessary for the
537
    // radiobutton's JavaScript dependency feature to work.
538
    '#id' => 'link-options-confirm',
539
    '#weight' => 21,
540
  );
541
542
  $form['display']['link_options_confirm']['flag_confirmation'] = array(
543
    '#type' => 'textfield',
544
    '#title' => t('Flag confirmation message'),
545
    '#default_value' => isset($flag->flag_confirmation) ? $flag->flag_confirmation : '',
546
    '#description' => t('Message displayed if the user has clicked the "flag this" link and confirmation is required. Usually presented in the form of a question such as, "Are you sure you want to flag this content?"'),
547 018e218c Assos Assos
    // This will get changed to a state by flag_link_type_options_states().
548
    '#required' => TRUE,
549 85ad3d82 Assos Assos
  );
550
551
  $form['display']['link_options_confirm']['unflag_confirmation'] = array(
552
    '#type' => 'textfield',
553
    '#title' => t('Unflag confirmation message'),
554
    '#default_value' => isset($flag->unflag_confirmation) ? $flag->unflag_confirmation : '',
555
    '#description' => t('Message displayed if the user has clicked the "unflag this" link and confirmation is required. Usually presented in the form of a question such as, "Are you sure you want to unflag this content?"'),
556 018e218c Assos Assos
    // This will get changed to a state by flag_link_type_options_states().
557
    '#required' => TRUE,
558 85ad3d82 Assos Assos
  );
559
560
  $form['actions'] = array(
561
    '#type' => 'actions',
562
  );
563
564
  $form['actions']['submit'] = array(
565
    '#type' => 'submit',
566
    '#value' => t('Save flag'),
567
    // We put this button on the form before calling $flag->options_form()
568
    // to give the flag handler a chance to remove it (e.g. flag_broken).
569
    '#weight' => 999,
570
  );
571
572
  // Add our process handler to disable access to locked properties.
573
  $form['#process'][] = 'flag_form_locked_process';
574
575
  // Allow the flag handler to make additions and changes to the form.
576
  // Note that the flag_broken handler will completely empty the form array!
577
  $flag->options_form($form);
578
579
  return $form;
580
}
581
582
/**
583 018e218c Assos Assos
 * FormAPI after_build function to set states on link type options fieldsets.
584 85ad3d82 Assos Assos
 *
585
 * We do this in an after build so we handle further link types fieldsets from
586
 * other modules that provide link types.
587
 *
588
 * This expects a link type's fieldset to be $form['display'][link_options_TYPE]
589
 * so that can be matched up with the radio button value.
590
 */
591
function flag_link_type_options_states($element) {
592
  $intro_element_values_array = array();
593
  foreach (element_children($element) as $key) {
594
    if (isset($element[$key]['#type']) && $element[$key]['#type'] == 'fieldset' && substr($key, 0, 12) == 'link_options') {
595
      // Trim the radio value from the fieldset key. This assumed the fieldset
596
      // key is 'link_options_TYPE'.
597
      $radio_value = substr($key, 13);
598
      $element[$key]['#states'] = array(
599
        'visible' => array(
600
          ':input[name="link_type"]' => array('value' => $radio_value),
601
        ),
602
      );
603 018e218c Assos Assos
604
      // If an element in a link type options fieldset is required, then we
605
      // remove this, as this would break the form, by demanding the user
606
      // enter a value for a form element they possibly can't see!
607
      // Instead, we set the required property as a state.
608
      foreach (element_children($element[$key]) as $child_key) {
609
        if (!empty($element[$key][$child_key]['#required'])) {
610
          $element[$key][$child_key]['#required'] = FALSE;
611
          $element[$key][$child_key]['#states']['required'] = array(
612
            ':input[name="link_type"]' => array('value' => $radio_value),
613
          );
614
        }
615
      }
616
617 85ad3d82 Assos Assos
      // Gather up the radio values for the format we need for a multiple
618
      // value state.
619
      $intro_element_values_array[] = array('value' => $radio_value);
620
    }
621
  }
622
623
  $element['link_options_intro']['#states'] = array(
624
    'visible' => array(
625
      ':input[name="link_type"]' => $intro_element_values_array,
626
    ),
627
  );
628
629
  return $element;
630
}
631
632
/**
633
 * Form process handler for locking flag properties.
634
 *
635
 * Flags defined in code may define an array of properties in $flag->locked that
636
 * are to be locked and may not be edited by the user.
637
 */
638
function flag_form_locked_process($element, &$form_state, $form) {
639
  $flag = $form['#flag'];
640
641
  // Disable access to a form element whose name matches a locked flag property.
642
  if (isset($element['#name']) && !empty($flag->locked[$element['#name']])) {
643
    $element['#access'] = FALSE;
644
  }
645
646
  // Recurse into the form array.
647
  foreach (element_children($element) as $key) {
648
    // Workaround for Core inconvenience: setting #process here prevents an
649
    // element's essential #process handlers from its hook_element_info()
650
    // definition from being set in form_builder().
651
    // @see http://drupal.org/node/1779496
652
    if (isset($element[$key]['#type']) && ($info = element_info($element[$key]['#type']))) {
653
      if (isset($info['#process'])) {
654
        $element[$key]['#process'] = $info['#process'];
655
      }
656
    }
657
658
    $element[$key]['#process'][] = 'flag_form_locked_process';
659
  }
660
661
  return $element;
662
}
663
664
/**
665
 * Add/Edit flag form validate.
666
 */
667
function flag_form_validate($form, &$form_state) {
668
  $form_state['values']['title'] = trim($form_state['values']['title']);
669
  $form_values = $form_state['values'];
670
671
  $flag = $form['#flag'];
672
  $flag->form_input($form_values);
673
  $errors = $flag->validate();
674
  foreach ($errors as $field => $field_errors) {
675
    foreach ($field_errors as $error) {
676
      form_set_error($field, $error['message']);
677
    }
678
  }
679
}
680
681
/**
682
 * Add/Edit flag form submit.
683
 */
684
function flag_form_submit($form, &$form_state) {
685
  $flag = $form['#flag'];
686
687
  $form_state['values']['title'] = trim($form_state['values']['title']);
688
  $flag->form_input($form_state['values']);
689
690
  $flag->save();
691
  $flag->enable();
692
  drupal_set_message(t('Flag @title has been saved.', array('@title' => $flag->get_title())));
693
  // We clear caches more vigorously if the flag was new.
694
  _flag_clear_cache($flag->entity_type, !empty($flag->is_new));
695
696 fd5a8e62 Assos Assos
  if (!empty($flag->is_new)) {
697
    field_attach_create_bundle('flagging', $flag->name);
698
  }
699
700 85ad3d82 Assos Assos
  // Save permissions.
701
  // This needs to be done after the flag cache has been cleared, so that
702
  // the new permissions are picked up by hook_permission().
703
  // This may need to move to the flag class when we implement extra permissions
704
  // for different flag types: http://drupal.org/node/879988
705
706 fd5a8e62 Assos Assos
  // If the flag machine name as changed, clean up all the obsolete permissions
707
  // and notify FieldAPI.
708 85ad3d82 Assos Assos
  if ($flag->name != $form['#flag_name']) {
709
    $old_name = $form['#flag_name'];
710
    $permissions = array("flag $old_name", "unflag $old_name");
711
    foreach (array_keys(user_roles()) as $rid) {
712
      user_role_revoke_permissions($rid, $permissions);
713
    }
714 fd5a8e62 Assos Assos
715
    field_attach_rename_bundle('flagging', $old_name, $flag->name);
716 85ad3d82 Assos Assos
  }
717
718
  foreach (array_keys(user_roles(!module_exists('session_api'))) as $rid) {
719
    // Create an array of permissions, based on the checkboxes element name.
720
    $permissions = array(
721
      "flag $flag->name" => $flag->roles['flag'][$rid],
722
      "unflag $flag->name" => $flag->roles['unflag'][$rid],
723
    );
724
    user_role_change_permissions($rid, $permissions);
725
  }
726
  // @todo: when we add database caching for flags we'll have to clear the
727
  // cache again here.
728
729
  $form_state['redirect'] = FLAG_ADMIN_PATH;
730
}
731
732
/**
733
 * Output the access options for roles in a table.
734
 */
735
function theme_flag_form_roles($variables) {
736
  $element = $variables['element'];
737
738
  $header = array(
739
    array('class' => array('checkbox'), 'data' => t('Flag')),
740
    array('class' => array('checkbox'), 'data' => t('Unflag')),
741
    t('Role'),
742
  );
743
  $rows = array();
744
  foreach (element_children($element['flag']) as $role) {
745
    $row = array();
746
    $role_name = $element['flag'][$role]['#title'];
747
    unset($element['flag'][$role]['#title']);
748
    unset($element['unflag'][$role]['#title']);
749
    $element['flag'][$role]['#attributes']['class'] = array('flag-access');
750
    $element['unflag'][$role]['#attributes']['class'] = array('unflag-access');
751
    $row[] = array('class' => array('checkbox'), 'data' => drupal_render($element['flag'][$role]));
752
    $row[] = array('class' => array('checkbox'), 'data' => drupal_render($element['unflag'][$role]));
753
    $row[] = $role_name;
754
    $rows[] = $row;
755
  }
756
757
  return theme('table', array(
758
    'header' => $header,
759
    'rows' => $rows,
760
    'attributes' => array(
761
      'class' => array('flag-admin-table'),
762
      'id' => 'flag-roles',
763
    ),
764
  ));
765
}
766
767
/**
768
 * Delete flag page.
769
 */
770
function flag_delete_confirm($form, &$form_state, $flag) {
771
  $form['#flag'] = $flag;
772
773
  return confirm_form($form,
774
    t('Are you sure you want to delete %title?', array('%title' => $flag->get_title())),
775
    !empty($_GET['destination']) ? $_GET['destination'] : FLAG_ADMIN_PATH,
776
    isset($flag->module) ? t('This flag is provided by the %module module. It will lose any customizations and be disabled.', array('%module' => $flag->module)) : t('This action cannot be undone.'),
777
    t('Delete'), t('Cancel')
778
  );
779
}
780
781
function flag_delete_confirm_submit($form, &$form_state) {
782
  $flag = $form['#flag'];
783
  if ($form_state['values']['confirm']) {
784
    $flag->delete();
785
    $flag->disable();
786
    _flag_clear_cache($flag->entity_type, TRUE);
787
  }
788
  drupal_set_message(t('Flag @name has been deleted.', array('@name' => $flag->get_title())));
789
  $form_state['redirect'] = FLAG_ADMIN_PATH;
790
}
791
792
/**
793
 * FormAPI after_build function to check that the link type exists.
794
 */
795
function flag_check_link_types($element) {
796
  $link_types = flag_get_link_types();
797
  if (!isset($link_types[$element['#value']])) {
798
    drupal_set_message(t('This flag uses a link type of %type, which does not exist.', array('%type' => $element['#value'])), 'error');
799
  }
800
  return $element;
801
}
802
803
/**
804
 * Clears various caches when one or more flags are modified.
805
 *
806 fd5a8e62 Assos Assos
 * @param string|array $entity_types
807 85ad3d82 Assos Assos
 *  The entity types for the flags. May be a single value or an array.
808 fd5a8e62 Assos Assos
 * @param bool $is_insert_or_delete
809 85ad3d82 Assos Assos
 *  Whether the modified flag is being inserted (saved for the first time) or
810
 *  deleted. This results in a more vigorous clearing of caches. In
811
 *  particular, when no flags exist yet, no Field admin UI paths exist and these
812
 *  need to be created.
813
 */
814
function _flag_clear_cache($entity_types, $is_insert_or_delete = FALSE) {
815
  if (!is_array($entity_types)) {
816
    $entity_types = array($entity_types);
817
  }
818
819
  // Reset our flags cache, thereby making the following code aware of the
820
  // modifications.
821
  drupal_static_reset('flag_get_flags');
822
823
  if ($is_insert_or_delete) {
824
    // A new or deleted flag means we are changing bundles on the Flagging
825
    // entity, and thus need to clear the entity info cache.
826
    entity_info_cache_clear();
827
  }
828
829
  // Clear FieldAPI's field_extra cache, so our changes to pseudofields are
830
  // noticed. It's rather too much effort to both a) check whether the
831
  // pseudofield setting has changed either way, and b) specifically clear just
832
  // the bundles that are (or were!!) affected, so we just clear for all bundles
833
  // on our entity type regardlesss.
834
  foreach ($entity_types as $entity_type) {
835
    cache_clear_all("field_info:bundle_extra:$entity_type:", 'cache_field', TRUE);
836
  }
837
838
  if (module_exists('views')) {
839
    views_invalidate_cache();
840
  }
841
842
  // The title of a flag may appear in the menu (indirectly, via our "default
843
  // views"), so we need to clear the menu cache. This call also clears the
844
  // page cache, which is desirable too because the flag labels may have
845
  // changed.
846
  menu_rebuild();
847
}