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

Contents of /contributions/modules/competition/competition.module

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


Revision 1.34 - (show annotations) (download) (as text)
Thu Jul 10 01:30:02 2008 UTC (16 months, 2 weeks ago) by colan
Branch: MAIN
CVS Tags: HEAD
Changes since 1.33: +16 -2 lines
File MIME type: text/x-php
Implemented hook_relativity_detach() to remove the associated competition.
1 <?php
2 // $Id: competition.module,v 1.33 2008/07/09 22:15:40 colan Exp $
3
4 /**
5 * @file
6 * Allows you to run competitions on your site.
7 *
8 * This module supports the creation of competitions. It provides tools
9 * necessary for managing a continuous cycle of competitions that run on
10 * your site.
11 *
12 * Copyright (C) 2008 Openject Consulting (http://www.openject.com/)
13 * Written by Colan Schwartz (http://drupal.org/user/58704)
14 */
15
16 /****************************************************************************
17 ** CONSTANT DEFINITIONS ****************************************************
18 ****************************************************************************/
19 define('COMPETITION_PHASE_NOT_SET', 0);
20 define('COMPETITION_PHASE_SUBMISSION', 1);
21 define('COMPETITION_PHASE_VOTING', 2);
22 define('COMPETITION_PHASE_POST_VOTING', 3);
23
24 define('COMPETITION_VOTING_DAYS_DEFAULT', 14);
25
26 /****************************************************************************
27 ** DRUPAL HOOKS ************************************************************
28 ****************************************************************************/
29
30 /**
31 * Implementation of hook_help().
32 */
33 function competition_help($section) {
34 switch ($section) {
35
36 case 'admin/help#competition':
37 // Return the brief help text.
38 return t('<p>Allows you to run competitions on your site.</p>');
39
40 case 'admin/settings/competition':
41 // Return a longer help text.
42 return t('<p>This module supports the creation of competitions. It provides tools necessary for managing a continuous cycle of competitions that run on your site.</p>');
43 }
44 }
45
46 /**
47 * Implementation of hook_perm().
48 */
49 function competition_perm() {
50 return array('administer competitions');
51 }
52
53 /**
54 * Implementation of hook_menu().
55 */
56 function competition_menu($may_cache) {
57
58 // Initialize the list of menu items.
59 $items = array();
60
61 if ($may_cache) {
62 // Define cacheable menu items.
63
64 // Add an item for the admin settings.
65 $items[] = array(
66 'path' => 'admin/settings/competition',
67 'title' => t('Competition settings'),
68 'description' => t('Make changes to your competition configuration.'),
69 'callback' => 'drupal_get_form',
70 'callback arguments' => array('competition_admin_settings'),
71 'access' => user_access('administer competitions'),
72 );
73
74 // Add an item for phase shifting.
75 $items[] = array(
76 'path' => 'admin/settings/competition/phase_shift',
77 'title' => t('Shift to next competition phase'),
78 'description' => t('Move the site to the next phase in the competition cycle.'),
79 'type' => MENU_CALLBACK,
80 'callback' => 'drupal_get_form',
81 'callback arguments' => array('competition_phase_shift'),
82 'access' => user_access('administer competitions'),
83 );
84 }
85
86 // Return the defined items.
87 return $items;
88 }
89
90 /**
91 * Implementation of hook_user
92 */
93 function competition_user($op, &$edit, &$account, $category = NULL) {
94 switch ($op) {
95
96 case 'view':
97
98 // Display the competitions that the user has entered.
99 $category = t('Competitions Entered');
100 $value = theme('competition_views_display_entered', $account);
101 if (sizeof($value)) {
102 return array($category => $value);
103 }
104
105 break;
106 }
107 }
108
109 /**
110 * Implementation of hook_nodeapi().
111 *
112 * @todo
113 * Fix the viewing of each competition's voting period. The 'view' code
114 * below doesn't actually work.
115 */
116 function competition_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
117 global $user;
118
119 switch ($op) {
120 case 'prepare':
121
122 // If this node has been entered into a competition, and the competition
123 // has closed, we don't want the owner to edit it. Otherwise, he/she
124 // would be able to make changes to his/her submission during the voting
125 // phase.
126 if ($node->nid) {
127 $is_entry = ($node->type == 'competition_entry');
128 $is_entry_content = db_result(db_query("
129 SELECT n.type FROM {node} n INNER JOIN {relativity} r
130 ON r.parent_nid = n.nid WHERE r.nid = %d
131 ", $node->nid)) == 'competition_entry';
132 if ($is_entry || $is_entry_content) {
133
134 // We don't need to block edits if any of the following are true:
135 // (1) The node is just being created now (obviously not entered yet)
136 // (2) The user is allowed to administer competitions.
137 if (($node->nid) && (!user_access('administer competitions'))) {
138
139 // Get the competition that this node is associated with.
140 if ($is_entry) {
141 $competition = $node->field_competition_entry_comp[0]['nid'];
142 }
143 else /* $is_entry_content */ {
144 $competition = db_result(db_query("
145 SELECT field_competition_entry_comp_nid
146 FROM {content_type_competition_entry}
147 WHERE nid = (SELECT parent_nid FROM {relativity} WHERE nid = %d)
148 ", $node->nid));
149 }
150
151 // If the associated competition is closed, we can't allow the user
152 // to edit this node. Display a message and redirect to the node
153 // view.
154 if ($competition && competition_is_closed($competition)) {
155 drupal_set_message(t('Your entry has been entered into a competition that has closed so it cannot be edited at this time.'), 'error');
156 drupal_goto('node/' . $node->nid);
157 }
158 }
159 }
160 }
161
162 break;
163 case 'view':
164
165 // If a competition's voting period hasn't been set, show the default.
166 if ($node->type == 'competition') {
167 if (!$node->field_competition_voting_days[0]['value']) {
168 $node->field_competition_voting_days[0]['value'] =
169 competition_voting_get_days();
170 }
171 }
172
173 break;
174 case 'insert':
175
176 // If this is a competition entry, we need to set the parent to the
177 // selected competition. Also, we need to unset the GET & POST
178 // variables in order to override relativity's behaviour of setting
179 // the parent from the URL.
180 if ($node->nid && ($node->type == 'competition_entry')) {
181 $node->parent_node = $node->field_competition_entry_comp[0]['nid'];
182 $_GET['parent_node'] = NULL;
183 $_POST['parent_node'] = NULL;
184 }
185
186 break;
187 case 'update':
188
189 // If this is a competition entry, update the parent.
190 if ($node->nid && ($node->type == 'competition_entry')) {
191 if ($node->parent_node) {
192 db_query('UPDATE {relativity} SET parent_nid = %d WHERE nid = %d',
193 $node->parent_node, $node->nid);
194
195 // If there's no record for this yet, create one.
196 if (!db_affected_rows()) {
197 db_query(
198 'INSERT INTO {relativity} (nid, parent_nid) VALUES (%d, %d)',
199 $node->nid, $node->parent_node
200 );
201 }
202 }
203 else /* !$node->parent_node */ {
204 // Rather than setting a zero, we should just remove the record.
205 db_query('DELETE FROM {relativity} WHERE nid = %d', $node->nid);
206 }
207 }
208
209 break;
210 case 'submit':
211
212 // If this is a competition entry, set the parent to the competition.
213 if ($node->type == 'competition_entry') {
214 $node->parent_node = $node->field_competition_entry_comp[0]['nid'];
215 }
216
217 break;
218 }
219 }
220
221 /**
222 * Implementation of hook_form_alter().
223 */
224 function competition_form_alter($form_id, &$form) {
225
226 // Alter the competition entry node form.
227 if ($form_id == 'competition_entry_node_form') {
228
229 // Don't show the log message textfield.
230 unset($form['log']);
231 }
232 }
233
234 /**
235 * Implementation of hook_link().
236 */
237 function competition_link($type, $node = 0, $main = 0) {
238 $links = array();
239
240 // Add links to competition nodes.
241 if ($type == 'node' && $node->type == 'competition') {
242
243 // Fetch all of the content types allowed for this competition.
244 $types = competition_get_content_types($node);
245
246 // Figure out if we should show the voting links.
247 $competition_round = $node->field_competition_round[0]['value'];
248 $show_voting_links = user_access('vote with versus') &&
249 ($competition_round == competition_round_get_current()) &&
250 (competition_phase_get_current() == COMPETITION_PHASE_VOTING);
251
252 // Figure out if we should show the standings links.
253 $show_standings_links = user_access('administer competitions') ||
254 (!variable_get('competition_standings_private', 0));
255
256 // Add any links for each allowed content type.
257 foreach ($types as $type) {
258 $name = node_get_types('name', $type);
259
260 // Show voting links if applicable.
261 if ($show_voting_links) {
262 $links['competition_voting_' . $type] = array(
263 'title' => t('Vote on @type', array('@type' => $name)),
264 'href' => "versus/vote/$type/" . $node->nid,
265 );
266 }
267
268 // Show standings links if applicable.
269 if ($show_standings_links) {
270 $links['competition_standings_' . $type] = array(
271 'title' => t('@name Standings', array('@name' => $name)),
272 'href' => "versus/standings/$type/" . $node->nid,
273 );
274 }
275 }
276 }
277
278 // Return them all.
279 return $links;
280 }
281
282 /****************************************************************************
283 ** FORMS *******************************************************************
284 ****************************************************************************/
285
286 /**
287 * Define the admin settings form.
288 *
289 * @return array
290 * The form.
291 */
292 function competition_admin_settings() {
293 $form = array();
294
295 // Display/edit the current phase.
296 $form['competition_state'] = array(
297 '#type' => 'fieldset',
298 '#title' => t('Competition state'),
299 '#collapsible' => TRUE,
300 '#collapsed' => FALSE,
301 '#description' => t('
302 <table>
303 <tr>
304 <td>Current state:</td>
305 <td>Round %current_round, %current_phase phase</td>
306 </tr>
307 <tr>
308 <td>Next state:</td>
309 <td>Round %next_round, %next_phase phase</td>
310 </tr>
311 </table>
312 <p>You may update the state of the site by <a href="/admin/settings/competition/phase_shift">moving to the next phase</a>. This will only work if the transition you are attempting makes sense. For example, you cannot move to the next phase if any competitions are live or you are in a voting period.</p>
313 ', array(
314 '%current_round' => competition_round_get_current(),
315 '%current_phase' => competition_phase_get_name(),
316 '%next_round' => (competition_phase_get_next(
317 competition_phase_get_current() + 1
318 ) == 1) ?
319 competition_round_get_next() : competition_round_get_current(),
320 '%next_phase' => competition_phase_get_name(competition_phase_get_next()),
321 )),
322 );
323
324 // Display/edit the default number of voting days for each competition.
325 $form['competition_voting_days'] = array(
326 '#type' => 'textfield',
327 '#title' => t('Default number of days in voting period'),
328 '#description' => t('Set the default number of days users are allowed to vote on each round\'s content. This can be overridden by individual competitions on their node edit forms.'),
329 '#size' => 3,
330 '#maxlength' => 3,
331 '#default_value' => competition_voting_get_days(),
332 );
333
334 // Determine if the standings links should be publically available.
335 $form['competition_standings_private'] = array(
336 '#type' => 'checkbox',
337 '#title' => t('Keep standings links private'),
338 '#default_value' => variable_get('competition_standings_private', 0),
339 '#description' => t('If this box is checked, competition nodes will only show the standings links to Competition administrators.'));
340
341 // Define a validation function.
342 $form['#validate'] = array(
343 'competition_admin_settings_validate' => array()
344 );
345
346 return system_settings_form($form);
347 }
348
349 /**
350 * Validate the competition settings.
351 */
352 function competition_admin_settings_validate($form_id, $form_values) {
353
354 // Make sure that the default number of voting days submitted is valid.
355 $days = $form_values['competition_voting_days'];
356 if (!is_numeric($days) || ($days <= 0)) {
357 form_set_error(
358 'competition_voting_days',
359 t('Please enter a positive number.')
360 );
361 }
362
363 // Any other validation checks go here.
364 }
365
366 /**
367 * Callback function for phase shifting.
368 */
369 function competition_phase_shift() {
370 return confirm_form(
371 $form, t(
372 'Are you sure you want to shift the site\'s state from <em>%phase_current</em> to <em>%phase_next</em>?',
373 array(
374 '%phase_current' => competition_phase_get_name(),
375 '%phase_next' => competition_phase_get_name(
376 competition_phase_get_next()
377 )
378 )
379 ),
380 'admin/settings/competition', '', t('Shift'), t('Cancel')
381 );
382 }
383
384 /**
385 * Submit function for phase shifting.
386 *
387 * @param $form_id
388 * The form ID.
389 * @param $form_values
390 * The form values that were submitted.
391 */
392 function competition_phase_shift_submit($form_id, $form_values) {
393 competition_phase_set_next();
394 return 'admin/settings/competition';
395 }
396
397 /****************************************************************************
398 ** INTERNAL FUNCTIONS ******************************************************
399 ****************************************************************************/
400
401 /**
402 * Return the current competition round.
403 *
404 * @return integer
405 * The current round number.
406 */
407 function competition_round_get_current() {
408 return variable_get('competition_round', 1);
409 }
410
411 /**
412 * Return the next competition round.
413 *
414 * @return integer
415 * The next round number.
416 */
417 function competition_round_get_next() {
418 return (competition_round_get_current() + 1);
419 }
420
421 /**
422 * Return the current competition phase.
423 *
424 * @return integer
425 * The current phase number.
426 */
427 function competition_phase_get_current() {
428 return variable_get('competition_phase', 1);
429 }
430
431 /**
432 * Return the name of a competition phase.
433 *
434 * The name of the specified phase is returned. If no phase is specified,
435 * the name of the current phase is returned.
436 *
437 * @param integer
438 * The phase number who's name is being requested.
439 * @return string
440 * The name of the provided phase.
441 */
442 function competition_phase_get_name($phase = NULL) {
443
444 // Set the current phase if one hasn't been set.
445 $phase = $phase ? $phase : competition_phase_get_current();
446
447 // Return its name.
448 $phases = competition_phase_get_phases();
449 return $phases[$phase];
450 }
451
452 /**
453 * Return the phase following a given phase.
454 *
455 * The next phase number is returned. If no phase number is specified,
456 * the current one is assumed.
457 *
458 * @param integer
459 * The phase number.
460 * @return integer
461 * The phase number of the following phase.
462 */
463 function competition_phase_get_next($phase = NULL) {
464
465 // Set the current phase if one hasn't been set.
466 $phase = $phase ? $phase : competition_phase_get_current();
467
468 // If there is a next phase, return it.
469 $phases = competition_phase_get_phases();
470 if (isset($phases[$phase + 1])) {
471 return ($phase + 1);
472 }
473 else /* there is no next phase so start over */ {
474 return 1;
475 }
476 }
477
478 /**
479 * Return information on the available competition phases.
480 *
481 * @return array
482 * An ordered list of competition phases.
483 */
484 function competition_phase_get_phases() {
485 return array(
486 COMPETITION_PHASE_NOT_SET => '[PHASE NOT SET]',
487 COMPETITION_PHASE_SUBMISSION => 'submission',
488 COMPETITION_PHASE_VOTING => 'voting',
489 COMPETITION_PHASE_POST_VOTING => 'post-voting',
490 );
491 }
492
493 /**
494 * Move the site's state into the next phase.
495 */
496 function competition_phase_set_next() {
497 $state_changed = FALSE;
498
499 drupal_set_message(t(
500 'Attempting to shift the site from <em>@phase_name</em> to the next competition phase...',
501 array('@phase_name' => competition_phase_get_name())
502 ));
503
504 // We need to know where we are to know where we're going.
505 // For each of these, make sure that we can move on to the next state.
506 // If we can, do it.
507 switch (competition_phase_get_current()) {
508
509 case COMPETITION_PHASE_SUBMISSION:
510 if (competition_phase_pass_submission()) {
511 competition_phase_set_voting();
512 $state_changed = TRUE;
513 }
514 break;
515
516 case COMPETITION_PHASE_VOTING:
517 if (competition_phase_pass_voting()) {
518 competition_phase_set_post_voting();
519 $state_changed = TRUE;
520 }
521 break;
522
523 case COMPETITION_PHASE_POST_VOTING:
524 if (competition_phase_pass_post_voting()) {
525 competition_phase_set_submission();
526 $state_changed = TRUE;
527 }
528 break;
529
530 default:
531 drupal_set_message(t('The competition phase has not been set!'), 'error');
532 break;
533 }
534
535 // Timestamp the state change and report status.
536 if ($state_changed) {
537 competition_state_set_changed(time());
538 drupal_set_message(t('The site is now in the <em>@phase_name</em> phase.',
539 array('@phase_name' => competition_phase_get_name())
540 ));
541 }
542 }
543
544 /**
545 * Determine if the submission phase can be passed.
546 *
547 * @return boolean
548 * TRUE if we can move on to the next phase, FALSE otherwise.
549 */
550 function competition_phase_pass_submission() {
551
552 // Check for open competitions. If there are any, we can't move forward yet.
553 // All competitions associated with the current round need to be wrapped up.
554 if (db_result(db_query("
555 SELECT COUNT(*) FROM {content_type_competition}
556 WHERE (field_competition_round_value = %d)
557 AND (field_competition_period_value2 > UNIX_TIMESTAMP())
558 ", competition_round_get_current()))) {
559
560 // Report the problem.
561 drupal_set_message(t('Could not leave the <em>@phase_name</em> phase yet. There are still active competitions in the current round.',
562 array('@phase_name' => competition_phase_get_name())
563 ), 'error');
564 return FALSE;
565 }
566
567 // No problems.
568 return TRUE;
569 }
570
571 /**
572 * Determine if the voting phase can be passed.
573 *
574 * @return boolean
575 * TRUE if we can move on to the next phase, FALSE otherwise.
576 */
577 function competition_phase_pass_voting() {
578
579 // Check for any competitions with voting periods that extend past the
580 // current time. If there are any, we can't move forward yet. All of
581 // the voting periods must end first.
582 if (db_result(db_query("
583 SELECT COUNT(*) FROM {content_type_competition}
584 WHERE (field_competition_round_value = %d)
585 AND ( ( (
586 IF(
587 field_competition_voting_days_value,
588 field_competition_voting_days_value,
589 %d
590 ) * 86400
591 ) + %d) >= UNIX_TIMESTAMP())
592 ", competition_round_get_current(), competition_voting_get_days(),
593 competition_state_get_changed()))) {
594
595 // Report the problem.
596 drupal_set_message(t('Could not leave the <em>@phase_name</em> phase yet. There are still competitions in the voting period.',
597 array('@phase_name' => competition_phase_get_name())
598 ), 'error');
599 return FALSE;
600 }
601
602 // No problems.
603 return TRUE;
604 }
605
606 /**
607 * Determine if the post-voting phase can be passed.
608 *
609 * @return boolean
610 * TRUE if we can move on to the next phase, FALSE otherwise.
611 */
612 function competition_phase_pass_post_voting() {
613 $current_round = competition_round_get_current();
614
615 // Make sure that there is at least one winner declared for each
616 // competition in the current round,
617 if (db_result(db_query("
618 SELECT (
619 SELECT COUNT(*) FROM {content_field_competition_winners} w
620 INNER JOIN {content_type_competition} c ON w.nid = c.nid
621 WHERE (w.delta = 0)
622 AND w.field_competition_winners_nid
623 AND (c.field_competition_round_value = %d)
624 ) <> (
625 SELECT COUNT(*) FROM {content_type_competition}
626 WHERE field_competition_round_value = %d
627 )
628 ", $current_round, $current_round))) {
629
630 // Report the problem.
631 drupal_set_message(t('Could not leave the <em>@phase_name</em> phase yet. There are still competitions that have no winners set.',
632 array('@phase_name' => competition_phase_get_name())
633 ), 'error');
634 return FALSE;
635 }
636
637 // No problems.
638 return TRUE;
639 }
640
641 /**
642 * Move the site into the voting phase.
643 *
644 * Pre: The site is in the submission phase, and is ready to move to voting.
645 * Post: The site is now in the voting phase.
646 */
647 function competition_phase_set_voting() {
648
649 // Enable voting.
650 versus_set_voting_status(TRUE);
651
652 // Set the new phase appropriately.
653 variable_set('competition_phase', COMPETITION_PHASE_VOTING);
654 }
655
656 /**
657 * Move the site into the post-voting phase.
658 *
659 * Pre: The site is in the voting phase, and is ready to move to post-voting.
660 * Post: The site is now in the post-voting phase.
661 */
662 function competition_phase_set_post_voting() {
663
664 // Disable voting.
665 versus_set_voting_status(FALSE);
666
667 // Set the new phase appropriately.
668 variable_set('competition_phase', COMPETITION_PHASE_POST_VOTING);
669 }
670
671 /**
672 * Move the site into the submission phase.
673 *
674 * Pre: The site is in the post-voting stage, and is ready to move to8
675 * submission.
676 * Post: The site is now in the submission phase.
677 */
678 function competition_phase_set_submission() {
679
680 /*
681 * If you'd like to delete any content at the end of the round, do it here.
682 * Just make sure that it is actually associated with the current round
683 * before deleting it. ;) You will probably want to keep winning content,
684 * as competitions with winners will link to them.
685 */
686
687
688 /*
689 * If you'd like to delete any votes at the end of the round, do it here.
690 * Otherwise, the Voting API votes table will continue to grow.
691 */
692
693
694 // Move to the next round.
695 variable_set('competition_round', competition_round_get_next());
696
697 // Set the new phase appropriately.
698 variable_set('competition_phase', COMPETITION_PHASE_SUBMISSION);
699 }
700
701 /**
702 * Get the length of the voting period.
703 *
704 * Returns the length of the voting period, in days, for a particular
705 * competition. If no competition is specified, the site-wide default is
706 * returned.
707 *
708 * @param $nid
709 * The node ID of the competition we're interested in.
710 * @return
711 * The number of days in the voting period.
712 */
713 function competition_voting_get_days($nid = NULL) {
714
715 // If it's defined for this nid, return it.
716 if ($nid && ($days = db_result(db_query("
717 SELECT field_competition_voting_days_value FROM {content_type_competition}
718 WHERE (nid = %d)
719 ", $nid)))) {
720 return $days;
721 }
722
723 // Otherwise, return the default.
724 return variable_get(
725 'competition_voting_days', COMPETITION_VOTING_DAYS_DEFAULT
726 );
727 }
728
729 /**
730 * Set the timestamp for a state change.
731 *
732 * @param $time
733 * The time to which the timestamp should be set.
734 */
735 function competition_state_set_changed($time) {
736 variable_set('competition_state_changed', $time);
737 }
738
739 /**
740 * Get the timestamp for the last state change.
741 *
742 * @return
743 * The timestamp of the last state change, a number of seconds since the
744 * UNIX epoch.
745 */
746 function competition_state_get_changed() {
747 return variable_get('competition_state_changed', 0);
748 }
749
750 /**
751 * Get the list of content types valid for entry into a competition.
752 *
753 * @param $competition object
754 * The competition node in question.
755 * @return
756 * The array of content types.
757 */
758 function competition_get_content_types($competition) {
759
760 // Initialize a list of items to return.
761 $types = array();
762
763 // Get the types allowed for this competition & add them to the list.
764 $type_array = $competition->field_competition_content_types;
765 if ($type_array) {
766 foreach ($type_array as $value) {
767 $types[] = $value['value'];
768 }
769 }
770
771 // Return it.
772 return $types;
773 }
774
775 /**
776 * Determines if a competition has closed.
777 *
778 * @param $competition
779 * The node ID of the competition.
780 * @return
781 * TRUE if the competition has closed, FALSE otherwise.
782 */
783 function competition_is_closed($competition) {
784
785 // Get the competition's closing date.
786 $closing_date = date_make_date(db_result(db_query("
787 SELECT field_competition_period_value2 FROM {content_type_competition}
788 WHERE nid = %d
789 ", $competition)), NULL, DATE_ISO);
790
791 // Determine if it was in the past.
792 return (time() > date_format($closing_date, 'U'));
793 }
794
795 /**
796 * Helper function that caches node types.
797 *
798 * This code was originally taken from _relatedlinks_node_get_types(),
799 * in the Related Links module (http://drupal.org/project/relatedlinks).
800 *
801 * @param $keys
802 * A boolean variable that decides whether an associative array of node
803 * types is to be returned (or not).
804 *
805 * @return
806 * An associative or non-associative array of node types.
807 */
808 function competition_node_get_types($keys = FALSE) {
809 static $node_types = NULL, $key_array = NULL;
810
811 // Check if there's anything in the cache.
812 if (!isset($node_types)) {
813
814 // There's nothing in the cache so we need to get content type information.
815 $node_types = node_get_types('names');
816 foreach ($node_types as $type => $name) {
817 $node_types[$type] = check_plain($name);
818 }
819
820 // Set the list of keys.
821 $key_array = array_keys($node_types);
822 }
823
824 // Either return the key list, or key/value pairs.
825 return $keys ? $key_array : $node_types;
826 }
827
828 /**
829 * Get the subquery resulting in a node's competition ID from within Views.
830 *
831 * @return string
832 * The subquery resulting in the competition ID.
833 */
834 function competition_views_get_associated_id() {
835
836 // Get the node ID and the node's parent ID.
837 $nid = "node.nid";
838 $parent_nid = "(SELECT parent_nid FROM {relativity} WHERE nid = $nid)";
839
840 // Set the subquery for fetching the competition ID.
841 $subquery = "(SELECT field_competition_entry_comp_nid FROM {content_type_competition_entry} WHERE nid = ";
842
843 // Set the queries for both the node and its parent.
844 $query_nid = $subquery . $nid . ")";
845 $query_parent = $subquery . $parent_nid . ")";
846
847 // If the node has a competition ID, return it. Otherwise, the parent's.
848 return "(SELECT IF($query_nid, $query_nid, $query_parent))";
849 }
850
851 /****************************************************************************
852 ** VIEWS HOOKS *************************************************************
853 ****************************************************************************/
854
855 /**
856 * Implementation of hook_views_tables().
857 */
858 function competition_views_tables() {
859 $tables['competition'] = array(
860 'name' => 'competition',
861 'fields' => array(
862 'id' => array(
863 'name' => t('Competition: ID'),
864 'help' => t('The associated competition for this node.'),
865 'sortable' => TRUE,
866 'notafield' => TRUE,
867 'query_handler' => 'competition_query_handler_competition_id',
868 ),
869 ),
870 'filters' => array(
871 'winner' => array(
872 'name' => t('Competition: Winner'),
873 'help' => t('Filters entry nodes based on whether they were winners in a competition.'),
874 'operator' => 'views_handler_operator_eqneq',
875 'handler' => 'views_handler_filter_competition_winner',
876 'value' => array(
877 '#type' => 'select',
878 '#options' => array(0 => t('False'), 1 => t('True'),),
879 ),
880 ),
881 'round' => array(
882 'name' => t('Competition: Round'),
883 'help' => t('Filters entry nodes based on their round. Enter a round number in the value field, or "current round" to specify the current round.'),
884 'operator' => 'views_handler_operator_gtlt',
885 'handler' => 'views_handler_filter_competition_round',
886 'value' => array(
887 '#type' => 'textfield',
888 '#default_value' => "current round",
889 '#size' => "15",
890 '#maxlength' => "15",
891 ),
892 ),
893 ),
894 );
895 return $tables;
896 }
897
898 /**
899 * Views filter handler to filter nodes based on their round.
900 */
901 function views_handler_filter_competition_round($op, $filterdata, $filterinfo,
902 &$query) {
903 switch ($op) {
904 case 'handler':
905
906 // Get the operator.
907 $operator = $filterdata['operator'];
908
909 // Set the value that we're comparing to. If it's the curernt round,
910 // fetch it. Otherwise, use the value provided.
911 if ($filterdata['value'] == "current round") {
912 $value = competition_round_get_current();
913 }
914 else /* filter based on a specific round, not the current one */ {
915 $value = intval($filterdata['value']);
916 }
917
918 // Check if this is one of the nodes in the list of those that
919 // match the conditions specified.
920 $query->add_where("node.nid IN (
921 SELECT ce.nid FROM {content_type_competition_entry} ce
922 INNER JOIN {content_type_competition} c
923 ON ce.field_competition_entry_comp_nid = c.nid
924 WHERE c.field_competition_round_value $operator $value
925 )");
926
927 break;
928 }
929 }
930
931 /**
932 * Views filter handler to determine if a node was a winner.
933 */
934 function views_handler_filter_competition_winner($op, $filterdata, $filterinfo,
935 &$query) {
936
937 // Declare a subquery that will return a list of winners.
938 $winners_list = "(
939 SELECT field_competition_winners_nid
940 FROM {content_field_competition_winners}
941 )";
942
943 switch ($op) {
944 case 'handler':
945
946 // Set the value and operator.
947 $value = $filterdata['value'];
948 $equals = ($filterdata['operator'] == '=');
949
950 switch (TRUE) {
951
952 case (($equals) && (!$value)):
953 case ((!$equals) && ($value)):
954 // The node should not be in the winners list.
955 $query->add_where("node.nid NOT IN $winners_list");
956 break;
957
958 case ((!$equals) && (!$value)):
959 case (($equals) && ($value)):
960 // The node should be in the winners list.
961 $query->add_where("node.nid IN $winners_list");
962 break;
963 }
964 break;
965 }
966 }
967
968 /**
969 * Views query handler to define the associated competition for a node.
970 */
971 function competition_query_handler_competition_id($field, $fieldinfo, &$query) {
972 $query->add_field(
973 competition_views_get_associated_id(), '', 'competition_id'
974 );
975 }
976
977 /**
978 * Implementation of hook_views_arguments().
979 */
980 function competition_views_arguments() {
981
982 // Add the "Competition: ID" argument.
983 $arguments['competition_id'] = array(
984 'name' => t('Competition: ID'),
985 'help' => t('The competition ID argument allows users to filter a view by specifying an associated competition.'),
986 'handler' => 'views_handler_arg_competition_id',
987 );
988
989 // Return all of the defined arguments.
990 return $arguments;
991 }
992
993 /**
994 * Views argument handler to define the associated competition for a node.
995 */
996 function views_handler_arg_competition_id($op, &$query, $argtype, $arg = '') {
997 switch ($op) {
998 case 'summary':
999 $fieldinfo['field'] = competition_views_get_associated_id();
1000 $fieldinfo['fieldname'] = 'competition_id';
1001 $query->add_field('parent_nid', 'relativity');
1002 return $fieldinfo;
1003 break;
1004 case 'sort':
1005 $query->add_orderby(NULL, competition_views_get_associated_id(),
1006 $argtype, 'competition_id');
1007 break;
1008 case 'filter':
1009 $query->add_where(competition_views_get_associated_id() . " = $arg");
1010 break;
1011 case 'link':
1012 $title = check_plain(db_result(db_query("
1013 SELECT title FROM {node} WHERE nid = %d
1014 ", $query->competition_id)));
1015 return l($title, "$arg/$query->competition_id");
1016 case 'title':
1017 return check_plain(db_result(db_query("
1018 SELECT title FROM {node} WHERE nid = %d
1019 ", $query)));
1020 }
1021 }
1022
1023 /**
1024 * Implementation of hook_views_default_views().
1025 */
1026 function competition_views_default_views() {
1027
1028 // Create an array to hold the defined views.
1029 $views = array();
1030
1031 /*
1032 * Display only open competitions.
1033 */
1034 $view = new stdClass();
1035 $view->name = 'competition_open';
1036 $view->description = 'A view of all open competitions';
1037 $view->access = array (
1038 );
1039 $view->view_args_php = '';
1040 $view->page = FALSE;
1041 $view->page_title = '';
1042 $view->page_header = '';
1043 $view->page_header_format = '1';
1044 $view->page_footer = '';
1045 $view->page_footer_format = '1';
1046 $view->page_empty = '';
1047 $view->page_empty_format = '1';
1048 $view->page_type = 'node';
1049 $view->url = '';
1050 $view->use_pager = TRUE;
1051 $view->nodes_per_page = '10';
1052 $view->sort = array (
1053 );
1054 $view->argument = array (
1055 );
1056 $view->field = array (
1057 );
1058 $view->filter = array (
1059 array (
1060 'tablename' => 'node',
1061 'field' => 'status',
1062 'operator' => '=',
1063 'options' => '',
1064 'value' => '1',
1065 ),
1066 array (
1067 'tablename' => 'node',
1068 'field' => 'type',
1069 'operator' => 'OR',
1070 'options' => '',
1071 'value' => array (
1072 0 => 'competition',
1073 ),
1074 ),
1075 array (
1076 'tablename' => 'node_data_field_competition_period',
1077 'field' => 'field_competition_period_value_default',
1078 'operator' => '<=',
1079 'options' => 'now',
1080 'value' => '',
1081 ),
1082 array (
1083 'tablename' => 'node_data_field_competition_period',
1084 'field' => 'field_competition_period_value_to|default',
1085 'operator' => '>=',
1086 'options' => 'now',
1087 'value' => '',
1088 ),
1089 );
1090 $view->exposed_filter = array (
1091 );
1092 $view->requires = array(node, node_data_field_competition_period);
1093 $views[$view->name] = $view;
1094
1095 /*
1096 * Display the latest competitions.
1097 */
1098 $view = new stdClass();
1099 $view->name = 'competition_latest';
1100 $view->description = 'A teaser listing of the latest competitions';
1101 $view->access = array (
1102 );
1103 $view->view_args_php = '';
1104 $view->page = TRUE;
1105 $view->page_title = 'Latest Competitions';
1106 $view->page_header = '';
1107 $view->page_header_format = '1';
1108 $view->page_footer = '';
1109 $view->page_footer_format = '1';
1110 $view->page_empty = 'There are no competitions available at this time.';
1111 $view->page_empty_format = '1';
1112 $view->page_type = 'teaser';
1113 $view->url = 'competition/latest';
1114 $view->use_pager = TRUE;
1115 $view->nodes_per_page = '5';
1116 $view->menu = TRUE;
1117 $view->menu_title = '';
1118 $view->menu_tab = TRUE;
1119 $view->menu_tab_weight = '0';
1120 $view->menu_tab_default = TRUE;
1121 $view->menu_tab_default_parent = NULL;
1122 $view->menu_tab_default_parent_type = 'normal';
1123 $view->menu_parent_tab_weight = '0';
1124 $view->menu_parent_title = 'Competitions';
1125 $view->block = TRUE;
1126 $view->block_title = 'Latest Competitions';
1127 $view->block_header = '';
1128 $view->block_header_format = '1';
1129 $view->block_footer = '';
1130 $view->block_footer_format = '1';
1131 $view->block_empty = '';
1132 $view->block_empty_format = '1';
1133 $view->block_type = 'teaser';
1134 $view->nodes_per_block = '5';
1135 $view->block_more = TRUE;
1136 $view->block_use_page_header = FALSE;
1137 $view->block_use_page_footer = FALSE;
1138 $view->block_use_page_empty = TRUE;
1139 $view->sort = array (
1140 array (
1141 'tablename' => 'node_data_field_competition_period',
1142 'field' => 'field_competition_period_value',
1143 'sortorder' => 'DESC',
1144 'options' => '',
1145 ),
1146 );
1147 $view->argument = array (
1148 );
1149 $view->field = array (
1150 );
1151 $view->filter = array (
1152 array (
1153 'tablename' => 'node',
1154 'field' => 'status',
1155 'operator' => '=',
1156 'options' => '',
1157 'value' => '1',
1158 ),
1159 array (
1160 'tablename' => 'node',
1161 'field' => 'type',
1162 'operator' => 'OR',
1163 'options' => '',
1164 'value' => array (
1165 0 => 'competition',
1166 ),
1167 ),
1168 );
1169 $view->exposed_filter = array (
1170 );
1171 $view->requires = array(node_data_field_competition_period, node);
1172 $views[$view->name] = $view;
1173
1174 /**
1175 * List all competitions.
1176 */
1177 $view = new stdClass();
1178 $view->name = 'competition_all';
1179 $view->description = 'A list of all competitions on the site';
1180 $view->access = array (
1181 );
1182 $view->view_args_php = '';
1183 $view->page = TRUE;
1184 $view->page_title = 'All Competitions';
1185 $view->page_header = '';
1186 $view->page_header_format = '1';
1187 $view->page_footer = '';
1188 $view->page_footer_format = '1';
1189 $view->page_empty = 'There are no competitions on the site at this time.';
1190 $view->page_empty_format = '1';
1191 $view->page_type = 'table';
1192 $view->url = 'competition/all';
1193 $view->use_pager = TRUE;
1194 $view->nodes_per_page = '100';
1195 $view->menu = TRUE;
1196 $view->menu_title = '';
1197 $view->menu_tab = TRUE;
1198 $view->menu_tab_weight = '5';
1199 $view->menu_tab_default = FALSE;
1200 $view->menu_tab_default_parent = NULL;
1201 $view->menu_tab_default_parent_type = 'existing';
1202 $view->menu_parent_tab_weight = '0';
1203 $view->menu_parent_title = '';
1204 $view->sort = array (
1205 );
1206 $view->argument = array (
1207 );
1208 $view->field = array (
1209 array (
1210 'tablename' => 'node',
1211 'field' => 'title',
1212 'label' => 'Title',
1213 'handler' => 'views_handler_field_nodelink',
1214 'sortable' => '1',
1215 'options' => 'link',
1216 ),
1217 array (
1218 'tablename' => 'node_data_field_competition_period',
1219 'field' => 'field_competition_period_value',
1220 'label' => 'Period',
1221 'handler' => 'content_views_field_handler_group',
1222 'sortable' => '1',
1223 'options' => 'default',
1224 ),
1225 array (
1226 'tablename' => 'node_data_field_competition_round',
1227 'field' => 'field_competition_round_value',
1228 'label' => 'Round',
1229 'handler' => 'content_views_field_handler_group',
1230 'sortable' => '1',
1231 'defaultsort' => 'DESC',
1232 'options' => 'default',
1233 ),
1234 );
1235 $view->filter = array (
1236 array (
1237 'tablename' => 'node',
1238 'field' => 'status',
1239 'operator' => '=',
1240 'options' => '',
1241 'value' => '1',
1242 ),
1243 array (
1244 'tablename' => 'node',
1245 'field' => 'type',
1246 'operator' => 'OR',
1247 'options' => '',
1248 'value' => array (
1249 0 => 'competition',
1250 ),
1251 ),
1252 );
1253 $view->exposed_filter = array (
1254 );
1255 $view->requires = array(node, node_data_field_competition_period, node_data_field_competition_round);
1256 $views[$view->name] = $view;
1257
1258 /**
1259 * List all entries from which winners can be chosen.
1260 */
1261 $view = new stdClass();
1262 $view->name = 'competition_entries_list';
1263 $view->description = 'A list of all competition entries from which winners can be chosen';
1264 $view->access = array (
1265 );
1266 $view->view_args_php = '';
1267 $view->page = FALSE;
1268 $view->page_title = '';
1269 $view->page_header = '';
1270 $view->page_header_format = '1';
1271 $view->page_footer = '';
1272 $view->page_footer_format = '1';
1273 $view->page_empty = '';
1274 $view->page_empty_format = '1';
1275 $view->page_type = 'node';
1276 $view->url = '';
1277 $view->use_pager = TRUE;
1278 $view->nodes_per_page = '10';
1279 $view->sort = array (
1280 );
1281 $view->argument = array (
1282 );
1283 $view->field = array (
1284 );
1285 $view->filter = array (
1286 array (
1287 'tablename' => 'node',
1288 'field' => 'status',
1289 'operator' => '=',
1290 'options' => '',
1291 'value' => '1',
1292 ),
1293 array (
1294 'tablename' => 'node',
1295 'field' => 'type',
1296 'operator' => 'OR',
1297 'options' => '',
1298 'value' => array (
1299 0 => 'competition_entry',
1300 ),
1301 ),
1302 );
1303 $view->exposed_filter = array (
1304 );
1305 $view->requires = array(node);
1306 $views[$view->name] = $view;
1307
1308 /**
1309 * Output an array of nodes which have been submitted by the user
1310 */
1311 $view = new stdClass();
1312 $view->name = 'competition_user_entered';
1313 $view->description = 'All competitions entered by a user';
1314 $view->access = array (
1315 );
1316 $view->view_args_php = '';
1317 $view->page = TRUE;
1318 $view->page_title = 'Competitions Entered by %1';
1319 $view->page_header = '';
1320 $view->page_header_format = '1';
1321 $view->page_footer = '';
1322 $view->page_footer_format = '1';
1323 $view->page_empty = '';
1324 $view->page_empty_format = '1';
1325 $view->page_type = 'table';
1326 $view->url = 'competition/list/$arg';
1327 $view->use_pager = FALSE;
1328 $view->nodes_per_page = '0';
1329 $view->sort = array (
1330 array (
1331 'tablename' => 'node',
1332 'field' => 'created',
1333 'sortorder' => 'DESC',
1334 'options' => 'day',
1335 ),
1336 );
1337 $view->argument = array (
1338 array (
1339 'type' => 'uid',
1340 'argdefault' => '2',
1341 'title' => '',
1342 'options' => '',
1343 'wildcard' => '',
1344 'wildcard_substitution' => '',
1345 ),
1346 );
1347 $view->field = array (
1348 array (