-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathuserreference_url.module
544 lines (503 loc) · 19.5 KB
/
userreference_url.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
540
541
542
543
544
<?php
/**
* @file
* Adds a "URL" widget to the User Reference field.
*/
/**
* Implements hook_theme().
*/
function userreference_url_theme() {
return array(
'userreference_url' => array(
'render element' => 'element',
),
);
}
/**
* Implements hook_form_alter().
*/
function userreference_url_form_alter(&$form, &$form_state, $form_id) {
if ($form_id == 'field_ui_field_edit_form' && isset($form['#instance']) &&
$form['#instance']['widget']['type'] == 'userreference_url') {
// Hide settings that don't apply to this widget.
$form['field']['cardinality']['#type'] = 'value';
$form['instance']['description']['#title'] = t('Fallback widget help text');
$form['instance']['description']['#weight'] = -1;
}
}
/**
* Implements hook_widget_info().
*/
function userreference_url_field_widget_info() {
return array(
'userreference_url' => array(
'label' => t('Reference from URL'),
'description' => t('User Reference calculated from URL'),
'field types' => array('user_reference'),
'settings' => array(
'fallback' => 'select',
'user_link' => array(),
'edit_fallback' => 0,
'autocomplete_match' => 'contains',
),
),
);
}
/**
* Implements hook_user_view().
*/
function userreference_url_user_view($account, $view_mode, $langcode) {
$links = userreference_url_build_all_links($account, $view_mode);
$account->content['links']['userreference_url'] = array(
'#theme' => 'links__user__userreference',
'#links' => $links,
'#attributes' => array('class' => array('links', 'inline')),
);
}
/**
* Build an array of links for userreference_url widgets
* that point to this user.
*
* @param objact $account
* A fully loaded user object.
* @param string $view_mode
* View mode, e.g. 'full'
*
* @return array
* An array of links for use with theme_links().
*/
function userreference_url_build_all_links($account, $view_mode) {
$links = array();
$fields = field_info_instances('node');
$instances = array();
foreach ($fields as $target_type => $field) {
foreach ($field as $field_name => $instance) {
if ($instance['widget']['type'] == 'userreference_url') {
$instances[$target_type][$field_name] = $instance;
}
}
}
foreach ($instances as $target_type => $instances_group) {
$alt_format = count($instances_group) > 1;
foreach ($instances_group as $field_name => $instance) {
$link_settings = $instance['widget']['settings']['user_link'];
if (isset($link_settings[$view_mode])) {
if ($link = userreference_url_build_link($account, $instance, $view_mode, $alt_format)) {
$links[$target_type . '_' . $field_name] = $link;
}
}
}
}
return $links;
}
/**
* Build an individual link.
*
* Checks to ensure that the current user can be referenced by the field,
* ensures the current user has permission to create the field's node type,
* and builds the link based on the field's settings.
*
* @param object $account
* A fully loaded user object.
* @param object $instance
* A field instance.
* @param string $view_mode
* View mode, e.g. 'full'
* @param string $alt_format
* Optional. Use the alternative (safer but more verbose) format for
* generating the link. Defaults to FALSE.
*
* @return array
* An array containing properties to build a single link.
*/
function userreference_url_build_link($account, $instance, $view_mode = 'full', $alt_format = FALSE) {
$link = array();
// Load the $field, which contains field-level configuration (such as the
// Views and content type settings).
$field = field_info_field($instance['field_name']);
// Check if this widget is using a views listing.
if (module_exists('views') && !empty($field['settings']['view']['view_name'])) {
// TODO: Remove legacy function after final release of References module.
if (function_exists('user_reference_potential_references')) {
$referenceable = (bool) user_reference_potential_references($field, array('ids' => array($account->uid), 'limit' => 1));
}
else {
$referenceable = (bool) _user_reference_potential_references($field, '', NULL, array($account->uid), 1);
}
}
// Otherwise restrict by status and role(s).
else {
$referenceable = !empty($field['settings']['referenceable_status'][$account->status]);
foreach (array_keys($account->roles) as $rid) {
$referenceable |= !empty($field['settings']['referenceable_roles'][$rid]);
}
}
if ($referenceable && node_access('create', $instance['bundle'])) {
$link_settings = $instance['widget']['settings']['user_link'];
if (!empty($link_settings[$view_mode])) {
$link['title'] = t($link_settings['title']);
$link['query'] = array();
// Get the first "preferred" path for creating User Reference links.
$link_urls = variable_get('userreference_url_paths', array('node/add/%type/%uid'));
// Basic wildcard replacement: %type and %uid.
$link_url = $link_urls[0];
$link_url = str_replace('%type', str_replace('_', '-', $instance['bundle']), $link_url);
if ($alt_format) {
// The alternative format is used when there are multiple fields on the
// node edit form, so we can't just add an parameter at the end for UID.
$link_url = preg_replace('!/%uid$!', '', $link_url);
$field_name = str_replace('field_', '', $instance['field_name']);
$link['query'][$field_name] = $account->uid;
}
else {
$link_url = str_replace('%uid', $account->uid, $link_url);
}
$link['href'] = $link_url;
if (!empty($link_settings['hover_title'])) {
$link['attributes']['title'] = t($link_settings['hover_title']);
}
if (!empty($link_settings['destination'])) {
if ($link_settings['destination'] == 'source') {
$link['query']['destination'] = isset($_REQUEST['destination']) ? $_REQUEST['destination'] : $_GET['q'];
}
elseif ($link_settings['destination'] == 'user') {
$link['query']['destination'] = drupal_get_path_alias('user/' . $account->uid);
}
}
if (module_exists('og_context')) {
// First try to get context based on the current page URL.
$group_entity = og_get_context_by_url();
// Otherwise try getting the context based on the user being referenced.
if (!$group_entity) {
$group_entity = og_context();
}
if ($group_entity) {
$link['query']['gids'] = array($group_entity->gid);
}
}
}
}
return $link;
}
/**
* Helper function for themers to easily create a link.
*
* This function should be used in custom themes, rather than making manual
* links because it first checks a user's access before showing the link. If
* the user does not have access to create the node then an empty string will
* be returned.
*
* @param object $account
* The user object that will be referenced.
* @param string $field_name
* The name of the User Reference field.
* @param srting $type_name
* The name of node type that contains the User Reference field.
* @param array $attributes
* Optional. An array of additional attributes to add to the link.
*/
function userreference_url_create_link($account, $field_name, $type_name, $attributes = array()) {
$output = '';
$instance = field_info_instance('node', $field_name, $type_name);
$instance['widget']['settings']['user_link']['full'] = TRUE;
if ($link = userreference_url_build_link($account, $instance)) {
$options = array();
$link_attributes = isset($link['attributes']) ? (array) $link['attributes'] : array();
$options['attributes'] = $attributes + $link_attributes;
if ($link['query']) {
$options['query'] = $link['query'];
}
$output = l($link['title'], $link['href'], $options);
}
return $output;
}
/**
* Implements of hook_elements_info().
*
* Any FAPI callbacks needed for individual widgets can be declared here,
* and the element will be passed to those callbacks for processing.
*
* Drupal will automatically theme the element using a theme with
* the same name as the hook_elements key.
*
* Autocomplete_path is not used by text_widget but other widgets can use it
* (see nodereference and userreference).
*/
function userreference_url_element_info() {
return array(
'userreference_url' => array(
'#input' => TRUE,
'#columns' => array('uid'),
'#delta' => 0,
'#process' => array('_userreference_url_process'),
'#theme' => 'userreference_url',
'#theme_wrappers' => array('form_element'),
),
);
}
/**
* Implements hook_field_widget_settings_form().
*/
function userreference_url_field_widget_settings_form($field, $instance) {
$widget = $instance['widget'];
$defaults = field_info_widget_settings($widget['type']);
$settings = array_merge($defaults, $widget['settings']);
$form = array();
if ($widget['type'] == 'userreference_url') {
$form['fallback'] = array(
'#type' => 'radios',
'#title' => t('Fallback behavior'),
'#options' => array(
'autocomplete' => t('Use autocomplete widget'),
'select' => t('Use select list widget'),
'page_not_found' => t('Display page not found error'),
'leave_blank' => t('Leave the field blank'),
),
'#default_value' => isset($settings['fallback']) ? $settings['fallback'] : 'autocomplete',
'#description' => t('If user is not referenced in the URL, determine how the form should be handled.'),
'#required' => TRUE,
'#element_validate' => array('userreference_url_fallback_validate'),
'#weight' => -11,
);
$form['autocomplete_match'] = array(
'#type' => 'select',
'#title' => t('Autocomplete matching'),
'#default_value' => $settings['autocomplete_match'],
'#options' => array(
'starts_with' => t('Starts with'),
'contains' => t('Contains'),
),
'#description' => t('Select the method used to collect autocomplete suggestions. <br /> Note that <em>Contains</em> can cause performance issues on sites with thousands of users.'),
'#states' => array(
'visible' => array(
':input[name="instance[widget][settings][fallback]"]' => array('value' => 'autocomplete'),
),
),
'#weight' => -10,
);
$form['edit_fallback'] = array(
'#type' => 'checkbox',
'#title' => t('Use fallback behavior when editing content'),
'#default_value' => isset($settings['edit_fallback']) ? $settings['edit_fallback'] : FALSE,
'#weight' => -9,
);
$form['user_link'] = array(
'#tree' => TRUE,
'#type' => 'fieldset',
'#title' => t('Referenceable user links'),
'#element_validate' => array('userreference_url_user_link_validate'),
'#description' => t('These settings will automatically make a link on user that can be referenced. Clicking the link will take the user to the new node form and prepopulate the value of this user reference field.'),
);
$form['user_link']['full'] = array(
'#type' => 'checkbox',
'#title' => t('Create link on the full view'),
'#default_value' => isset($settings['user_link']['full']) ? $settings['user_link']['full'] : TRUE,
);
$form['user_link']['title'] = array(
'#type' => 'textfield',
'#title' => t('Link title'),
'#default_value' => isset($settings['user_link']['title']) ? $settings['user_link']['title'] : '',
'#description' => t('The title is the visible text for the link. This is required if you enable the content links.'),
);
$form['user_link']['hover_title'] = array(
'#type' => 'textfield',
'#title' => t('Link hover title'),
'#default_value' => isset($settings['user_link']['hover_title']) ? $settings['user_link']['hover_title'] : '',
'#description' => t('Text shown while hovering over the link.'),
);
$form['user_link']['destination'] = array(
'#type' => 'select',
'#title' => t('Return path'),
'#default_value' => isset($settings['user_link']['destination']) ? $settings['user_link']['destination'] : 'default',
'#options' => array(
'default' => t('The new node (no redirect)'),
'user' => t('The referenced user'),
'source' => t('The previous page'),
),
'#description' => t('After creating the new node through the link, determine where the user should be redirected.'),
);
}
return $form;
}
/**
* Implements hook_field_widget_error().
*/
function userreference_url_field_widget_error($element, $error, $form, &$form_state) {
form_error($element['uid'], $error['message']);
}
/**
* Element validation function to ensure invalid options are not selected.
*/
function userreference_url_fallback_validate($element, &$form_state) {
if ($form_state['values']['instance']['required'] &&
$form_state['values']['instance']['widget']['settings']['fallback'] == 'leave_blank') {
form_error($element, t('The fallback behavior cannot be left blank if this field is also required.'));
}
}
/**
* Element validation function that makes title required when creating a link.
*/
function userreference_url_user_link_validate($element, &$form_state, $form) {
$link_settings = $form_state['values']['instance']['widget']['settings']['user_link'];
if ($link_settings['full'] && empty($link_settings['title'])) {
form_error($element['title'], t('A link title must be specified if creating links on referenceable content.'));
}
}
/**
* Implements hook_field_widget_form().
*/
function userreference_url_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
$field_name = $field['field_name'];
$field_name_url = preg_replace('/^field_/', '', $field_name);
$referenced_uid = NULL;
$fallback = $instance['widget']['settings']['fallback'];
// Check for an existing UID.
if (isset($items[$delta]['uid']) && is_numeric($items[$delta]['uid'])) {
$referenced_uid = $items[$delta]['uid'];
}
// Check in the input array (used during AJAX requests).
elseif (!empty($form_state['input'][$field_name][$langcode][$delta]['uid'])) {
$referenced_uid = $form_state['input'][$field_name][$langcode][$delta]['uid'];
}
// Check for a reference in the query string.
elseif (isset($_GET[$field_name_url]) && is_numeric($_GET[$field_name_url])) {
$referenced_uid = $_GET[$field_name_url];
}
// Pull from the URL.
else {
$referenced_uid = userreference_url_get_uid($field_name);
}
// Check that the UID is a valid reference.
if (!empty($referenced_uid)) {
// TODO: Remove legacy function after final release of References module.
if (function_exists('user_reference_potential_references')) {
$reference = user_reference_potential_references($field, array('ids' => array($referenced_uid), 'limit' => 1));
}
else {
$reference = _user_reference_potential_references($field, '', 'equals', array($referenced_uid), 1);
}
if (empty($reference)) {
$referenced_uid = NULL;
}
}
// If no UID is available or editing this field, use the fallback behavior.
if (empty($referenced_uid) || (!empty($instance['widget']['settings']['edit_fallback']) && (empty($form['#node_edit_form']) || !empty($form['#node']->{$field_name}[$langcode])))) {
// If not on a node/add page (such as editing a node that does not yet have
// a reference), switch to using an autocomplete widget.
if (in_array($fallback, array('page_not_found', 'leave_blank')) && userreference_url_get_uid($field_name) === FALSE) {
$fallback = 'autocomplete';
}
// Page not found error.
// Check for the form_build_id to prevent throwing a page not found on
// manual builds. See http://drupal.org/node/397606.
if ($fallback == 'page_not_found') {
drupal_set_message(t('To create a new @type, a referenced piece of content must be specified in the link you followed.', array('@type' => $field['bundles']['node'][0])), 'error');
drupal_not_found();
exit();
}
// Fallback to select list.
elseif ($fallback == 'select') {
$options = user_reference_options_list($field);
if (module_exists('views') && !empty($field['settings']['view']['view_name'])) {
foreach ($options as $key => $value) {
$options[$key] = strip_tags($value);
}
}
$element += array(
'#type' => 'select',
'#default_value' => isset($items[$delta]['uid']) ? $items[$delta]['uid'] : NULL,
'#options' => array('' => t('- None -')) + $options,
);
}
// Fallback to autocomplete.
elseif ($fallback == 'autocomplete') {
$element += array(
'#type' => 'textfield',
'#default_value' => isset($items[$delta]['uid']) ? $items[$delta]['uid'] : NULL,
'#autocomplete_path' => 'user_reference/autocomplete/' . $instance['entity_type'] . '/' . $instance['bundle'] . '/' . $field['field_name'],
'#value_callback' => 'user_reference_autocomplete_value',
'#element_validate' => array('user_reference_autocomplete_validate'),
);
}
}
if (isset($referenced_uid) && (empty($element['#type']))) {
$element += array(
'#title' => $instance['label'],
'#type' => 'userreference_url',
'#field_name' => $field_name,
'#default_value' => $referenced_uid,
);
}
return array('uid' => $element);
}
/**
* Process an individual element.
*
* Build the form element. When creating a form using FAPI #process,
* note that $element['#value'] is already set.
*/
function _userreference_url_process($element, $form_state, $form) {
if (isset($element['#value']) && is_numeric($element['#value']) && ($user = user_load($element['#value']))) {
$element['#display_title'] = check_plain($user->name);
}
else {
$element['#display_title'] = t('Referenced content not found.');
}
$element['uid'] = array(
'#type' => 'hidden',
'#value' => isset($element['#value']) ? $element['#value'] : $element['#value'],
'#parents' => $element['#parents'],
);
return $element;
}
/**
* Check the current URL and pull the referenced user from it.
*/
function userreference_url_get_uid($field_name) {
$add_urls = variable_get('userreference_url_paths', array('node/add/%type/%uid'));
$field_name_url = preg_replace('/^field_/', '', $field_name);
$referenced_uid = NULL;
foreach ($add_urls as $url) {
$args = explode('/', $url);
foreach ($args as $part => $arg) {
// Set the target UID if matching on this part of the URL.
if ($arg == '%uid') {
$referenced_uid = arg($part);
}
// Set the target UID based on the field name, allowing for multiple
// references in the same URL.
elseif ($arg == '%' . $field_name_url) {
$referenced_uid = arg($part);
}
// Skip any other wildcards in the URL.
elseif (strpos($arg, '%') === 0) {
continue;
}
// Arguments must line up exactly if they're not a wildcard.
elseif (arg($part) != $arg) {
$referenced_uid = FALSE;
break;
}
}
if ($referenced_uid) {
break;
}
}
return $referenced_uid;
}
/**
* FAPI theme for an individual elements.
*
* This theme function controls the display of the widget when an existing item
* is being referenced.
*
* $element['#display_title'] contains the title of the item being referenced.
* $element['#field_name'] contains the field name.
* $element['#delta] is the position of this element in the group.
*/
function theme_userreference_url($variables) {
$element = $variables['element'];
$output = $element['#display_title'];
$output .= drupal_render_children($element);
return $output;
}