Incorporates patch role_expire-1280450-01.patch which provides
[project/role_expire.git] / role_expire.module
1 <?php
2
3 /**
4 * @file
5 * Role Expire module
6 *
7 * Enables user roles to expire on given time.
8 */
9
10
11 /*******************************************************************************
12 * API functions
13 ******************************************************************************/
14
15 /**
16 * API function; Get expiration time of a user role.
17 * @param $uid
18 * User ID.
19 * @param $rid
20 * Role ID.
21 * @return
22 * Array with the expiration time.
23 */
24 function role_expire_get_user_role_expiry_time($uid, $rid) {
25 $result = db_query("SELECT expiry_timestamp FROM {role_expire} WHERE uid = :uid AND rid = :rid", array(':uid' => $uid, ':rid' => $rid))->fetchField();
26 return (!empty($result)) ? $result : '';
27 }
28
29 /**
30 * API function; Get expiration of all roles of a user.
31 * @param $uid
32 * User ID.
33 * @return
34 * Array with the expiration time.
35 */
36 function role_expire_get_all_user_records($uid) {
37 $return = array();
38 $result = db_query("SELECT rid, expiry_timestamp FROM {role_expire} WHERE uid = :uid", array(':uid' => $uid));
39 foreach ($result as $row) {
40 $return[$row->rid] = $row->expiry_timestamp;
41 }
42 return $return;
43 }
44
45 /**
46 * API function; Delete a record from the database.
47 *
48 * @param $uid
49 * User ID.
50 * @param $rid
51 * Role ID.
52 * @return
53 */
54 function role_expire_delete_record($uid, $rid) {
55 db_delete('role_expire')
56 ->condition('uid', $uid)
57 ->condition('rid', $rid)
58 ->execute();
59 }
60 /**
61 * API function; Delete all user expirations.
62 *
63 * @param $uid
64 * User ID.
65 * @return
66 */
67 function role_expire_delete_user_records($uid) {
68 db_delete('role_expire')
69 ->condition('uid', $uid)
70 ->execute();
71 }
72
73
74 /**
75 * API function; Insert or update a record in the database.
76 *
77 * @param $uid
78 * User ID.
79 * @param $rid
80 * Role ID.
81 * @param $expiry_time
82 * The expiration timestamp.
83 */
84 function role_expire_write_record($uid, $rid, $expiry_timestamp) {
85 $existing = db_query("SELECT expiry_timestamp FROM {role_expire} WHERE uid = :uid AND rid = :rid", array(':uid' => $uid, ':rid' => $rid))->fetchField();
86 if ($existing && $expiry_timestamp != $existing) {
87 $result = db_update('role_expire')
88 ->fields(array(
89 'expiry_timestamp' => $expiry_timestamp,
90 ))
91 ->condition('uid', $uid)
92 ->condition('rid', $rid)
93 ->execute();
94 }
95 elseif ($existing == FALSE) {
96 $id = db_insert('role_expire')
97 ->fields(array(
98 'uid' => $uid,
99 'rid' => $rid,
100 'expiry_timestamp' => $expiry_timestamp,
101 ))
102 ->execute();
103 }
104 }
105
106
107 /**
108 * API function; Get the default duration for a role.
109 * @param $rid
110 * Required. The role_id to check.
111 * @return
112 * String containing the strtotime compatible default duration of the role
113 * or empty string if not set.
114 */
115 function role_expire_get_default_duration($rid) {
116 $result = db_query("SELECT duration FROM {role_expire_length} WHERE rid = :rid", array(':rid' => $rid))->fetchField();
117 return (!empty($result)) ? $result : '';
118 }
119
120
121 /**
122 * API function; Set the default expiry duration for a role.
123 *
124 * @param $rid
125 * Role ID.
126 * @param $duration
127 * The strtotime-compatible duration string.
128 */
129 function role_expire_set_default_duration($rid, $duration) {
130
131 if (!empty($duration)) {
132 $result = db_update('role_expire_length')
133 ->fields(array('duration' => check_plain($duration),))
134 ->condition('rid', $rid)
135 ->execute();
136
137 if (!$result) {
138 $id = db_insert('role_expire_length')
139 ->fields(array(
140 'rid' => $rid,
141 'duration' => check_plain($duration),
142 ))
143 ->execute();
144 }
145 }
146 }
147
148 /**
149 * API function; Delete default duration(s) for a role.
150 * @param $rid
151 * Required. The role_id to remove.
152 */
153 function role_expire_delete_default_duration($rid) {
154 db_delete('role_expire_length')
155 ->condition('rid', $rid)
156 ->execute();
157 }
158
159 /**
160 * API function; Get all records that should be expired.
161 *
162 * @param $time
163 * Optional. The time to check, if not set it will check current time.
164 */
165 function role_expire_get_expired($time = '') {
166 $return = array();
167 if (!$time) {
168 $time = REQUEST_TIME;
169 }
170 $result = db_query("SELECT rid, uid, expiry_timestamp FROM {role_expire} WHERE expiry_timestamp <= :expiry_timestamp", array(':expiry_timestamp' => $time));
171 foreach ($result as $row) {
172 $return[] = $row;
173 }
174 return $return;
175 }
176
177
178 /*******************************************************************************
179 * Hook implementations
180 ******************************************************************************/
181
182 /**
183 * Implements hook_views_api().
184 */
185 function role_expire_views_api() {
186 // We can't just return "what is the currently installed views API version!
187 // We have to specify which version of the API we designed this implementation
188 // around.
189 // return array("api" => views_api_version());
190 return array('api' => '3.0');
191 }
192
193 /**
194 * Implements hook_migrate_init().
195 */
196 function role_expire_migrate_init() {
197 // Don't load migration support unless we need it
198 $path = drupal_get_path('module', 'role_expire') . '/role_expire.migrate.inc';
199 include_once DRUPAL_ROOT . '/' . $path;
200 }
201
202 /**
203 * Implements hook_permission().
204 */
205 function role_expire_permission() {
206 return array(
207 'administer role expire' => array(
208 'title' => t('administer role expire'),
209 'description' => t('For each role, manage default role expiration dates (admin/people/permissions/roles/edit/#) and user role expiration dates (user/#/edit).'),
210 ),
211 );
212 }
213
214 /**
215 * Implements hook_form_FORM-ID_alter().
216 */
217 function role_expire_form_user_register_form_alter(&$form, $form_state) {
218 $form = array_merge_recursive($form, role_expire_add_expiration_input());
219 }
220
221 /**
222 * Implements hook_form_FORM-ID_alter().
223 */
224 function role_expire_form_user_profile_form_alter(&$form, $form_state) {
225 $form = array_merge_recursive($form, role_expire_add_expiration_input($form['#user']));
226 }
227
228 /**
229 * Implements hook_form_FORM-ID_alter().
230 */
231 function role_expire_form_user_admin_role_alter(&$form, $form_state) {
232 $form['role_expire'] = array(
233 '#title' => t("Default duration for the role %role",
234 array('%role' => drupal_ucfirst($form['name']['#default_value']))),
235 '#type' => 'textfield',
236 '#size' => 10,
237 '#default_value' => role_expire_get_default_duration($form['rid']['#value']),
238 '#maxlength' => 32,
239 '#attributes' => array('class' => array('role-expire-role-expiry')),
240 '#description' => t('Enter the time span you want to set as the default duration for this role. Examples: 12 hours, 1 day, 3 days, 4 weeks, 3 months, 1 year. Leave blank for no default duration. (If you speak php, this value may be any !l-compatible relative form.)',
241 array('!l' => l('strtotime', 'http://php.net/manual/en/function.strtotime.php'))
242 )
243 );
244 // Reposition the submit button and delete.
245 $form['submit']['#weight'] = 2;
246 if (arg(4)) {
247 $form['delete']['#weight'] = 3;
248 }
249 $form['#validate'][] = 'role_expire_user_admin_role_validate';
250 $form['#submit'][] = 'role_expire_user_admin_role_submit';
251 }
252
253 /**
254 * Form validation handler invoked by role_expire_form_user_admin_role_alter.
255 * Ensure that the specified duration is a valid, relative, positive strtotime-
256 * compatible string.
257 * @param $form
258 * @param $form_state
259 */
260 function role_expire_user_admin_role_validate($form, &$form_state) {
261 if (!empty($form_state['values']['role_expire'])) {
262 // Capture the duration from the form
263 $duration_string = check_plain($form_state['values']['role_expire']);
264 // Make sure it's a *relative* duration string. That is, it will result in a
265 // different strtotime when a different 'now' value is used.
266 $now = time();
267 $timestamp = strtotime($duration_string,$now);
268 $timestamp2 = strtotime($duration_string,$now-100);
269 if ($timestamp===FALSE || $timestamp < 0) {
270 form_set_error('role_expire', 'Role expiry default duration must be a strtotime-compatible string.');
271 } // invalid format
272 elseif ($timestamp < $now) {
273 form_set_error('role_expire', 'Role expiry default duration must be a <strong>future</strong> strtotime-compatible string.');
274 } // in the past
275 elseif ($timestamp == $timestamp2) {
276 // This is an absolute (or special) timestamp. That's not allowed.
277 form_set_error('role_expire', 'Role expiry default duration must be a <strong>relative</strong> strtotime-compatible string.');
278 } // not relative
279 } // !empty
280 }
281
282 /**
283 * Form submit handler invoked by role_expire_form_user_admin_role_alter.
284 * Updates default duration in database.
285 */
286 function role_expire_user_admin_role_submit($form, &$form_state) {
287 if ($form_state['values']['op'] == t('Delete role')) {
288 role_expire_delete_default_duration($form_state['values']['rid']);
289 }
290 elseif ($form_state['values']['op'] == t('Save role')) {
291
292 // If the form doesn't specify a default duration, then delete default duration.
293 // Otherwise, set the default duration to what's specified.
294 $new_default_duration = $form_state['values']['role_expire'];
295 if (is_numeric($new_default_duration) && $new_default_duration > 0) {
296 role_expire_set_default_duration($form_state['values']['rid'], $form_state['values']['role_expire']);
297 drupal_set_message('New default role expiration set.');
298 }
299 else {
300 role_expire_delete_default_duration($form_state['values']['rid']);
301 }
302 }
303 }
304
305 /**
306 * Implements hook_user_update().
307 */
308 function role_expire_user_update(&$edit, $account, $category) {
309 if ($category == 'account' && (user_access('administer role expire') || user_access('administer users'))) {
310
311 // Add roles expiry information for the user role.
312 foreach (array_keys($edit) as $name) {
313 if (strpos($name, 'role_expire_') === 0) {
314 $value = $edit[$name];
315 $rid = substr($name, strlen('role_expire_'));
316 if ($value != '' && array_key_exists($rid, $edit['roles'])) {
317 $expiry_timestamp = strtotime($value);
318 role_expire_write_record($account->uid, $rid, $expiry_timestamp);
319 }
320 else {
321 role_expire_delete_record($account->uid, $rid);
322 }
323 }
324 }
325
326 if (isset($edit['roles'])) {
327
328 // Add default expiration to any new roles that have been given to the user.
329 $new_roles = array_diff(array_keys($edit['roles']), array_keys($edit['original']->roles));
330 if (isset($new_roles)) {
331 // We have the new roles, loop over them and see whether we need to assign expiry to them.
332 foreach ($new_roles as $role_id) {
333 role_expire_process_default_role_duration_for_user($role_id, $account->uid);
334 }
335 }
336
337 // Remove expiration for roles that have been removed from the user.
338 $del_roles = array_diff(array_keys($edit['original']->roles), array_keys($edit['roles']));
339 if (isset($del_roles)) {
340 // We have the deleted roles, loop over them and remove their expiry info.
341 foreach ($del_roles as $role_id) {
342 role_expire_delete_record($account->uid, $role_id);
343 }
344 }
345
346 } // if edit[roles]
347
348 } // if category && user_access
349 }
350
351
352 /**
353 * Implements hook_user_insert().
354 */
355 function role_expire_user_insert(&$edit, $account, $category) {
356
357 if ($category == 'account' && (user_access('administer role expire') || user_access('administer users'))) {
358
359 // This adds default expiration to any new roles that have been given to the user.
360 $new_roles = array_keys($edit['roles']);
361 // We have the new roles, loop over them and see whether we need to assign expiry to them.
362 foreach ($new_roles as $role_id) {
363 role_expire_process_default_role_duration_for_user($role_id, $account->uid);
364 }
365 }
366 }
367
368 /**
369 * Implements hook_user_cancel().
370 */
371 function role_expire_user_cancel($edit, $account, $method) {
372 // Delete user records.
373 role_expire_delete_user_records($account->uid);
374 }
375
376 /**
377 * Implements hook_user_load().
378 */
379 function role_expire_user_load($users) {
380 // We don't load the information to the user object. Other modules can use
381 // our API to query the information.
382
383 /**
384 * Load the starter roles into a static cache so it is easy to
385 * see what has changed later on.
386 *
387 * TODO. Support multiple users that are being loaded here. Not sure yet
388 * what that means for Role Expire 7.
389 */
390 foreach ($users as $account) {
391 _role_static_user_roles($account->uid, $account->roles);
392 }
393
394 }
395
396 /**
397 * Implements hook_user_view().
398 */
399 function role_expire_user_view($account, $view_mode) {
400 global $user;
401 if (user_access('administer role expire') || user_access('administer users') || $user->uid == $account->uid) {
402 $roles = array();
403 $expiry_roles = role_expire_get_all_user_records($account->uid);
404 foreach ($account->roles as $key => $val) {
405 if (array_key_exists($key, $expiry_roles)) {
406 $roles[$key] = t("%role role expiration date: %timedate", array('%role' => ucfirst($val), '%timedate' => format_date($expiry_roles[$key])));
407 }
408 }
409 if ($roles) {
410 $account->content['summary']['role_expire'] = array(
411 '#type' => 'user_profile_item',
412 '#title' => t('Role expiration'),
413 '#markup' => theme('item_list', array('items' => $roles)),
414 '#attributes' => array('class' => array('role-expiry-roles')),
415 );
416 }
417 }
418 }
419
420 /**
421 * Implements hook_cron().
422 */
423 function role_expire_cron() {
424 if ($expires = role_expire_get_expired()) {
425 $roles = _role_expire_get_role();
426 foreach ($expires as $expire) {
427
428 // Remove the role expiration record from the role_expires table.
429 role_expire_delete_record($expire->uid, $expire->rid);
430
431 // Remove the role from the user.
432 // TODO Convert "user_load" to "user_load_multiple" if "$expire['uid']" is other than a uid.
433 // To return a single user object, wrap "user_load_multiple" with "array_shift" or equivalent.
434 // Example: array_shift(user_load_multiple(array(), $expire['uid']))
435 $account = user_load($expire->uid);
436
437 // If the account *does* exist, update it.
438 if (!empty($account)) {
439 $edit = $account->roles;
440 unset($edit[$expire->rid]);
441 // In the documentation for the role_expire implementation of hook_user we
442 // state to use $category = 'account'. We don't do that here because
443 // that would cause the delete to occur twice.
444 user_save($account, array('roles' => $edit), NULL);
445
446 // Handle the bizarre case of role_expire not being a valid role.
447 $role_name = (isset($roles[$expire->rid])) ? $roles[$expire->rid] : t('-unset-');
448 watchdog('role expire',
449 'Removed role @role from user @account.',
450 array('@role' => $role_name, '@account' => $account->name)
451 );
452 }
453 else {
454
455 // The account doesn't exist. Database altered outside of Drupal.
456 // Throw a warning message.
457 watchdog('role expire',
458 'Data integrity warning: Role_expire table updated, but no user with uid @uid.',
459 array('@uid' => $expire->uid,),
460 WATCHDOG_WARNING
461 );
462 }
463 }
464 }
465 }
466
467
468 /**
469 * Add form element that accepts the role expiration time.
470 *
471 * @param $account
472 * The user object.
473 * @return
474 * Form element.
475 */
476 function role_expire_add_expiration_input($account = NULL) {
477 $form = array();
478 if (user_access('administer users') || user_access('administer role expire')) {
479 drupal_add_js(drupal_get_path('module', 'role_expire') . '/role_expire.js');
480 $form['roles']['#attributes'] = array('class' => array('role-expire-roles'));
481
482 foreach (_role_expire_get_role() as $rid => $role) {
483 if (is_object($account) and array_key_exists('uid', $account)) {
484 $expiry_timestamp = role_expire_get_user_role_expiry_time($account->uid, $rid);
485 }
486 else {
487 $expiry_timestamp = '';
488 }
489
490 $form['role_expire_' . $rid] = array(
491 '#title' => t("%role role expiration date/time", array('%role' => drupal_ucfirst($role))),
492 '#type' => 'textfield',
493 '#default_value' => !empty($expiry_timestamp) ? date("Y-m-d H:i:s", $expiry_timestamp) : '',
494 '#attributes' => array('class' => array('role-expire-role-expiry')),
495 '#description' => t("Leave blank for default role expiry (never, or the duration you have set for the role), enter date and time in format: <em>yyyy-mm-dd hh:mm:ss</em> or use relative time i.e. 1 day, 2 months, 1 year, 3 years.")
496 );
497 }
498
499 $form['#validate'][] = '_role_expire_validate_role_expires';
500 }
501 return $form;
502 }
503
504 /*******************************************************************************
505 * Helper functions
506 ******************************************************************************/
507
508 /**
509 * Helper function; Store user roles for this page request.
510 * @return
511 * array of roles
512 */
513 function _role_static_user_roles($id, $roles = '') {
514 static $user_roles = array();
515 if (!isset($user_roles[$id]) && is_array($roles)) {
516 $user_roles[$id] = $roles;
517 }
518 if (!isset($user_roles[$id])) {
519 return FALSE;
520 }
521 else {
522 return $user_roles[$id];
523 }
524
525 }
526
527 /**
528 * Helper function; Get valid roles.
529 * @return unknown_type
530 */
531 function _role_expire_get_role() {
532 $roles = user_roles(TRUE);
533 unset($roles[DRUPAL_AUTHENTICATED_RID]);
534 return $roles;
535 }
536
537 /**
538 * Form validation handler for the role expiration on the user_profile_form().
539 *
540 * @see user_profile_form()
541 */
542 function _role_expire_validate_role_expires(&$form, &$form_state) {
543
544 $time = REQUEST_TIME;
545
546 foreach ($form_state['values'] as $name => $value) {
547 if (strpos($name, 'role_expire_') === 0 && trim($value) != '')
548 {
549 $expiry_time = strtotime($value);
550 if (!$expiry_time) {
551 form_set_error($name, t("Role expiry is not in correct format."));
552 }
553 if ($expiry_time <= $time) {
554 form_set_error($name, t("Role expiry must be in the future."));
555 }
556 }
557 }
558 }
559
560 /**
561 * Sets the default role duration for the current user/role combination.
562 * @param $role_id
563 * The ID of the role.
564 * @param $uid
565 * The user ID.
566 */
567 function role_expire_process_default_role_duration_for_user($role_id, $uid) {
568 // Does a default expiry exist for this role?
569 $default_duration = role_expire_get_default_duration($role_id);
570 if ($default_duration) {
571 $user_role_expiry = role_expire_get_user_role_expiry_time($uid, $role_id);
572 // If the expiry is empty then we act!.
573 if (!$user_role_expiry) {
574 // Use strtotime of default duration.
575 role_expire_write_record($uid, $role_id, strtotime($default_duration));
576 }
577 }
578 }