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
|
}
|