Project

General

Profile

Paste
Download (12.1 KB) Statistics
| Branch: | Revision:

root / drupal7 / sites / all / modules / field_permissions / field_permissions.module @ a8cee257

1
<?php
2

    
3
/**
4
 * @file
5
 * This is the main script for the Field Permissions module. It merely contains
6
 * the implementation of hooks invoked by Drupal core and CCK.
7
 * All common functions are externalized into several scripts that are included
8
 * on demand to save memory consumption during normal site operation.
9
 */
10

    
11
/**
12
 * Indicates that a field does not have any access control.
13
 */
14
define('FIELD_PERMISSIONS_PUBLIC', 0);
15

    
16
/**
17
 * Indicates that a field is private.
18
 *
19
 * Private fields are never displayed, and are only editable by the author (and
20
 * by site administrators with the 'access private fields' permission).
21
 */
22
define('FIELD_PERMISSIONS_PRIVATE', 1);
23

    
24
/**
25
 * Indicates that a field has custom permissions.
26
 */
27
define('FIELD_PERMISSIONS_CUSTOM', 2);
28

    
29
/**
30
 * Implements hook_help().
31
 */
32
function field_permissions_help($path, $arg) {
33
  switch ($path) {
34
    // Main module help for the Field Permissions module.
35
    case 'admin/help#field_permissions':
36
      return '<p>' . t('Set field-level permissions to edit or view CCK fields in any node, edit field during node creation, and edit or view permissions for nodes owned by the current user.') . '</p>';
37

    
38
    // Help for the Field Permissions overview page.
39
    case 'admin/reports/fields/permissions':
40
      return '<p>' . t('Report and troubleshoot field permissions.') . '</p>';
41
  }
42
}
43

    
44
/**
45
 * Implements hook_menu().
46
 */
47
function field_permissions_menu() {
48
  $items['admin/reports/fields/list'] = array(
49
    'title' => 'List',
50
    'type' => MENU_DEFAULT_LOCAL_TASK,
51
    'weight' => -10,
52
  );
53
  $items['admin/reports/fields/permissions'] = array(
54
    'title' => 'Permissions',
55
    'description' => 'Report and troubleshoot field permissions.',
56
    'page callback' => 'field_permissions_overview',
57
    'access arguments' => array('administer field permissions'),
58
    'file' => 'field_permissions.admin.inc',
59
    'type' => MENU_LOCAL_TASK,
60
    'weight' => 0,
61
  );
62
  return $items;
63
}
64

    
65
/**
66
 * Implementation of hook_permission().
67
 */
68
function field_permissions_permission() {
69
  module_load_include('inc', 'field_permissions', 'field_permissions.admin');
70
  return _field_permissions_permission();
71
}
72

    
73
/**
74
 * Implements of hook_form_FORM_ID_alter().
75
 */
76
function field_permissions_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
77
  // Injects the Field Permissions settings on the Edit field tab.
78
  form_load_include($form_state, 'inc', 'field_permissions', 'field_permissions.admin');
79
  return _field_permissions_field_settings_form_alter($form, $form_state, $form_id);
80
}
81

    
82
/**
83
 * Implements hook_field_permissions_userid_ENTITY_TYPE_alter().
84
 */
85
function field_permissions_field_permissions_userid_field_collection_item_alter(&$uid, $entity) {
86
  $uid = isset($entity->hostEntity()->uid) ? $entity->hostEntity()->uid : $uid;
87
}
88

    
89
/**
90
 * Implementation of hook_field_access().
91
 *
92
 * @param $op
93
 *   The operation to be performed. Possible values:
94
 *   - 'edit'
95
 *   - 'view'
96
 * @param $field
97
 *   The field on which the operation is to be performed.
98
 * @param $entity_type
99
 *   The type of entity; e.g. 'node' or 'user'.
100
 * @param $entity
101
 *   The entity on which the operation is to be performed.
102
 * @param $account
103
 *   The account to check.
104
 *
105
 * @return
106
 *   FALSE if the operation is not allowed.
107
 *   Note when field_access() is invoked, access is granted unless one
108
 *   implementation of hook_field_access() explicitly returns FALSE.
109
 *
110
 * @see field_access()
111
 */
112
function field_permissions_field_access($op, $field, $entity_type, $entity, $account) {
113
  // Ignore the request if permissions have not been enabled for this field.
114
  if (!isset($field['field_permissions']['type']) || $field['field_permissions']['type'] == FIELD_PERMISSIONS_PUBLIC) {
115
    return;
116
  }
117
  // If the field is private, then only the author (and administrators with the
118
  // 'access private fields' permissions) can view and edit it.
119
  elseif ($field['field_permissions']['type'] == FIELD_PERMISSIONS_PRIVATE) {
120
    if (isset($entity)) {
121
      return _field_permissions_entity_is_owned_by_account($entity, $account) || user_access('access private fields', $account);
122
    }
123
    // If the entity does not exist, we must check if there is access to any
124
    // entity; see comments in field_permissions_empty_entity_access(). In this
125
    // case that will always be true, since private fields are always editable
126
    // by their authors and in theory any user account can be the author of
127
    // some entity on the site.
128
    else {
129
      return TRUE;
130
    }
131
  }
132
  // Otherwise, check access by permission.
133
  elseif ($field['field_permissions']['type'] == FIELD_PERMISSIONS_CUSTOM) {
134
    // Allow other modules to deny access first.
135
    $result = module_invoke_all('field_permissions_custom_field_access', $op, $field, $entity_type, $entity, $account);
136
    if (in_array(FALSE, $result)) {
137
      return FALSE;
138
    }
139

    
140
    if (!isset($entity)) {
141
      return field_permissions_empty_entity_access($op, $field['field_name'], $account);
142
    }
143
    elseif ($op == 'view') {
144
      return _field_permissions_field_view_access($field['field_name'], $entity_type, $entity, $account);
145
    }
146
    elseif ($op == 'edit') {
147
      return _field_permissions_field_edit_access($field['field_name'], $entity_type, $entity, $account);
148
    }
149
  }
150
}
151

    
152
/**
153
 * Determines custom field permissions access when the entity is unknown.
154
 *
155
 * When a module calls field_access() without providing an entity (which the
156
 * API allows it to do), it is doing so in order to check generic access to the
157
 * field. Therefore, we should only deny access if we know that there is no
158
 * entity anywhere on the site for which the user has access to the provided
159
 * field.
160
 *
161
 * For example, Views calls field_access('view') without providing the entity,
162
 * in order to determine if the field can be included in the query itself. So
163
 * we only want to return FALSE if we know that there are no entities for which
164
 * access will be granted. Later on, Views will invoke field_access('view')
165
 * again, indirectly, when rendering the fields using field_view_field(), and
166
 * at that point the entity will be passed along so we can do our normal checks
167
 * on it.
168
 *
169
 * As another example, the FileField Sources module uses field_access('edit')
170
 * as a menu access callback for the IMCE file browser and does not pass along
171
 * the entity. So we must return TRUE here if there is any entity for which the
172
 * user is allowed to edit the field (otherwise the user would not have access
173
 * to the IMCE file browser interface when editing the fields they do have
174
 * permission to edit).
175
 *
176
 * @param $op
177
 *   The operation to be performed ('view' or 'edit').
178
 * @param $field_name
179
 *   The name of the field whose access is being checked.
180
 * @param $account
181
 *   The user account whose access is being checked.
182
 *
183
 * @return
184
 *   TRUE if access should be allowed, or FALSE if it shouln't.
185
 */
186
function field_permissions_empty_entity_access($op, $field_name, $account) {
187
  $all_permissions['view'] = array(
188
    'view ' . $field_name,
189
    'view own ' . $field_name,
190
  );
191
  $all_permissions['edit'] = array(
192
    'create ' . $field_name,
193
    'edit ' . $field_name,
194
    'edit own ' . $field_name,
195
  );
196

    
197
  // If there's any scenario where the user might have permission to perform
198
  // the operation on the field, return TRUE.
199
  if (isset($all_permissions[$op])) {
200
    foreach ($all_permissions[$op] as $permission) {
201
      if (user_access($permission, $account)) {
202
        return TRUE;
203
      }
204
    }
205
  }
206

    
207
  return FALSE;
208
}
209

    
210
/**
211
 * Implementation of hook_field_access('view').
212
 */
213
function _field_permissions_field_view_access($field_name, $entity_type, $entity, $account) {
214
  // Check if user has access to view this field in any entity.
215
  if (user_access('view ' . $field_name, $account)) {
216
    return TRUE;
217
  }
218

    
219
  // If the user has permission to view entities that they own, return TRUE if
220
  // they own this entity or FALSE if they don't.
221
  if (user_access('view own ' . $field_name, $account)) {
222
    return _field_permissions_entity_is_owned_by_account($entity, $account);
223
  }
224

    
225
  return FALSE;
226
}
227

    
228
/**
229
 * Implementation of hook_field_access('edit').
230
 */
231
function _field_permissions_field_edit_access($field_name, $entity_type, $entity, $account) {
232
  // If this is a new entity, check if the user has access to edit the field on
233
  // entity creation.
234
  if (isset($entity->is_new)) {
235
    // Some entities provide an "is_new" property. If that is present, respect
236
    // whatever it's set to.
237
    $is_new = $entity->is_new;
238
  }
239
  else {
240
    // Otherwise, try to find out if the entity is new by checking its ID. Note
241
    // that checking empty() rather than !isset() is important here, to deal
242
    // with the case of entities that store "0" as their ID while the final
243
    // entity is in the process of being created (user accounts are a good
244
    // example of this).
245
    list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
246
    $is_new = empty($id);
247
  }
248
  if ($is_new) {
249
    return user_access('create ' . $field_name, $account);
250
  }
251

    
252
  // Check if user has access to edit this field in any entity.
253
  if (user_access('edit ' . $field_name, $account)) {
254
    return TRUE;
255
  }
256

    
257
  // If the user has permission to edit entities that they own, return TRUE if
258
  // they own this entity or FALSE if they don't.
259
  if (user_access('edit own ' . $field_name, $account)) {
260
    return _field_permissions_entity_is_owned_by_account($entity, $account);
261
  }
262

    
263
  return FALSE;
264
}
265

    
266
/**
267
 * Returns TRUE if an entity is owned by a user account, FALSE otherwise.
268
 */
269
function _field_permissions_entity_is_owned_by_account($entity, $account) {
270
  // Try to get the uid of the entity owner from the entity itself. If it's not
271
  // set (for example, if the entity type does not store a uid or does not have
272
  // a concept of "ownership"), we need to assume that the provided user
273
  // account does not own it.
274
  $uid = isset($entity->uid) ? $entity->uid : FALSE;
275
  if (method_exists($entity, 'entityType')) {
276
    drupal_alter('field_permissions_userid_' . $entity->entityType(), $uid, $entity);
277
  }
278

    
279
  return $uid === $account->uid;
280
}
281

    
282
/**
283
 * Implements hook_features_pipe_COMPONENT_alter().
284
 *
285
 * Add field permissions to features when exporting a field_base.
286
 */
287
function field_permissions_features_pipe_field_base_alter(&$pipe, $data, $export) {
288
  // Validate if there are field_base components that will be exported for this
289
  // feature.
290
  if (isset($export['features']['field_base'])) {
291
    module_load_include('inc', 'field_permissions', 'field_permissions.admin');
292
    // Iterate through the exported field_base components for this feature and
293
    // add the defined field permissions.
294
    foreach ($export['features']['field_base'] as $field_name) {
295
      $field = field_info_field($field_name);
296
      if (isset($field['field_permissions']['type']) && $field['field_permissions']['type'] == FIELD_PERMISSIONS_CUSTOM) {
297
        $perms = field_permissions_list_field_permissions($field, '');
298
        foreach ($perms as $perm => $info) {
299
          $pipe['user_permission'][] = $perm;
300
        }
301
      }
302
    }
303
  }
304
}
305

    
306
/**
307
 * Implements hook_field_attach_form().
308
 */
309
function field_permissions_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
310
  // Some fields are validated if they are #required even if field's #access
311
  // property is set to false. For example: file/image fields, options fields.
312
  foreach (element_children($form) as $key) {
313
    if (isset($form[$key]['#access']) && !$form[$key]['#access']) {
314
      _field_permissions_make_elements_non_required($form[$key]);
315
    }
316
  }
317
}
318

    
319
/**
320
 * Sets the #required property to FALSE recursively on form elements.
321
 */
322
function _field_permissions_make_elements_non_required(&$elements) {
323
  if (!is_array($elements)) {
324
    return;
325
  }
326
  if (!empty($elements['#required'])) {
327
    $elements['#required'] = FALSE;
328
  }
329
  foreach (element_children($elements) as $key) {
330
    _field_permissions_make_elements_non_required($elements[$key]);
331
  }
332
}
333

    
334
/**
335
 * Implements hook_field_delete_field().
336
 */
337
function field_permissions_field_delete_field($field) {
338
  // Delete any permissions related to the deleted field.
339
  $all_permissions = array_keys(field_permissions_permission());
340
  if (!empty($all_permissions)) {
341
    db_delete('role_permission')
342
      ->condition('module', 'field_permissions')
343
      ->condition('permission', $all_permissions, 'NOT IN')
344
      ->execute();
345
  }
346
}