/[drupal]/contributions/modules/temporary_invitation/temporary_invitation.module
ViewVC logotype

Contents of /contributions/modules/temporary_invitation/temporary_invitation.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.27 - (show annotations) (download) (as text)
Wed Nov 4 10:03:46 2009 UTC (3 weeks, 1 day ago) by fago
Branch: MAIN
CVS Tags: DRUPAL-5--2-4, DRUPAL-5--2-3, HEAD
Changes since 1.26: +3 -3 lines
File MIME type: text/x-php
  fixing XSS problem when displaying invitations
1 <?php
2 // $Id: temporary_invitation.module,v 1.26 2009/02/11 21:34:56 jpetso Exp $
3 /**
4 * @file
5 * Temporary Invitation - Invite guests for a limited timespan.
6 *
7 * Copyright 2007, 2009 by Jakob Petsovits ("jpetso", http://drupal.org/user/56020)
8 * Copyright 2006 by D R Pratten <http://www.davidpratten.com>
9 */
10 include_once(drupal_get_path('module', 'temporary_invitation') .'/temporary_invitation_api.inc');
11 include_once(drupal_get_path('module', 'temporary_invitation') .'/temporary_invitation_rules.inc');
12
13
14 /**
15 * Implementation of hook_menu().
16 */
17 function temporary_invitation_menu($may_cache) {
18 global $user;
19 $items = array();
20
21 if ($may_cache) {
22 $items[] = array(
23 'path' => 'admin/user/temporary-invitation',
24 'title' => t('Temporary invitation'),
25 'description' => t('Custom configuration options for temporary invitations.'),
26 'callback' => 'drupal_get_form',
27 'callback arguments' => array('temporary_invitation_admin'),
28 'access' => user_access('manage temporary invitations'),
29 );
30 $items[] = array(
31 'path' => 'temporary-invitation-login',
32 'title' => t('User login'),
33 'callback' => 'temporary_invitation_login',
34 'access' => TRUE,
35 'type' => MENU_CALLBACK,
36 );
37 if ($user->uid && _temporary_invitation_get_show_menu('standard_item')) {
38 $items[] = array(
39 'path' => 'temporary-invitation',
40 'title' => t('My invitations'),
41 'callback' => 'drupal_get_form',
42 'callback arguments' => array('temporary_invitation_manage', $user->uid),
43 'access' => user_access('manage own temporary invitations')
44 || user_access('manage temporary invitations'),
45 );
46 }
47 }
48 else {
49 // Direct login path - only handle the alias, as the original path
50 // is already caught by the cached login path item.
51 $login_path = drupal_get_path_alias('temporary-invitation-login');
52
53 if ($login_path != 'temporary-invitation-login' // so it's an alias
54 && strpos($_GET['q'], $login_path) === 0) // starts with the login path
55 {
56 $arg_offset = substr_count($login_path, '/');
57 $items[] = array(
58 'path' => $login_path .'/'. arg($arg_offset + 1),
59 'title' => t('User login'),
60 'callback' => 'temporary_invitation_direct_login',
61 'callback arguments' => array(arg(1)),
62 'access' => TRUE,
63 'type' => MENU_CALLBACK,
64 );
65 }
66
67 // "Delete invitation" path. Aliases are not used here.
68 if (arg(0) == 'temporary-invitation' && arg(3) == 'delete' && is_numeric(arg(1))) {
69 $host_uid = arg(1);
70 $passcode_md5 = arg(2);
71 $items[] = array(
72 'path' => 'temporary-invitation/'. $host_uid .'/'. $passcode_md5 .'/delete',
73 'callback' => 'temporary_invitation_delete',
74 'callback arguments' => array($passcode_md5, TRUE),
75 'access' => TRUE, // access checks are done inside the delete function
76 'type' => MENU_CALLBACK,
77 );
78 }
79
80 $temporary_invitation_path = drupal_get_path_alias('temporary-invitation');
81
82 // "My account" tab
83 if (arg(0) == 'user' && is_numeric(arg(1)) && _temporary_invitation_get_show_menu('my_account')) {
84 $host_user = temporary_invitation_fast_user_load(arg(1));
85 $host_uid = $host_user->uid;
86
87 $items[] = array(
88 'path' => 'user/'. $host_uid .'/'. $temporary_invitation_path,
89 'title' => t('Invitations'),
90 'callback' => 'drupal_get_form',
91 'callback arguments' => array('temporary_invitation_manage', $host_uid),
92 'access' => ($user->uid == $host_uid && user_access('manage own temporary invitations'))
93 || user_access('manage temporary invitations'),
94 'type' => MENU_LOCAL_TASK,
95 );
96 }
97
98 // standard menu path
99 if (_temporary_invitation_get_show_menu('standard_item')
100 && strpos($_GET['q'], $temporary_invitation_path) === 0) {
101 // The path may be something like 'bla/invitations', so count the slashes
102 // in order to determine the arg() number of the host uid.
103 $arg_offset = substr_count($temporary_invitation_path, '/');
104 $host_uid = arg($arg_offset + 1);
105
106 if (is_numeric($host_uid)) {
107 $host_user = temporary_invitation_fast_user_load($host_uid);
108
109 if ($host_user !== FALSE) {
110 $host_uid = $host_user->uid;
111
112 $items[] = array(
113 'path' => $temporary_invitation_path .'/'. $host_uid,
114 'title' => ($user->uid == $host_uid)
115 ? t('My invitations')
116 : t('@user\'s invitations', array('@user' => $host_user->name)),
117 'callback' => 'drupal_get_form',
118 'callback arguments' => array('temporary_invitation_manage', $host_uid),
119 'access' => ($user->uid == $host_uid && user_access('manage own temporary invitations'))
120 || user_access('manage temporary invitations'),
121 'type' => MENU_CALLBACK,
122 );
123 }
124 }
125 } // end of standard menu path handling
126 } // end of ($may_cache == FALSE)
127 return $items;
128 }
129
130 /**
131 * Implementation of hook_perm().
132 */
133 function temporary_invitation_perm() {
134 return array('manage temporary invitations', 'manage own temporary invitations');
135 }
136
137
138 /**
139 * Load the corresponding user, but if the @p $uid is the one of
140 * the current user then return the global $user object instead.
141 *
142 * @return
143 * The user object corresponding to @p $uid or FALSE if there is no such user.
144 */
145 function temporary_invitation_fast_user_load($uid) {
146 global $user;
147
148 if (is_numeric($uid)) {
149 switch ($uid) {
150 case 0:
151 return FALSE;
152 case $user->uid:
153 return drupal_clone($user);
154 default:
155 return user_load(array('uid' => $uid));
156 }
157 }
158 }
159
160
161
162 /**
163 * The admin settings form.
164 */
165 function temporary_invitation_admin() {
166 $form = array();
167
168 // Fetch all roles except anonymous and authenticated user
169 $roles = user_roles(TRUE);
170 unset($roles[DRUPAL_AUTHENTICATED_RID]);
171
172 $form['temporary_invitation_show_menu'] = array(
173 '#type' => 'checkboxes',
174 '#title' => t('Show the invitation management page'),
175 '#options' => _temporary_invitation_get_show_menu('all'),
176 '#default_value' => _temporary_invitation_get_show_menu(),
177 );
178
179 $form['temporary_invitation_user_roles_default'] = array(
180 '#type' => 'checkboxes',
181 '#title' => t('Roles assigned to invited users'),
182 '#options' => $roles,
183 '#default_value' => _temporary_invitation_get_user_roles_default(),
184 '#description' => t('Every invited user is automatically assigned the \'authenticated user\' role plus the ones specified here.'),
185 );
186 $form['temporary_invitation_no_roles_for_existing_user'] = array(
187 '#type' => 'checkbox',
188 '#title' => t('Don\'t assign these roles to existing users'),
189 '#default_value' => !_temporary_invitation_existing_user_gets_roles(),
190 '#description' => t('If this option is enabled, the roles specified above are only assigned to users that are newly created from an invitation and didn\'t exist on this site before. Subsequently, users with an existing account are not assigned any additional roles.'),
191 );
192
193 $default_duration = _temporary_invitation_get_default_duration();
194 $form['default_duration'] = array(
195 '#type' => 'duration',
196 '#name' => 'temporary_invitation_default_duration',
197 '#title' => t('Default timespan until invitation expiry'),
198 '#description' => t('Specify how long an invitation will be valid by default. The host user can customize this timespan for each invited user.'),
199 '#default_metric' => $default_duration['metric'],
200 '#default_value' => $default_duration['value'],
201 '#required' => TRUE,
202 );
203
204 $form['temporary_invitation_flood_threshold'] = array(
205 '#type' => 'textfield',
206 '#title' => t('Maximum number of invitations per hour'),
207 '#default_value' => variable_get('temporary_invitation_flood_threshold', 20),
208 '#description' => t('In order to prevent spam abuse with invitation mails, you can define a limit on how many invitations may be sent per hour. Enter a positive number to enable that check, or 0 if there should be no limit.'),
209 '#required' => TRUE,
210 '#size' => 3,
211 );
212
213 $form['temporary_invitation_delete_action'] = array(
214 '#type' => 'select',
215 '#title' => t('When an invitation is deleted'),
216 '#options' => _temporary_invitation_get_delete_action('all'),
217 '#default_value' => _temporary_invitation_get_delete_action(),
218 '#description' => t('Determine what happens with the user that belongs to an invitation if the invitation is removed.'),
219 '#required' => TRUE,
220 );
221
222 // Login target path form elements.
223 $form['target'] = array(
224 '#type' => 'fieldset',
225 '#title' => t('Login target path'),
226 '#collapsible' => TRUE,
227 '#collapsed' => TRUE,
228 );
229 if (module_exists('token')) {
230 $replacement_description = t('Use the following replacement patterns for dynamically inserting text fragments into the login target path.');
231 }
232 else { // backwards compatibility
233 $replacement_description = t('Available variables: [invited-user:uid], [host-user:uid].');
234 }
235 $form['target']['temporary_invitation_login_target'] = array(
236 '#type' => 'textfield',
237 '#title' => t('Login target path'),
238 '#default_value' => _temporary_invitation_get_login_target(),
239 '#description' => t('The path where users are redirected to when they log in with their login codes. Enter \'&lt;front&gt;\' for the front page. !replacements', array('!replacements' => $replacement_description)),
240 '#required' => TRUE,
241 );
242 if (module_exists('token')) {
243 $form['target']['invitation_replacements'] = array(
244 '#type' => 'fieldset',
245 '#title' => t('Replacement patterns for invitation properties'),
246 '#collapsible' => TRUE,
247 '#collapsed' => TRUE,
248 );
249 $form['target']['invitation_replacements']['content'] = array(
250 '#value' => theme('token_help', 'temporary_invitation_with_passcode', '[invitation:', ']'),
251 );
252 $form['target']['invited_user_replacements'] = array(
253 '#type' => 'fieldset',
254 '#title' => t('Replacement patterns for invited user properties'),
255 '#collapsible' => TRUE,
256 '#collapsed' => TRUE,
257 );
258 $form['target']['invited_user_replacements']['content'] = array(
259 '#value' => theme('token_help', 'user', '[invited-user:', ']'),
260 );
261 $form['target']['host_user_replacements'] = array(
262 '#type' => 'fieldset',
263 '#title' => t('Replacement patterns for host user properties'),
264 '#collapsible' => TRUE,
265 '#collapsed' => TRUE,
266 );
267 $form['target']['host_user_replacements']['content'] = array(
268 '#value' => theme('token_help', 'user', '[host-user:', ']'),
269 );
270 }
271
272 // Construct the description text for the email body template.
273 if (module_exists('token')) {
274 $replacement_description = t('Use the following replacement patterns for dynamically inserting text fragments into invitation mails.');
275 }
276 else { // backwards compatibility
277 $mail_replacement_keys = array(
278 '[mail:subject]', '[mail:body]', '[host-user:mail]', '[host-user:user-raw]',
279 '[invitation:login-code]', '[invitation:direct-login-url]',
280 '[invitation:login-form-url]', '[invitation:expiration-date]',
281 '[invitation:site-name]', '[invitation:site-url]', '[invitation:site-date]'
282 );
283 $replacement_description = t('Available variables: !vars.',
284 array('!vars' => implode(', ', $mail_replacement_keys))
285 );
286 }
287
288 // Email template form elements.
289 $form['email'] = array(
290 '#type' => 'fieldset',
291 '#title' => t('Invitation e-mail'),
292 '#description' => t('When a user creates an invitation, the user-supplied mail body can (and should) be amended with additional information, including either the direct login URL or the login form URL and login code. How this e-mail looks like is defined by this template.'),
293 '#collapsible' => TRUE,
294 '#collapsed' => FALSE,
295 );
296 $form['email']['temporary_invitation_email_body'] = array(
297 '#type' => 'textarea',
298 '#title' => t('E-mail body'),
299 '#description' => t(
300 'The body of the invitation e-mail. !replacements',
301 array('!replacements' => $replacement_description)
302 ),
303 '#default_value' => _temporary_invitation_get_message_template('temporary_invitation_email_body'),
304 '#rows' => 15,
305 '#required' => TRUE,
306 );
307 if (module_exists('token')) {
308 $form['email']['invitation_replacements'] = array(
309 '#type' => 'fieldset',
310 '#title' => t('Replacement patterns for invitation properties'),
311 '#collapsible' => TRUE,
312 '#collapsed' => TRUE,
313 );
314 $form['email']['invitation_replacements']['content'] = array(
315 '#value' => theme('token_help', 'temporary_invitation_with_passcode', '[invitation:', ']'),
316 );
317 $form['email']['mail_replacements'] = array(
318 '#type' => 'fieldset',
319 '#title' => t('Replacement patterns for user-supplied mail properties'),
320 '#collapsible' => TRUE,
321 '#collapsed' => TRUE,
322 );
323 $form['email']['mail_replacements']['content'] = array(
324 '#value' => theme('token_help', 'temporary_invitation_mail', '[mail:', ']'),
325 );
326 $form['email']['host_user_replacements'] = array(
327 '#type' => 'fieldset',
328 '#title' => t('Replacement patterns for host user properties'),
329 '#collapsible' => TRUE,
330 '#collapsed' => TRUE,
331 );
332 $form['email']['host_user_replacements']['content'] = array(
333 '#value' => theme('token_help', 'user', '[host-user:', ']'),
334 );
335 }
336
337 return system_settings_form($form);
338 }
339
340 function temporary_invitation_admin_validate($form_id, $form_values) {
341 if (!is_numeric($form_values['temporary_invitation_flood_threshold'])
342 || $form_values['temporary_invitation_flood_threshold'] < 0) {
343 form_set_error('temporary_invitation_flood_threshold', t('Please set the per-hour limit of invitations to a positive number.'));
344 }
345 }
346
347 /**
348 * Admin form submit hook:
349 * Store settings with system_settings_form_submit(), assign user roles,
350 * and clear the menu cache so that possible changes in the menu are respected.
351 */
352 function temporary_invitation_admin_submit($form_id, $form_values) {
353 // Store current user roles, so we can compare them to the new ones later
354 $old_roles = _temporary_invitation_get_user_roles_default();
355 $old_existing_user_gets_roles = _temporary_invitation_existing_user_gets_roles();
356
357 // automatically stores the form values
358 $form_values['array_filter'] = TRUE; // only store checked checkbox keys
359 system_settings_form_submit($form_id, $form_values);
360
361 // assign the new roles to all invited users, but only if they have changed
362 $new_roles = _temporary_invitation_get_user_roles_default();
363 $new_existing_user_gets_roles = _temporary_invitation_existing_user_gets_roles();
364
365 if ($old_roles !== $new_roles
366 || $old_existing_user_gets_roles !== $new_existing_user_gets_roles) {
367 temporary_invitation_update_user_roles($old_roles, $new_roles);
368 drupal_set_message('The roles of all temporary users have been updated.');
369 }
370
371 // The management page can be a tab on "My account" and/or a standard menu
372 // item. And this is configurable by the admin, thus, clear the menu cache.
373 cache_clear_all('*', 'cache_menu', TRUE);
374 }
375
376
377
378 /**
379 * The user's own temporary invitation management form.
380 *
381 * @param $host_uid
382 * User ID of the user whose invitations shall be retrieved.
383 */
384 function temporary_invitation_manage($host_uid, $form_values = NULL) {
385 if (!is_array($form_values)) { // in case of more arg()s than there should be
386 unset($form_values);
387 }
388 if (isset($form_values)) {
389 if ($form_values['op'] == t('Send invitation e-mail')) {
390 unset($form_values); // Has been sent, clear all the values.
391 }
392 else if ($form_values['op'] == t('Preview invitation e-mail')) {
393 $preview = TRUE;
394 }
395 }
396 $form = array();
397
398 if ($preview) {
399 $host_user = temporary_invitation_fast_user_load($host_uid);
400
401 $invitation = new stdClass();
402 $invitation->passcode = t('[login-code]');
403 $invitation->created = time();
404 $invitation->ticket = new stdClass();
405 $invitation->ticket->expires =
406 temporary_invitation_duration_widget_add('duration', $form_values);
407
408 // Retrieve the mail object by means of a hook, so that third-party modules
409 // can extend it with additional form values if they choose to do so.
410 $mail = new stdClass();
411 module_invoke_all('temporary_invitation_mail_submit', $mail, $form_values);
412 $form['#mail'] = $mail;
413
414 $template = isset($mail->template)
415 ? $mail->template
416 : 'temporary_invitation_email_body';
417
418 $body = _temporary_invitation_get_message_template($template);
419 $body = temporary_invitation_mail_replace($body, $invitation, $mail, $host_user);
420
421 $form['preview'] = array(
422 '#type' => 'markup',
423 '#value' => '',
424 '#prefix' => '<div class="temporary-invitation-preview">',
425 '#suffix' => '</div>',
426 );
427 $form['preview']['name'] = array(
428 '#type' => 'hidden',
429 '#value' => $form_values['name'],
430 );
431 $form['preview']['invited_email'] = array(
432 '#type' => 'hidden',
433 '#value' => $form_values['invited_email'],
434 );
435 $form['preview']['email_subject'] = array(
436 '#type' => 'hidden',
437 '#value' => $form_values['email_subject'],
438 );
439 $form['preview']['email_body'] = array(
440 '#type' => 'hidden',
441 '#value' => $form_values['email_body'],
442 );
443 $form['preview']['duration_metric'] = array(
444 '#type' => 'hidden',
445 '#value' => $form_values['duration_metric'],
446 );
447 $form['preview']['duration_value'] = array(
448 '#type' => 'hidden',
449 '#value' => $form_values['duration_value'],
450 );
451
452 $form['preview']['description'] = array(
453 '#type' => 'markup',
454 '#value' => t('Please check the e-mail address, subject and contents of your invitation mail before it is sent to %invited-user.', array('%invited-user' => $form_values['name'])),
455 '#required' => TRUE,
456 );
457 $form['preview']['address'] = array(
458 '#type' => 'item',
459 '#title' => t('E-mail address of the invited user'),
460 '#value' => check_plain($mail->to),
461 );
462 $form['preview']['subject'] = array(
463 '#type' => 'item',
464 '#title' => t('E-mail subject'),
465 '#value' => check_plain($mail->subject),
466 );
467 $form['preview']['body'] = array(
468 '#type' => 'item',
469 '#title' => t('E-mail contents'),
470 '#value' => '<pre>'. check_plain($body) .'</pre>',
471 );
472
473 $metric_options = _temporary_invitation_duration_metric_options();
474 $form['preview']['duration'] = array(
475 '#type' => 'item',
476 '#title' => t('Invitation expires in'),
477 '#value' => t('!number !duration-metric', array(
478 '!number' => $form_values['duration_value'],
479 '!duration-metric' => $metric_options[$form_values['duration_metric']],
480 )),
481 );
482
483 $form['preview']['copy_to_self'] = array(
484 '#type' => 'checkbox',
485 '#title' => t('Send me a copy.'),
486 '#default_value' => $mail->copy_to_self,
487 );
488 $form['preview']['host_uid'] = array(
489 '#type' => 'value',
490 '#value' => $host_uid,
491 );
492
493 $form['preview']['back'] = array(
494 '#type' => 'submit',
495 '#value' => t('Back'),
496 '#weight' => 10,
497 );
498 $form['preview']['submit'] = array(
499 '#type' => 'submit',
500 '#value' => t('Send invitation e-mail'),
501 '#weight' => 11,
502 );
503 }
504 else { // $preview == FALSE
505 $overview_table = temporary_invitation_overview($host_uid);
506
507 $form['add'] = array(
508 '#type' => 'fieldset',
509 '#title' => t('Create new invitation'),
510 '#description' => t('Using this form, you can invite people to this site by sending an invitation e-mail to the specified e-mail address. Login information for the invited user will automatically be added to the contents of this e-mail.'),
511 '#collapsible' => TRUE,
512 '#collapsed' => !(isset($form_values) || empty($overview_table)),
513 '#prefix' => '<div class="temporary-invitation-input">',
514 '#suffix' => '</div>',
515 );
516
517 $form['add']['name'] = array(
518 '#type' => 'textfield',
519 '#title' => t('Who to invite'),
520 '#description' => t('The name of the person or company that you want to invite. This can be any text you like, its primary purpose is to tell your invitations apart from each other if you have got more of them.'),
521 '#required' => TRUE,
522 );
523 $form['add']['invited_email'] = array(
524 '#type' => 'textfield',
525 '#title' => t('E-mail address of the invited user'),
526 '#description' => t('The invitation e-mail will be sent to this address.'),
527 '#required' => TRUE,
528 );
529 $form['add']['email_subject'] = array(
530 '#type' => 'textfield',
531 '#title' => t('E-mail subject'),
532 '#required' => TRUE,
533 );
534 $form['add']['email_body'] = array(
535 '#type' => 'textarea',
536 '#title' => t('E-mail contents'),
537 '#rows' => 15,
538 '#required' => TRUE,
539 );
540 $form['add']['copy_to_self'] = array(
541 '#type' => 'hidden',
542 '#value' => isset($form_values['copy_to_self']) ? $form_values['copy_to_self'] : 1,
543 );
544
545 $default_duration = _temporary_invitation_get_default_duration();
546 $form['add']['duration'] = array(
547 '#type' => 'duration',
548 '#name' => 'duration',
549 '#title' => t('Invitation expires in'),
550 '#description' => t('The invited user will be able to log in as long as you specify here.'),
551 '#default_metric' => $default_duration['metric'],
552 '#default_value' => $default_duration['value'],
553 '#required' => TRUE,
554 );
555
556 $form['add']['submit'] = array(
557 '#type' => 'submit',
558 '#value' => t('Preview invitation e-mail'),
559 '#weight' => 11,
560 );
561
562 $form['overview'] = array(
563 '#value' => $overview_table,
564 );
565 }
566
567 $form['#multistep'] = TRUE;
568 $form['#redirect'] = FALSE;
569 return $form;
570 }
571
572 /**
573 * Display the given user's existing temporary invitations in a table.
574 *
575 * @param $host_uid
576 * User ID of the user whose invitations shall be retrieved.
577 * @param $destination_path
578 * The destination path that will be opened when a "Delete" link is followed.
579 * If NULL (which is the default), the current path is used as destination.
580 * @param $include_expired
581 * If TRUE (which is the default), the resulting table also includes
582 * invitations that are already expired. Otherwise, only valid invitations
583 * are included in the table.
584 */
585 function temporary_invitation_overview($host_uid, $destination_path = NULL, $include_expired = TRUE) {
586 $invitations = temporary_invitation_load_array($host_uid, $include_expired);
587
588 if (count($invitations) == 0) {
589 return '';
590 }
591
592 $header = array(t('Name'), t('Created on'), t('Valid until'), '');
593 $rows = array();
594 $rows_expired = array(); // those come at the bottom of the list
595
596 if (isset($destination_path)) {
597 $destination = 'destination='. drupal_urlencode($destination_path);
598 }
599 else {
600 $destination = drupal_get_destination();
601 }
602 foreach($invitations as $invitation) {
603 $delete_link = l(t('delete'),
604 'temporary-invitation/'. $host_uid .'/'. $invitation->ticket->passcode_md5 .'/delete',
605 array(), $destination
606 );
607 $created_date = empty($invitation->created)
608 ? t('(unknown)')
609 : format_date($invitation->created);
610
611 if (time() < $invitation->ticket->expires) {
612 $expires_text = format_date($invitation->ticket->expires);
613 $rows[] = array(
614 check_plain($invitation->name), $created_date, $expires_text, $delete_link,
615 );
616 }
617 else {
618 $expires_text = t('expired');
619 $rows_expired[] = array(
620 check_plain($invitation->name), $created_date, $expires_text, $delete_link,
621 );
622 }
623 }
624 foreach ($rows_expired as $row) {
625 $rows[] = $row;
626 }
627
628 return theme('table', $header, $rows, array('class' => 'temporary-invitation'));
629 }
630
631 /**
632 * Invitation management form validation hook:
633 * Don't let invalid mail addresses or empty mail subjects/bodies through.
634 */
635 function temporary_invitation_manage_validate($form_id, $form_values) {
636 // If something is entered at all, validate the e-mail address.
637 // (Otherwise, the '#required' property takes care of this.)
638 $invited_email = $form_values['invited_email'];
639
640 if (!empty($invited_email)) {
641 // Check for a valid mail address.
642 if ($error = user_validate_mail($invited_email)) {
643 form_set_error('invited_email', $error);
644 }
645 }
646
647 // If defined by the admin, restrict the number of invitations per hour.
648 $threshold = variable_get('temporary_invitation_flood_threshold', 20);
649
650 if ($threshold != 0 && !flood_is_allowed('temporary_invitation', $threshold)) {
651 form_set_error('email_body', t('You have exceeded the allowed number of invitations per hour. Please wait some time before sending out more invitations.'));
652 }
653 }
654
655 /**
656 * Invitation management form submit hook:
657 * Evaluate the expiration time, and create a new invitation for the user.
658 */
659 function temporary_invitation_manage_submit($form_id, $form_values) {
660 if ($form_values['op'] != t('Send invitation e-mail')) {
661 return;
662 }
663 $expiration_time = temporary_invitation_duration_widget_add('duration', $form_values);
664 $host_user = user_load(array('uid' => $form_values['host_uid']));
665
666 $mail = new stdClass();
667 module_invoke_all('temporary_invitation_mail_submit', $mail, $form_values);
668
669 temporary_invitation_create($host_user, $form_values['name'], $mail, $expiration_time);
670 flood_register_event('temporary_invitation');
671
672 // Go to the destination path if it has been passed in the request.
673 if (isset($_REQUEST['destination'])) {
674 drupal_goto();
675 }
676 }
677
678 /**
679 * Implementation of hook_temporary_invitation_mail_submit():
680 * Extend the given @p $mail object with form values of the manage/send form
681 * if the "Preview" or "Send" buttons have been pressed.
682 */
683 function temporary_invitation_temporary_invitation_mail_submit(&$mail, $form_values) {
684 $mail->to = $form_values['invited_email'];
685 $mail->subject = $form_values['email_subject'];
686 $mail->body = $form_values['email_body'];
687 $mail->copy_to_self = $form_values['copy_to_self'];
688 }
689
690
691
692 /**
693 * Implementation of hook_block():
694 * Provide a login block for entering passcodes.
695 */
696 function temporary_invitation_block($op = 'list', $delta = 0, $edit = array()) {
697 global $user;
698
699 if ($op == 'list') {
700 $blocks[0] = array('info' => t('Temporary invitation user login'));
701 return $blocks;
702 }
703 else if ($op == 'view') {
704 $block = array();
705
706 switch ($delta) {
707 case 0:
708 // Provide a login block for anonymous users only, and don't show
709 // the block if the current page is already the login form.
710 if (!$user->uid && !(arg(0) == 'temporary-invitation-login')) {
711 $block['subject'] = t('User login');
712 $block['weight'] = -1;
713 $block['content'] = drupal_get_form('temporary_invitation_login_form');
714 }
715 return $block;
716 }
717 }
718 }
719
720 /**
721 * Implementation of hook_form_alter():
722 * Hide the user login block when the temporary invitation login form is shown.
723 */
724 function temporary_invitation_form_alter($form_id, &$form) {
725 switch ($form_id) {
726 case 'user_login_block':
727 if ($_GET['q'] == 'temporary-invitation-login') {
728 $form = array();
729 }
730 return;
731 }
732 }
733
734 /**
735 * Page callback for "temporary-invitation-login":
736 * The user login form that accepts passcodes as a means of authentication.
737 */
738 function temporary_invitation_login($passcode = NULL) {
739 if (isset($passcode)) {
740 return temporary_invitation_direct_login($passcode);
741 }
742 else {
743 return drupal_get_form('temporary_invitation_login_form');
744 }
745 }
746
747 function temporary_invitation_login_form() {
748 global $user;
749 $form = array();
750
751 if ($user->uid) {
752 $form['warning'] = array(
753 '#value' => t('You are already logged in. Note that by entering a valid login code, you will be logged out and assigned another user identity instead.'),
754 );
755 }
756 $form['passcode'] = array(
757 '#type' => 'textfield',
758 '#title' => t('Login code'),
759 '#maxlength' => 60,
760 '#size' => 15,
761 '#required' => TRUE,
762 );
763 $form['submit'] = array(
764 '#type' => 'submit',
765 '#value' => t('Log in'),
766 );
767 return $form;
768 }
769
770 /**
771 * Validate handler for the login form:
772 * Try to log the user with the related passcode in.
773 * If the login fails, issue a form error.
774 */
775 function temporary_invitation_login_form_validate($form_id, $form_values) {
776 $user = loginticket_login('temporary_invitation', $form_values['passcode']);
777 if ($user === FALSE) {
778 form_set_error('login', t('Sorry, the entered login code is not valid or has already expired.'));
779 }
780 }
781
782 /**
783 * Submit handler for the login form:
784 * Determine the login target path and redirect there.
785 */
786 function temporary_invitation_login_form_submit($form_id, $form_values) {
787 unset($_REQUEST['destination']); // don't let anyone disturb us.
788 return _temporary_invitation_get_login_target($form_values['passcode']);
789 }
790
791 /**
792 * Page callback for "temporary-invitation-login/$passcode":
793 * Log in with the passcode directly in the URL, bypassing the login form.
794 */
795 function temporary_invitation_direct_login($passcode) {
796 $success = loginticket_login('temporary_invitation', $passcode);
797 if (!$success) {
798 return t('Sorry, the entered login code is not valid or has already expired.');
799 }
800
801 // Write the target path directly to $_REQUEST['destination']
802 // so that possible previous destination assignments (e.g. in hook_user())
803 // are disregarded.
804 $_REQUEST['destination'] = _temporary_invitation_get_login_target($passcode);
805 drupal_goto();
806 }
807
808
809
810 /**
811 * Determine if the menu item is shown in the specified location.
812 *
813 * @param $location
814 * If set to 'all', an associative array consisting of all available
815 * menu location keys and descriptions is returned.
816 * If set to 'selected', the function returns an array that contains
817 * the currently selected keys.
818 * If set to 'my_account' or 'standard_item', the function returns a boolean
819 * value which specifies if the given location is visible/accessible or not.
820 */
821 function _temporary_invitation_get_show_menu($which = 'selected') {
822 if ($which == 'all') {
823 return array(
824 'my_account' => t('As a tab in "My account"'),
825 'standard_item' => t('As a standard menu item'),
826 );
827 }
828
829 $selected = variable_get('temporary_invitation_show_menu', array('my_account'));
830
831 if ($which == 'selected') {
832 return $selected;
833 }
834 else {
835 return in_array($which, $selected);
836 }
837 }
838
839 /**
840 * Retrieve the login path for invited users who are logging in with their
841 * passcode.
842 *
843 * @param $passcode
844 * Optional parameter: if given, the returned menu path will enable
845 * Token replacement for the login target path, using the invitation
846 * and its related users as replacement patterns.
847 */
848 function _temporary_invitation_get_login_target($passcode = NULL) {
849 $target = variable_get('temporary_invitation_login_target', '<front>');
850
851 if (isset($passcode)) {
852 if (module_exists('token')) {
853 $invitation = temporary_invitation_load(md5($passcode));
854 $invitation->passcode = $passcode;
855 $host_user = temporary_invitation_get_host_user($invitation);
856 $invited_user = temporary_invitation_get_invited_user($invitation);
857
858 $target = token_replace($target, 'temporary_invitation_with_passcode', $invitation, '[invitation:', ']');
859 $target = token_replace($target, 'user', $host_user, '[host-user:', ']');
860 $target = token_replace($target, 'user', $invited_user, '[invited-user:', ']');
861 }
862 else { // backwards compatibility
863 $replacements = array(
864 '[invited-user:uid]' => $invitation->invited_uid,
865 '[host-user:uid]' => $invitation->host_uid,
866 );
867 $target = strtr($target, $replacements);
868 }
869 }
870 return $target;
871 }
872
873 /**
874 * Retrieve the default timespan for valid invitations.
875 *
876 * @return An associative array with keys 'metric' and 'value'.
877 */
878 function _temporary_invitation_get_default_duration() {
879 return array(
880 'metric' => variable_get('temporary_invitation_default_duration_metric', 'mon'),
881 'value' => variable_get('temporary_invitation_default_duration_value', 1),
882 );
883 }
884
885 /**
886 * Implementation of hook_temporary_invitation():
887 * Notify the host and/or invited user about the newly created invitation
888 * by means of a notification email and a short Drupal message.
889 */
890 function temporary_invitation_temporary_invitation($op, $invitation, $passcode = NULL, $mail = NULL) {
891 switch ($op) {
892 case 'insert':
893 global $user;
894 $host_user = drupal_clone($user);
895
896 drupal_set_message(
897 t('Your invitation has been registered, and the notification email will be mailed out shortly.')
898 );
899
900 $invitation->passcode = $passcode;
901 $template = isset($mail->template)
902 ? $mail->template
903 : 'temporary_invitation_email_body';
904
905 $body = _temporary_invitation_get_message_template($template);
906 $body = temporary_invitation_mail_replace($body, $invitation, $mail, $host_user);
907
908 // Send a notification email to the invited and host users.
909 drupal_mail(
910 'temporary-invitation-created',
911 $mail->to, $mail->subject, $body, $host_user->mail
912 );
913 if ($mail->copy_to_self) {
914 drupal_mail(
915 'temporary-invitation-created',
916 $host_user->mail, $mail->subject, $body, $host_user->mail
917 );
918 }
919
920 if (module_exists('workflow_ng')) {
921 workflow_ng_invoke_event(
922 'temporary_invitation_insert', $invitation, $mail
923 );
924 }
925 return;
926
927 case 'login':
928 $invitation->passcode = $passcode;
929 if (module_exists('workflow_ng')) {
930 workflow_ng_invoke_event(
931 'temporary_invitation_login', $invitation
932 );
933 }
934 return;
935
936 case 'delete':
937 if (module_exists('workflow_ng')) {
938 workflow_ng_invoke_event(
939 'temporary_invitation_delete', $invitation
940 );
941 }
942 return;
943
944 default:
945 return;
946 }
947 }
948
949 /**
950 * Replace tokens of an invitation mail text with actual properties of the
951 * given invitation object.
952 *
953 * @param $body
954 * The mail body string, probably including replacement tokens.
955 * @param $invitation
956 * The invitation object, including the properties "passcode", "created"
957 * and "ticket", the latter being a (login ticket) object of which only the
958 * "expires" property is being used.
959 * @param $mail
960 * The user-supplied mail text (which will be fed into the mail template),
961 * including the properties "to", "subject" and "body".
962 * @param $host_user
963 * A Drupal user object holding the properties of the host user.
964 *
965 * @return
966 * The @p $body string after token replacement.
967 */
968 function temporary_invitation_mail_replace($body, $invitation, $mail, $host_user) {
969 global $base_url;
970
971 if (module_exists('token')) {
972 $body = token_replace($body,
973 'temporary_invitation_with_passcode', $invitation, '[invitation:', ']'
974 );
975 $body = token_replace($body, 'user', $host_user, '[host-user:', ']');
976 $body = token_replace($body, 'temporary_invitation_mail', $mail, '[mail:', ']');
977 }
978 else { // backwards compatibility
979 $login_form_url = url('temporary-invitation-login', NULL, NULL, TRUE);
980 $direct_login_url = $login_form_url .'/'. drupal_urlencode($invitation->passcode);
981
982 $mail_replacements = array(
983 '[invitation:login-code]' => $invitation->passcode,
984 '[invitation:direct-login-url]' => $direct_login_url,
985 '[invitation:login-form-url]' => $login_form_url,
986 '[invitation:creation-date]' => format_date($invitation->created, 'short'),
987 '[invitation:expiration-date]' => format_date($invitation->ticket->expires, 'short'),
988 '[invitation:site-name]' => variable_get('site_name', 'Drupal'),
989 '[invitation:site-url]' => $base_url,
990 '[invitation:site-date]' => format_date(time(), 'short', '', variable_get('date_default_timezone', 0)),
991 '[host-user:mail]' => $host_user->mail,
992 '[host-user:user-raw]' => $host_user->name,
993 '[mail:to]' => $mail->to,
994 '[mail:subject]' => $mail->subject,
995 '[mail:body]' => $mail->body,
996 );
997 $body = strtr($body, $mail_replacements);
998 }
999 foreach (module_implements('temporary_invitation_mail_body_alter') as $module) {
1000 $function = $module .'_temporary_invitation_mail_body_alter';
1001 $body = $function($body, $invitation, $mail, $host_user);
1002 }
1003 return $body;
1004 }
1005
1006 /**
1007 * Implementation of hook_token_list().
1008 */
1009 function temporary_invitation_token_list($type = 'all') {
1010 $tokens = array();
1011
1012 if ($type == 'temporary_invitation' || $type == 'temporary_invitation_with_passcode' || $type == 'all') {
1013 $login_form_url = url('temporary-invitation-login', NULL, NULL, TRUE);
1014 $login_form_link = l($login_form_url, $login_form_url);
1015 $current_date = format_date(time(), 'short');
1016
1017 $creation_tokens = array(
1018 'login-code' => t('Login code, e.g. "!code"', array('!code' => 'EbN2F3')),
1019 'direct-login-url' => t('URL for direct user login, including the login code, e.g. !url',
1020 array('!url' => $login_form_url .'/EbN2F3')),
1021 );
1022 $general_tokens = array(
1023 'login-form-url' => t('URL of the login form, which is !link.',
1024 array('!link' => $login_form_link)),
1025 'creation-date' => t('Creation date/time of the invitation, e.g. "@time"',
1026 array('@time' => $current_date)),
1027 'expiration-date' => t('Expiration date/time of the invitation, e.g. "@time"',
1028 array('@time' => $current_date)),
1029 );
1030 $tokens['temporary invitation'] = ($type == 'temporary_invitation_with_passcode')
1031 ? $creation_tokens + $general_tokens
1032 : $general_tokens;
1033 }
1034 if ($type == 'temporary_invitation_mail' || $type == 'all') {
1035 $tokens['temporary invitation mail'] = array(
1036 'to' => t('E-mail address of the invited user.'),
1037 'subject' => t('E-mail subject, as plaintext. WARNING - raw user input.'),
1038 'subject-filtered' => t('E-mail subject, as filtered plaintext (not recommoned for usage in e-mails)'),
1039 'body' => t('E-mail body, as plaintext. WARNING - raw user input.'),
1040 'body-filtered' => t('E-mail body, as filtered plaintext (not recommoned for usage in e-mails)'),
1041 );
1042 }
1043 return $tokens;
1044 }
1045
1046 /**
1047 * Implementation of hook_token_values().
1048 */
1049 function temporary_invitation_token_values($type, $object = NULL) {
1050 $tokens = array();
1051
1052 if ($type == 'temporary_invitation' || $type == 'temporary_invitation_with_passcode') {
1053 $invitation = $object;
1054 $login_form_url = url('temporary-invitation-login', NULL, NULL, TRUE);
1055
1056 if (isset($invitation->passcode)) {
1057 $tokens['login-code'] = $invitation->passcode;
1058 $tokens['direct-login-url'] = $login_form_url .'/'. $invitation->passcode;
1059 }
1060 $timezone = variable_get('date_default_timezone', 0);
1061
1062 $tokens['login-form-url'] = $login_form_url;
1063 $tokens['expiration-date'] = format_date($invitation->ticket->expires, 'short', '', $timezone);
1064 $tokens['creation-date'] = empty($invitation->created)
1065 ? t('(unknown)')
1066 : format_date($invitation->created, 'short', '', $timezone);
1067 }
1068 if ($type == 'temporary_invitation_mail') {
1069 $mail = $object;
1070 $tokens['to'] = $mail->to;
1071 $tokens['subject'] = $mail->subject;
1072 $tokens['subject-filtered'] = check_plain($mail->subject);
1073 $tokens['body'] = $mail->body;
1074 $tokens['body-filtered'] = check_plain($mail->body);
1075 }
1076 return $tokens;
1077 }
1078
1079 /**
1080 * Retrieve the currently set message.
1081 */
1082 function _temporary_invitation_get_message_template($which) {
1083 // Check if an admin setting overrides the default string.
1084 if ($admin_setting = variable_get($which, FALSE)) {
1085 return $admin_setting;
1086 }
1087 $function = $which .'_default_template';
1088
1089 if (function_exists($function)) {
1090 return $function();
1091 }
1092 else {
1093 drupal_set_message('Error in temporary_invitation.module:
1094 No such message template ("'. $which .'")!', 'warning'
1095 );
1096 }
1097 }
1098
1099 /**
1100 * Implementation of temporary_invitation's [variable]_default_template() callback.
1101 */
1102 function temporary_invitation_email_body_default_template() {
1103 return t(
1104 '[mail:body]
1105
1106 ----
1107 [host-user:user-raw] has invited you to the [invitation:site-name] web site.
1108 You can now log in until [invitation:expiration-date] by following this link:
1109 [invitation:direct-login-url]
1110
1111 Keep in mind that you will not be able to log in anymore when
1112 your invitation has expired, or if it is revoked by user [host-user:user-raw].
1113
1114 -- [invitation:site-name] team'
1115 );
1116 }

  ViewVC Help
Powered by ViewVC 1.1.2