-
Notifications
You must be signed in to change notification settings - Fork 3
/
signup.module
1787 lines (1680 loc) · 63.6 KB
/
signup.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
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?php
// $Id: signup.module,v 1.242 2009/10/08 00:01:24 dww Exp $
/**
* @file
* The Signup module (http://drupal.org/project/signup) manages replies to
* nodes. In particular, it's good for event management. Signup supports
* sending reminder emails and automatically closing signups for nodes with
* a start time, via the Event module (http://drupal.org/project/event) or
* with a CCK date field (http://drupal.org/project/date). Signup provides
* extensive Views integration (http://drupal.org/project/views). For more
* information, see the README.txt and INSTALL.txt files in this directory.
*/
/**
* @defgroup signup_core Core drupal hooks
*/
/**
* Implementation of hook_theme(), the theme registry.
*/
function signup_theme() {
$path = drupal_get_path('module', 'signup') .'/theme';
return array(
'signup_admin_page' => array(
'file' => 'admin.inc',
'path' => $path,
'arguments' => array(
'filter_status_form' => NULL,
'signup_admin_form' => NULL,
),
),
'signup_filter_status_form' => array(
'file' => 'admin.inc',
'path' => $path,
'arguments' => array(
'form' => NULL,
),
),
'signup_admin_form' => array(
'file' => 'admin.inc',
'path' => $path,
'arguments' => array(
'form' => NULL,
),
),
'signup_email_token_custom_data' => array(
'file' => 'email.inc',
'path' => $path,
'arguments' => array(
'signup_data' => NULL,
),
),
'signup_custom_data_email' => array(
'file' => 'email.inc',
'path' => $path,
'arguments' => array(
'data' => NULL,
),
),
'signup_custom_data_field_text' => array(
'file' => 'email.inc',
'path' => $path,
'arguments' => array(
'key' => NULL,
'value' => NULL,
),
),
'signup_broadcast_sender_copy' => array(
'file' => 'email.inc',
'path' => $path,
'arguments' => array(
'raw_message' => NULL,
'cooked_message' => NULL,
),
),
'signup_user_list' => array(
'file' => 'no_views.inc',
'path' => $path,
'arguments' => array(
'node' => NULL,
'registered_signups' => NULL,
'anon_signups' => NULL,
),
),
'signup_user_schedule' => array(
'file' => 'no_views.inc',
'path' => $path,
'arguments' => array(
'node' => NULL,
),
),
'signup_node_admin_page' => array(
'file' => 'node.admin.inc',
'path' => $path,
'arguments' => array(
'node' => NULL,
'signup_node_admin_summary_form' => NULL,
'signup_node_admin_details_form' => NULL,
'signup_form' => NULL,
),
),
'signup_node_admin_summary_form' => array(
'file' => 'node.admin.inc',
'path' => $path,
'arguments' => array(
'form' => NULL,
),
),
'signup_node_admin_details_form' => array(
'file' => 'node.admin.inc',
'path' => $path,
'arguments' => array(
'form' => NULL,
),
),
'signup_custom_data' => array(
'file' => 'node.admin.inc',
'path' => $path,
'arguments' => array(
'data' => NULL,
),
),
'signup_attended_text' => array(
'file' => 'node.admin.inc',
'path' => $path,
'arguments' => array(
'attended' => NULL,
),
),
'signup_signups_closed' => array(
'file' => 'node.inc',
'path' => $path,
'arguments' => array(
'node' => NULL,
'current_signup' => '',
),
),
'signup_anonymous_user_login_text' => array(
'file' => 'node.inc',
'path' => $path,
'arguments' => array(
'anon_login_text' => NULL,
),
),
'signup_node_output_header' => array(
'file' => 'node.inc',
'path' => $path,
'arguments' => array(
'node' => NULL,
),
),
'signup_anonymous_username' => array(
'file' => 'signup_form.inc',
'path' => $path,
'arguments' => array(
'form_data' => NULL,
'email' => NULL,
),
),
'signup_settings_view_label' => array(
'file' => 'admin.settings.inc',
'path' => drupal_get_path('module', 'signup') .'/includes',
'arguments' => array(
'view' => NULL,
),
),
'signup_node_settings_form_panes' => array(
'file' => 'node_settings.inc',
'path' => drupal_get_path('module', 'signup') . '/includes',
'arguments' => array('form'),
),
);
}
/**
* Implementation of hook_init().
*/
function signup_init() {
_signup_initialize_scheduler_backend();
}
/**
* Implementation of hook_cron().
*
* There are two cron-based tasks that can be performed by the signup module:
* sending reminder emails to nodes that will begin soon, and auto-closing
* signup on nodes that have already started (depending on settings). Each of
* these tasks is rather complicated and depends on the specific date-based
* backend module that's currently installed (if any), so each one is handled
* in a separate helper function.
*
* @ingroup signup_core
* @see _signup_cron_send_reminders()
* @see _signup_cron_autoclose()
*/
function signup_cron() {
module_load_include('inc', 'signup', 'includes/cron');
_signup_initialize_scheduler_backend();
_signup_cron_send_reminders();
_signup_cron_autoclose();
}
/**
* Private query builder helper function.
*
* @param $common_sql
* Nested array of shared query fragments that are common to all date-based
* backends. The supported keys are:
* 'primary': the query's primary table and its alias (required).
* 'fields': array of fields to SELECT.
* 'joins': array of JOIN statements for other tables.
* 'where': array of WHERE clauses.
* 'group_by': array of GROUP BY fields.
*
* @param $backend_sql
* Similar nested array provided by the date-based backend include file,
* except that 'primary' is not allowed.
*
* @return
* Complete SQL statement based on the given query fragments.
*/
function _signup_build_query($common_sql, $type_sql) {
// Combine type-specific sql with common_sql.
$full_sql = array_merge_recursive($common_sql, $type_sql);
$sql = 'SELECT '. implode(', ', $full_sql['fields']);
$sql .= ' FROM '. $common_sql['primary'] .' ';
if (!empty($full_sql['joins'])) {
$sql .= implode(' ', $full_sql['joins']);
}
if (!empty($full_sql['where'])) {
$sql .= ' WHERE ('. implode(') AND (', $full_sql['where']) .')';
}
if (!empty($full_sql['group_by'])) {
$sql .= ' GROUP BY '. implode(', ', $full_sql['group_by']);
}
return $sql;
}
/**
* Implementation of hook_help().
*
* @ingroup signup_core
*/
function signup_help($path, $arg) {
switch ($path) {
case 'admin/help#signup':
return '<p>'.
t('Signup allows users to sign up (in other words, register) for content of any type. The most common use is for events, where users indicate they are planning to attend. This module includes options for sending a notification email to a selected email address upon a new user signup (good for notifying event coordinators, etc.) and a confirmation email to users who sign up. Each of these options are controlled per node. When used on event nodes (with <a href="@event_url">event.module</a> installed) or nodes that have a date field (with <a href="@date_url">date.module</a>) and regular cron runs, it can also send out reminder emails to all signups a configurable number of days before the start of the event (also controlled per node) and to automatically close signups 1 hour before their start (general setting). Settings exist for resticting signups to selected roles and content types.', array('@event_url' => url('http://drupal.org/project/event'), '@date_url' => url('http://drupal.org/project/date')))
.'</p><p>'.
t('To use signup, you must enable which content types should allow signups in administer->settings->content types, and you must also grant the %sign_up_for_content permission to any user role that should be able to sign up in administer->access control. Each signup-enabled node will now have a place for users to sign up.', array('%sign_up_for_content' => 'sign up for content'))
.'</p><p>'.
t('There are two ways for a user to have administrative access to the signup information about a given node: either the user has the %administer_signups_for_own_content permission and they are viewing a node they created, or the user has the global %administer_all_signups permission. Administrative access allows a user to view all the users who have signed up for the node, along with whatever information they included when they signed up. Signup administrators can also cancel other user\'s signups for the node, and can close signups on the node entirely (meaning no one else is allowed to sign up).', array('%administer_signups_for_own_content' => 'administer signups for own content', '%administer all signups' => 'administer all signups'))
.'</p><p>'.
t('Default settings for notification email address, reminder emails and confirmation emails are located in administer->settings->signup. These will be the default values used for a signup node unless otherwise specified (to configure these options per node, visit \'edit\' for that node and make the adjustments in the \'Sign up settings\' section).')
.'</p><p>'.
t('Signups can be manually closed for any node at the %signup_overview page, or on the \'signups\' tab on each node.', array('%signup_overview' => t('Signup overview')))
.'</p><p>'.
t('The user signup form is fully themable -- form fields may be added or deleted. For more details see the instructions in signup.theme, where a sample user form is included.')
.'</p>';
}
// If we're still here, consider the URL for help on various menu tabs.
if (($node = menu_get_object()) && arg(2) == 'signups') {
switch (arg(3)) {
case 'broadcast':
return '<p>'. t('This page allows you to send an email message to every user who signed up for this %node_type.', array('%node_type' => node_get_types('name', $node->type))) .'</p>';
case 'add':
return '<p>'. t('This page allows you to sign up another user for this %node_type.', array('%node_type' => node_get_types('name', $node->type))) .'</p>';
}
}
// See if we need to add our extra checking and validation while configuring
// CCK node types for signup. We only want to do this if the $path
// doesn't contain 'help#' since hook_help() is invoked twice on admin
// pages.
if (!empty($arg[0]) && $arg[0] == 'admin' && $arg[1] == 'content'
&& function_exists('signup_date_check_node_types')
) {
if ($arg[2] == 'types' && $arg[3] != 'add') {
signup_date_check_node_types();
}
elseif ($arg[2] == 'node-type') {
// Thanks to stupid core handling of node types with underscores, we
// need to convert this back to the actual machine-readable type name.
signup_date_check_node_types(str_replace('-', '_', $arg[3]));
}
}
}
/**
* Implementation of hook_menu().
*
* @ingroup signup_core
*/
function signup_menu() {
$path = drupal_get_path('module', 'signup') .'/includes';
$items = array();
$items['admin/settings/signup'] = array(
'description' => 'Configure settings for signups.',
'access arguments' => array('administer all signups'),
'page callback' => 'drupal_get_form',
'page arguments' => array('signup_settings_form'),
'title' => user_access('access administration pages') ? 'Signup' : 'Signup settings',
'file' => 'admin.settings.inc',
'file path' => $path,
);
$items['signup/cancel/%signup_menu/%'] = array(
'description' => 'View all signup-enabled posts, and open or close signups on them.',
'type' => MENU_CALLBACK,
'access callback' => '_signup_menu_signup_access',
'access arguments' => array(2, 'cancel'),
'page callback' => 'signup_cancel_signup_page',
'page arguments' => array(2, 3),
'file' => 'signup_cancel.inc',
'file path' => $path,
);
$items['signup/edit/%signup_menu'] = array(
'title' => 'Edit signup',
'page callback' => 'signup_edit_page',
'page arguments' => array(2),
'access callback' => '_signup_menu_signup_access',
'access arguments' => array(2, 'edit'),
'type' => MENU_LOCAL_TASK,
'file' => 'signup_edit_form.inc',
'file path' => $path,
);
$items['admin/content/signup'] = array(
'description' => 'View all signup-enabled posts, and open or close signups on them.',
'access arguments' => array('administer all signups'),
'page callback' => 'signup_admin_page',
'title' => 'Signup administration',
'file' => 'admin.signup_administration.inc',
'file path' => $path,
);
// Conditionally add any available signup-related tabs to nodes.
$items['node/%node/signups'] = array(
'title' => 'Signups',
'page callback' => 'signup_node_tab_page',
'page arguments' => array(1),
'access callback' => '_signup_menu_access',
'access arguments' => array(1, 'any'),
'type' => MENU_LOCAL_TASK,
'weight' => 20,
);
$items['node/%node/signups/signup'] = array(
'title' => 'Sign up',
'type' => MENU_DEFAULT_LOCAL_TASK,
'access callback' => '_signup_menu_access',
'access arguments' => array(1, 'signup'),
'weight' => -10,
);
$items['node/%node/signups/list'] = array(
'title' => 'List',
'page callback' => 'signup_user_list_output',
'page arguments' => array(1),
'access callback' => '_signup_menu_access',
'access arguments' => array(1, 'list-tab'),
'type' => MENU_LOCAL_TASK,
'weight' => -5,
'file' => 'node_output.inc',
'file path' => $path,
);
$items['node/%node/signups/admin'] = array(
'title' => 'Administer',
'page callback' => 'signup_node_admin_page',
'page arguments' => array(1),
'access callback' => '_signup_menu_access',
'access arguments' => array(1, 'admin'),
'type' => MENU_LOCAL_TASK,
'weight' => 0,
'file' => 'node_admin.inc',
'file path' => $path,
);
$items['node/%node/signups/settings'] = array(
'title' => 'Settings',
'page callback' => 'signup_node_settings_page',
'page arguments' => array(1),
'access callback' => '_signup_menu_access',
'access arguments' => array(1, 'admin'),
'type' => MENU_LOCAL_TASK,
'weight' => 1,
'file' => 'node_settings.inc',
'file path' => $path,
);
$items['node/%node/signups/confirm'] = array(
'page callback' => 'drupal_get_form',
'page arguments' => array('signup_cancel_multiple_confirm', 1),
'access callback' => '_signup_menu_access',
'access arguments' => array(1, 'admin'),
'type' => MENU_CALLBACK,
'file' => 'node_admin.inc',
'file path' => $path,
);
$items['node/%node/signups/add'] = array(
'title' => 'Add',
'page callback' => 'signup_node_admin_add_user_page',
'page arguments' => array(1),
'access callback' => '_signup_menu_access',
'access arguments' => array(1, 'add'),
'type' => MENU_LOCAL_TASK,
'weight' => 5,
'file' => 'signup_form.inc',
'file path' => $path,
);
$items['node/%node/signups/broadcast'] = array(
'title' => 'Signup broadcast',
'page callback' => 'drupal_get_form',
'page arguments' => array('signup_broadcast_form', 1),
'access callback' => '_signup_menu_access',
'access arguments' => array(1, 'broadcast'),
'type' => MENU_LOCAL_TASK,
'weight' => 10,
'file' => 'broadcast.inc',
'file path' => $path,
);
// Add extra menu items if we're not using views.
if (!module_exists('views')) {
module_load_include('inc', 'signup', 'includes/no_views');
signup_no_views_menu($items);
}
return $items;
}
/**
* Determine menu access for a given type of signup menu item.
*
* This ensures that the node is signup enabled, and that that the current
* user should have permission to view the requested menu item type.
*
* @param $node
* The fully loaded node object from the menu autoloader.
* @param $menu_type
* String specifying what kind of menu item to test access for. Can be:
* 'signup': the signup form
* 'list': the signup attendee listing
* 'list-tab': the signup attendee listing tab
* 'admin': the signup administration tab
* 'add': the signup administration tab to add other users (requires
* that signups are currently open on the given node).
* 'broadcast': for the broadcast tab
* 'any': if the user has permission to see any of these
*
* @return
* TRUE if the current node is signup enabled and the current user has
* permisison to access to requested menu item, otherwise FALSE.
*
* @see signup_menu()
*/
function _signup_menu_access($node, $menu_type = 'node') {
global $user;
// If the node isn't signup enabled, immediately return failure.
if (empty($node->signup)) {
return FALSE;
}
// For certain menu types, invoke a hook to allow other modules to alter the
// access behavior for signup menu items. Just relying on hook_menu_alter()
// for this won't work, since there are places outside of the menu system,
// where we call this function to decide if a user should have access to
// something. If multiple modules return a value, the logical OR is used, so
// if anyone returns TRUE, access is granted.
if (in_array($menu_type, array('signup', 'list', 'admin', 'add', 'broadcast'))) {
$access_array = module_invoke_all('signup_menu_access', $node, $menu_type);
if (!empty($access_array)) {
// Return TRUE if any values are TRUE, otherwise, FALSE.
return in_array(TRUE, $access_array);
}
}
// No module returned a value in hook_signup_menu_access, so continue with
// the main logic.
switch ($menu_type) {
case 'signup':
// See if this user can signup, if the node is configured to display the
// signup form on a separate tab, and if the node has signup output.
return (user_access('sign up for content') && variable_get('signup_form_location', 'node') == 'tab') && _signup_needs_output($node);
case 'list':
return user_access('view all signups') || _signup_menu_access($node, 'admin');
case 'list-tab':
// See if this user can view signups, and if the site is configured to
// display the signup user list as a tab.
$user_list = variable_get('signup_display_signup_user_list', 'signup');
if ($user_list == 'signup-tab' || $user_list == 'embed-view-tab') {
$user_list_tab = TRUE;
}
else {
$user_list_tab = FALSE;
}
$list_access = _signup_menu_access($node, 'list');
return $list_access && $user_list_tab && _signup_needs_output($node);
case 'admin':
$admin_all = user_access('administer all signups');
$admin_own = user_access('administer signups for own content') && ($user->uid == $node->uid);
return $admin_all || $admin_own;
case 'add':
return ($node->signup_status && _signup_menu_access($node, 'admin'));
case 'broadcast':
$email_all = user_access('email all signed up users');
$email_own = user_access('email users signed up for own content') && ($user->uid == $node->uid);
return $email_all || $email_own;
case 'any':
$signup = _signup_menu_access($node, 'signup');
$list = _signup_menu_access($node, 'list-tab');
$admin = _signup_menu_access($node, 'admin');
$email = _signup_menu_access($node, 'broadcast');
return $signup || $list || $admin || $email;
}
}
function _signup_user_menu_access($account) {
global $user;
return user_access('administer all signups') || $account->uid == $user->uid;
}
/**
* Menu loader callback to load a project node.
*/
function signup_menu_load($sid) {
if (!is_numeric($sid)) {
return FALSE;
}
$signup = signup_load_signup($sid);
if (empty($signup)) {
return FALSE;
}
return $signup;
}
/**
* Determine menu access callback for a specific signup.
*
* @param $signup
* The fully-loaded signup object that would be affected.
* @param $op
* The operation the menu item would perform. Can be 'edit' or 'cancel'.
*
* @return
* TRUE if the operation should be permitted, otherwise FALSE.
*/
function _signup_menu_signup_access($signup, $op) {
global $user;
$node = node_load($signup->nid);
// Ensure the user still has access to view the node they signed up for.
if (!node_access('view', $node)) {
return FALSE;
}
// See if the user is allowed to perform the operation on their own signup.
$permission = "$op own signups";
if (user_access($permission) && ($user->uid == $signup->uid)) {
return TRUE;
}
// Check admin powers for this signup.
if (_signup_menu_access($node, 'admin')) {
return TRUE;
}
return FALSE;
}
/**
* Menu callback to handle the default tab at node/N/signups
*
* This tests the user's permission to see what tab they should really see.
* If they have permission to signup for nodes and the site is configured to
* put the signup form on a separate tab from the node itself, display the
* current user's signup info (either a form to signup, or their current
* signup). If not, see if they can view a signup user list, and redirect
* there. If not, see if they can administer signups and redirect there.
* Finally, if they can at least send a signup broadcast, go there.
*
* @param $node
* Fully loaded node object to generate the node/N/signup menu handler for.
*
* @return
* Either the output of the default task ("Sign up") or redirect to a tab.
*
* @see signup_menu()
* @see _signup_menu_access()
* @see _signup_current_user_signup();
*/
function signup_node_tab_page($node) {
$signup = _signup_menu_access($node, 'signup');
$list = _signup_menu_access($node, 'list-tab');
$admin = _signup_menu_access($node, 'admin');
$broadcast = _signup_menu_access($node, 'broadcast');
if ($signup) {
module_load_include('inc', 'signup', 'includes/node_output');
return _signup_current_user_signup($node, 'tab');
}
elseif ($list) {
drupal_goto("node/$node->nid/signups/list");
}
elseif ($admin) {
drupal_goto("node/$node->nid/signups/admin");
}
elseif ($broadcast) {
drupal_goto("node/$node->nid/signups/broadcast");
}
}
/**
* Initialize the necessary scheduler backend(s).
*/
function _signup_initialize_scheduler_backend() {
module_load_include('inc', 'signup', '/includes/scheduler');
_signup_load_scheduler_includes();
}
/**
* Implementation of hook_perm().
*
* @ingroup signup_core
*/
function signup_perm() {
return array(
'sign up for content',
'cancel signups',
'cancel own signups',
'edit own signups',
'view all signups',
'administer all signups',
'administer signups for own content',
'email users signed up for own content',
'email all signed up users',
);
}
/**
* Implementation of hook_user().
*
* When a user is deleted, cancel all of that user's signups to remove all
* instances of that user from the {signup_log} table, free up space in nodes
* with signup limits, etc.
*
* @ingroup signup_core
*/
function signup_user($type, &$edit, &$user, $category = NULL) {
switch ($type) {
case 'delete':
$uids = array();
if (is_array($edit['accounts'])) {
// A multi-user delete from Admin > User management > Users.
$uids = $edit['accounts'];
}
else {
// A single-user delete from the edit tab on the user's profile.
$uids[] = $edit['_account']->uid;
}
foreach ($uids as $uid) {
$query = db_query("SELECT * FROM {signup_log} WHERE uid = %d", $uid);
while ($signup = db_fetch_object($query)) {
signup_cancel_signup($signup);
}
}
break;
}
// If we're not using views, we need to support additional user
// operations, for example, to add the user's current signup
// schedule to their user profile page.
if (!module_exists('views')) {
module_load_include('inc', 'signup', 'includes/no_views');
return _signup_user_no_views($type, $edit, $user, $category);
}
}
/**
* Implementation of hook_form_alter().
*
* @ingroup signup_core
*/
function signup_form_alter(&$form, &$form_state, $form_id) {
if (!empty($form['type']['#value'])) {
if ($form_id == $form['type']['#value'] .'_node_form') {
module_load_include('inc', 'signup', 'includes/node_form');
signup_alter_node_form($form, $form_state, $form_id);
}
}
}
/**
* Alters the form for administrator settings per node type.
* (admin/content/types)
*/
function signup_form_node_type_form_alter(&$form, &$form_state) {
$type = $form['old_type']['#value'];
$form['signup'] = array(
'#type' => 'fieldset',
'#title' => t('Signup settings'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['signup']['signup_node_default_state'] = array(
'#type' => 'radios',
'#title' => t('Signup options'),
'#options' => array(
'disabled' => t('Disabled'),
'allowed_off' => t('Allowed (off by default)'),
'enabled_on' => t('Enabled (on by default)'),
),
'#default_value' => variable_get('signup_node_default_state_'. $type, 'disabled'),
'#description' => t('If %disabled is selected, signups will not be possible for this content type. If %allowed_off is selected, signups will be off by default, but users with the %admin_all_signups permission will be able to allow signups for specific posts of this content type. If %enabled_on is selected, users will be allowed to signup for this content type unless an administrator disbles signups on specific posts.', array('%disabled' => t('Disabled'), '%allowed_off' => t('Allowed (off by default)'), '%enabled_on' => t('Enabled (on by default)'), '%admin_all_signups' => t('administer all signups'))),
);
if (!empty($type) && function_exists('_signup_date_alter_node_type_form')) {
_signup_date_alter_node_type_form($form, $form_state);
}
}
/**
* @defgroup signup_nodeapi Functions for nodeapi integration
*/
/**
* Implementation of hook_nodeapi().
*
* @ingroup signup_nodeapi
*/
function signup_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
switch ($op) {
case 'insert':
case 'update':
module_load_include('inc', 'signup', 'includes/node_form');
signup_save_node($node, $op);
break;
case 'delete':
// Clean up the signup tables for the deleted node.
db_query("DELETE FROM {signup} WHERE nid = %d", $node->nid);
db_query("DELETE FROM {signup_panes} WHERE nid = %d", $node->nid);
db_query("DELETE FROM {signup_log} WHERE nid = %d", $node->nid);
break;
case 'load':
// Check for a signup for this node.
// If it's a new node, load the defaults.
$result = db_query("SELECT * FROM {signup} WHERE nid = %d", ($node->nid ? $node->nid : 0));
$signup = db_fetch_object($result);
// Load signup data for both new nodes w/ enabled node types,
// and any existing nodes that are already signup enabled.
if ((!$node->nid && variable_get('signup_node_default_state_'. $node->type, 'disabled') == 'enabled_on') || ($node->nid && !empty($signup))) {
$node->signup = 1;
$node->signup_forwarding_email = $signup->forwarding_email;
$node->signup_send_confirmation = $signup->send_confirmation;
$node->signup_confirmation_email = $signup->confirmation_email;
$node->signup_send_reminder = $signup->send_reminder ;
$node->signup_reminder_days_before = $signup->reminder_days_before;
$node->signup_reminder_email = $signup->reminder_email;
$node->signup_close_signup_limit = $signup->close_signup_limit;
$node->signup_status = $signup->status;
if ($node->nid) {
$node->signup_total = db_result(db_query("SELECT COUNT(*) FROM {signup_log} WHERE nid = %d", $node->nid));
$node->signup_effective_total = db_result(db_query("SELECT SUM(count_towards_limit) FROM {signup_log} WHERE nid = %d", $node->nid));
}
// Load pane data
$result = db_query("SELECT * FROM {signup_panes} WHERE nid = %d ORDER BY weight", $node->nid);
$node->signup_form_panes = array();
while ($pane_data = db_fetch_array($result)) {
$node->signup_form_panes[$pane_data['pane_id']] = array(
'weight' => $pane_data['weight'],
'callback' => $pane_data['callback'],
);
}
}
else {
$node->signup = 0;
}
break;
case 'view':
// If this is a signup node, figure out what (if anything) to print.
// Only include any of this if we're trying to view the node as
// a page, not during the view from comment validation, etc.
if ($page && _signup_needs_output($node)) {
$info_location = variable_get('signup_form_location', 'node');
$list_location = variable_get('signup_display_signup_user_list', 'signup');
if ($info_location == 'node' || $list_location == 'signup' || $list_location == 'embed-view') {
module_load_include('inc', 'signup', 'includes/node_output');
}
if ($info_location == 'node') {
$signup_info = _signup_node_output($node);
if (!empty($signup_info)) {
if (module_exists('content')) {
// Due to a bug in CCK (http://drupal.org/node/363456), we need
// to call this function twice to ensure we get the real value.
// The bug is present when you first enable the setting to
// display in the node instead of a separate tab, or when you
// first upgrade to the version that contains this code.
content_extra_field_weight($node->type, 'signup_node_info');
$weight = content_extra_field_weight($node->type, 'signup_node_info');
}
else {
$weight = variable_get('signup_info_node_weight_'. $node->type, 10);
}
$node->content['signup'] = array(
'#value' => $signup_info,
'#weight' => $weight,
);
}
}
if ($list_location == 'signup' || $list_location == 'embed-view') {
$signup_list = signup_user_list_output($node);
if (!empty($signup_list)) {
if (module_exists('content')) {
// Call this twice to work-around a bug in CCK (#363456).
content_extra_field_weight($node->type, 'signup_node_list');
$weight = content_extra_field_weight($node->type, 'signup_node_list');
}
else {
$weight = variable_get('signup_list_node_weight_'. $node->type, 11);
}
$node->content['signup_list'] = array(
'#value' => $signup_list,
'#weight' => $weight,
);
}
}
}
break;
}
}
/**
* Helper function that determines if a given node should have any
* signup-related output.
*
* @param $node A fully loaded node object.
*
* @return TRUE if this node should have signup output, FALSE if not.
*
* @see signup_nodeapi()
*/
function _signup_needs_output($node) {
if (!$node->signup) {
// Not signup enabled at all.
return FALSE;
}
$suppress = module_invoke_all('signup_suppress', $node);
if (in_array(TRUE, $suppress)) {
// Someone doesn't want signup details printed.
return FALSE;
}
return TRUE;
}
/**
* Implementation of hook_action_info().
*/
function signup_action_info() {
return array(
'signup_cancel_action' => array(
'type' => 'signup',
'description' => t('Cancel signup'),
'configurable' => FALSE,
),
'signup_mark_attended_action' => array(
'type' => 'signup',
'description' => t('Mark signup attended'),
'configurable' => FALSE,
),
'signup_mark_not_attended_action' => array(
'type' => 'signup',
'description' => t('Mark signup did not attend'),
'configurable' => FALSE,
),
);
}
/**
* Action callback to cancel a given signup.
*
* @param $signup
* Reference to a fully-loaded signup object to cancel.
*
* @see signup_load_signup()
* @see signup_cancel_signup()
*/
function signup_cancel_action($signup) {
signup_cancel_signup($signup);
watchdog('action', 'Canceled signup @signup_id.', array('@signup_id' => $signup->sid));
}
/**
* Action callback to mark a given signup that the user attended the node.
*
* @param $signup
* Reference to a fully-loaded signup object to record attendance on.
*
* @return
* Nothing: $signup object is modified by reference and the {signup_log}
* table is directly UPDATE'ed in the database.
*/
function signup_mark_attended_action(&$signup) {
db_query("UPDATE {signup_log} SET attended = %d WHERE sid = %d", TRUE, $signup->sid);
$signup->attended = TRUE;
watchdog('action', 'Marked signup @signup_id attended.', array('@signup_id' => $signup->sid));
}
/**
* Action callback to mark a given signup that the user didn't attend the node.
*
* @param $signup
* Reference to a fully-loaded signup object to record attendance on.
*
* @return
* Nothing: $signup object is modified by reference and the {signup_log}
* table is directly UPDATE'ed in the database.
*/
function signup_mark_not_attended_action(&$signup) {
db_query("UPDATE {signup_log} SET attended = %d WHERE sid = %d", FALSE, $signup->sid);
$signup->attended = FALSE;
watchdog('action', 'Marked signup @signup_id did not attend.', array('@signup_id' => $signup->sid));
}
/**
* Implementation of hook_views_bulk_operations_object_info().
*
* Exports information to VBO about what kinds of objects to do operations on.
*/
function signup_views_bulk_operations_object_info() {
return array(
'signup' => array(
'type' => 'signup',
'base_table' => 'signup_log',
'load' => 'signup_load_signup',
'title' => 'label',
),
);
}
/**
* Deprecated implmentation of hook_object_info().
*
* This is the old name for hook_views_bulk_operations_object_info(). It was
* renamed in VBO at http://drupal.org/node/362534 to prevent possible
* namespace colisions. However, it means that modules supporting VBO need to
* have both implementations during the transition while users upgrade to VBO
* version 6.x-1.4. Once 6.x-1.4 is ubiquitous, this can be removed.
*/
function signup_object_info() {
return signup_views_bulk_operations_object_info();
}
/**
* Load a $signup object from the given Signup ID (sid).
*
* In addition to pulling all the fields from the {signup_log} table, this
* method also adds a "label" member to the object which is used by Views bulk
* operations (VBO) in various parts of its UI.
*
* @param $sid
* Signup ID to load.
*
* @return
* Fully loaded $signup object corresponding to the given ID.
*/
function signup_load_signup($sid) {
$signup = db_fetch_object(db_query("SELECT sl.*, n.title, u.name, u.mail FROM {signup_log} sl INNER JOIN {node} n ON sl.nid = n.nid INNER JOIN {users} u ON sl.uid = u.uid WHERE sl.sid = %d", $sid));
if (!empty($signup)) {
// This label is escaped by VBO, so it needs to be plain text, not HTML.
$signup->label = t("!user signup for '!title'", array('!user' => $signup->name, '!title' => $signup->title));
}
return $signup;
}
/**
* Save a $signup object to the database.
*
* @param $signup
* Fully-loaded signup object to save.
*
* @return
* The return value from drupal_write_record().
*
* @see signup_load_signup()
* @see drupal_write_record()
*/
function signup_save_signup(&$signup) {
$rval = FALSE;
if (is_array($signup->form_data)) {
$form_data_array = $signup->form_data;
$signup->form_data = serialize($form_data_array);
}
if (empty($signup->sid)) {