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

Contents of /contributions/modules/bookmaker/bookmaker.module

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


Revision 1.71 - (show annotations) (download) (as text)
Mon Nov 3 21:00:39 2008 UTC (12 months, 3 weeks ago) by toddy
Branch: MAIN
CVS Tags: DRUPAL-6--1-0, HEAD
Changes since 1.70: +4 -4 lines
File MIME type: text/x-php
Update to new Form API
1 <?php
2 // $Id: bookmaker.module,v 1.70 2008/11/03 10:29:26 toddy Exp $
3 /*
4 * Copyright (C) 2006-2008 Tobias Quathamer <t.quathamer@gmx.net>
5 *
6 * This file is part of Bookmaker.
7 *
8 * Bookmaker is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * Bookmaker is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with Bookmaker; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23
24
25 /**
26 * @file
27 * Enables users to place bets on anything
28 *
29 * Enables your users to place a bet on virtually anything; you decide
30 * which bets you offer. After the outcome of the bet offer is published,
31 * the module automatically calculates the amount of points each user
32 * gets.
33 *
34 * The module is compatible with Drupal 6.x
35 *
36 * @author Tobias Quathamer
37 */
38
39
40
41 // Include helper tools
42 require_once drupal_get_path('module', 'bookmaker') .'/bookmaker.inc';
43
44
45
46 /*********************************************************************
47 * General Drupal hooks for registering the module
48 ********************************************************************/
49
50
51
52 /**
53 * Implementation of hook_node_info().
54 *
55 * This function registers the provided node types. Currently,
56 * this is only "bet_offer".
57 */
58 function bookmaker_node_info() {
59 return array(
60 'bet_offer' => array(
61 'name' => t('Bet offer'),
62 'module' => 'bookmaker_betoffer',
63 'description' => t('Bet offers enable other users to place a bet on the bet offer. You can specify how long the bet offer is accepting user bets. There is also the possibility of assigning points to correct guesses, which will be summed up for each individual user.')
64 )
65 );
66 }
67
68
69
70 /**
71 * Implementation of hook_menu().
72 */
73 function bookmaker_menu() {
74 $items['bookmaker'] = array(
75 'title' => 'Bet offers',
76 'description' => 'Show a list of all available bet offers',
77 'page callback' => 'bookmaker_show_betoffers',
78 'access arguments' => array('access content'),
79 );
80 $items['bookmaker/place_bet/%node'] = array(
81 'title' => 'Place a bet',
82 'description' => 'Place your bet on a bet offer',
83 'page callback' => 'bookmaker_userbet_place_bet',
84 'page arguments' => array(2),
85 'access arguments' => array('place a bet'),
86 );
87 return $items;
88 }
89
90
91
92 /**
93 * Implementation of hook_perm().
94 */
95 function bookmaker_perm() {
96 return array(
97 'create bet offers',
98 'edit own bet offers',
99 'place a bet',
100 );
101 }
102
103
104
105 /**
106 * Implementation of hook_access().
107 */
108 function bookmaker_betoffer_access($op, $node, $account) {
109 if ($op == 'create') {
110 return user_access('create bet offers', $account);
111 }
112
113 if ($op == 'update' || $op == 'delete') {
114 if (user_access('edit own bet offers', $account) && ($account->uid == $node->uid)) {
115 return TRUE;
116 }
117 }
118 }
119
120
121
122 /*********************************************************************
123 * Drupal hooks for providing blocks
124 ********************************************************************/
125
126
127
128 /**
129 * Implementation of hook_block().
130 */
131 function bookmaker_block($op = 'list', $delta = 0, $edit = array()) {
132 switch ($op) {
133 case 'list':
134 // return a list of block descriptions to the administrator
135 $blocks[0]['info'] = t('Shortly closing bet offers');
136 $blocks[1]['info'] = t('Recently added bet offers');
137 return $blocks;
138 case 'configure':
139 // create a form for configuring the blocks
140 $form = array();
141 if ($delta == 0) {
142 $form['bookmaker_number_of_shortly_closing_bet_offers'] = array(
143 '#type' => 'textfield',
144 '#title' => t('Number of shortly closing bet offers'),
145 '#size' => 4,
146 '#description' => t('This sets the number of shortly closing bet offers that will be shown in the block.'),
147 '#default_value' =>
148 variable_get('bookmaker_number_of_shortly_closing_bet_offers', 5),
149 );
150 }
151 if ($delta == 1) {
152 $form['bookmaker_number_of_recently_added_bet_offers'] = array(
153 '#type' => 'textfield',
154 '#title' => t('Number of recently added bet offers'),
155 '#size' => 4,
156 '#description' => t('This sets the number of recently added bet offers that will be shown in the block.'),
157 '#default_value' =>
158 variable_get('bookmaker_number_of_recently_added_bet_offers', 5),
159 );
160 }
161 return $form;
162 case 'save':
163 // save settings from the configuration form
164 if ($delta == 0) {
165 // ensure that we save a number greater than 0
166 $number = (int) $edit['bookmaker_number_of_shortly_closing_bet_offers'];
167 $number = max(1, $number);
168 variable_set('bookmaker_number_of_shortly_closing_bet_offers', $number);
169 }
170 if ($delta == 1) {
171 // ensure that we save a number greater than 0
172 $number = (int) $edit['bookmaker_number_of_recently_added_bet_offers'];
173 $number = max(1, $number);
174 variable_set('bookmaker_number_of_recently_added_bet_offers', $number);
175 }
176 return;
177 case 'view':
178 default:
179 // prepare the block for display
180 if (user_access('access content')) {
181 switch ($delta) {
182 case 0:
183 default:
184 $block['subject'] = t('Shortly closing bet offers');
185 $block['content'] = _bookmaker_block_content(0);
186 break;
187 case 1:
188 $block['subject'] = t('Recently added bet offers');
189 $block['content'] = _bookmaker_block_content(1);
190 break;
191 }
192 return $block;
193 }
194 }
195 }
196
197
198
199 /**
200 * Returns different block contents
201 */
202 function _bookmaker_block_content($delta = 0) {
203 switch ($delta) {
204 case 0:
205 default:
206 $sql = "SELECT n.nid, n.title, bm.closes_on FROM {node} AS n\n";
207 $sql .= "LEFT JOIN {bookmaker} AS bm ON n.vid=bm.vid\n";
208 $sql .= "WHERE n.type='bet_offer' AND n.status=1 AND bm.closes_on>UNIX_TIMESTAMP()\n";
209 $sql .= "ORDER BY bm.closes_on ASC, n.title ASC";
210 $limit = variable_get('bookmaker_number_of_shortly_closing_bet_offers', 5);
211 break;
212 case 1:
213 $sql = "SELECT n.nid, n.title, bm.closes_on FROM {node} AS n\n";
214 $sql .= "LEFT JOIN {bookmaker} AS bm ON n.vid=bm.vid\n";
215 $sql .= "WHERE n.type='bet_offer' AND n.status=1 AND bm.closes_on>UNIX_TIMESTAMP()\n";
216 $sql .= "ORDER BY n.created DESC, n.title ASC";
217 $limit = variable_get('bookmaker_number_of_recently_added_bet_offers', 5);
218 break;
219 }
220
221 $result = db_query_range(db_rewrite_sql($sql), 0, $limit);
222
223 $output = '<ul>';
224 while ($node = db_fetch_object($result)) {
225 $output .= '<li>'. l($node->title, "node/$node->nid") .'<br />';
226 $output .= t('@time left',
227 array('@time' => format_interval($node->closes_on - gmmktime()))) .'</li>';
228 }
229 $output .= '</ul>';
230 return $output;
231 }
232
233
234
235 /*********************************************************************
236 * Drupal hooks for the extension of an own node type
237 ********************************************************************/
238
239
240
241 /**
242 * Implementation of hook_form().
243 */
244 function bookmaker_betoffer_form(&$node) {
245 // The site admin can decide if this node type has a title and body, and how
246 // the fields should be labeled. We need to load these settings so we can
247 // build the node form correctly.
248 $type = node_get_types('type', $node);
249
250 if ($type->has_title) {
251 $form['title'] = array(
252 '#type' => 'textfield',
253 '#title' => check_plain($type->title_label),
254 '#required' => TRUE,
255 '#default_value' => $node->title,
256 '#weight' => -5
257 );
258 }
259
260 if ($type->has_body) {
261 // In Drupal 6, we can use node_body_field() to get the body and filter
262 // elements. This replaces the old textarea + filter_form() method of
263 // setting this up. It will also ensure the teaser splitter gets set up
264 // properly.
265 $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count);
266 }
267
268 // Now we define the form elements specific to a bet offer
269 if (isset($node->closes_on)) {
270 $default_value = format_date($node->closes_on, 'custom', 'Y-m-d H:i:s O');
271 }
272 else {
273 $default_value = '';
274 }
275 $form['closes_on'] = array(
276 '#type' => 'textfield',
277 '#title' => t('Closes on'),
278 '#description' => t('Format: @timeformat.',
279 array('@timeformat' => date('Y-m-d H:i:s O'))),
280 '#default_value' => $default_value,
281 '#required' => TRUE
282 );
283 $form['outcome'] = array(
284 '#type' => 'textfield',
285 '#title' => t('Outcome'),
286 '#default_value' => $node->outcome
287 );
288 $form['points'] = array(
289 '#type' => 'fieldset',
290 '#title' => t('Points'),
291 '#collapsible' => TRUE,
292 '#collapsed' => FALSE,
293 '#tree' => TRUE
294 );
295 // convert the points string into an array
296 $points = _bookmaker_betoffer_points_array($node->points);
297 $form['points']['exact'] = array(
298 '#type' => 'textfield',
299 '#title' => t('Exact match'),
300 '#default_value' => $points['exact'],
301 '#description' =>
302 t('The number of points for an exact match. Example: outcome = "5:2", user\'s bet = "5:2"')
303 );
304 $form['points']['tendency'] = array(
305 '#type' => 'textfield',
306 '#title' => t('Correct tendency'),
307 '#default_value' => $points['tendency'],
308 '#description' =>
309 t('The number of points for a correct tendency. Example: outcome = "2:3", user\'s bet = "0:4"')
310 );
311 $form['#submit'] = array('bookmaker_betoffer_node_form_submit');
312
313 return $form;
314 }
315
316
317
318 /**
319 * Implementation of hook_validate().
320 */
321 function bookmaker_betoffer_validate(&$node) {
322 if (strtotime($node->closes_on) === FALSE) {
323 form_set_error('closes_on', t('Please enter a valid date for the closing time.'));
324 }
325 }
326
327
328
329 /**
330 * Implementation of hook_submit().
331 */
332 function bookmaker_betoffer_node_form_submit($form, &$form_state) {
333 $form_state['values']['closes_on'] =
334 strtotime($form_state['values']['closes_on']);
335 $form_state['values']['points'] =
336 _bookmaker_betoffer_points_string($form_state['values']['points']);
337 }
338
339
340
341 /**
342 * Implementation of hook_insert().
343 */
344 function bookmaker_betoffer_insert($node) {
345 $sql = "INSERT INTO {bookmaker}
346 (nid, vid, closes_on, outcome, points)
347 VALUES (%d, %d, %d, '%s', '%s')";
348
349 db_query($sql,
350 $node->nid,
351 $node->vid,
352 $node->closes_on,
353 $node->outcome,
354 $node->points
355 );
356 }
357
358
359
360 /**
361 * Implementation of hook_update().
362 */
363 function bookmaker_betoffer_update($node) {
364 // if this is a new node or we're adding a new revision, insert it
365 if ($node->revision) {
366 bookmaker_betoffer_insert($node);
367 }
368 else {
369 $sql = "UPDATE {bookmaker} SET
370 closes_on = %d,
371 outcome = '%s',
372 points = '%s'
373 WHERE vid = %d";
374
375 db_query($sql,
376 $node->closes_on,
377 $node->outcome,
378 $node->points,
379 $node->vid
380 );
381 }
382
383 // extract user bets for this bet offer
384 $result = db_query("SELECT uid, bet FROM {bookmaker_bets} WHERE nid=%d",
385 $node->nid);
386 // turn the points string into an array for easier processing
387 $points = _bookmaker_betoffer_points_array($node->points);
388 // get all user's bets into an array for points calculation
389 $user_bets = array();
390 while ($bet = db_fetch_array($result)) {
391 $user_bets[$bet['uid']] = $bet['bet'];
392 }
393
394 // update user bets, if there are any
395 if (count($user_bets)) {
396 $user_points = bookmaker_calculate_user_points($node->outcome,
397 $user_bets, $points);
398
399 // update each user with the assigned points
400 foreach ($user_points as $uid => $points) {
401 $sql = "UPDATE {bookmaker_bets} SET user_points=%d WHERE nid=%d AND uid=%d";
402 db_query($sql, $points, $node->nid, $uid);
403 }
404 }
405 }
406
407
408
409 /**
410 * Implementation of hook_nodeapi().
411 *
412 * When a node revision is deleted, we need to remove the corresponding record
413 * from our table. The only way to handle revision deletion is by implementing
414 * hook_nodeapi().
415 */
416 function bookmaker_nodeapi(&$node, $op, $teaser, $page) {
417 if ($op == 'delete revision') {
418 // Notice that we're matching a single revision based on the node's vid.
419 db_query('DELETE FROM {bookmaker} WHERE vid = %d', $node->vid);
420 }
421 }
422
423
424
425 /**
426 * Implementation of hook_delete().
427 */
428 function bookmaker_betoffer_delete($node) {
429 // Notice that we're matching all revision, by using the node's nid.
430 db_query('DELETE FROM {bookmaker} WHERE nid = %d', $node->nid);
431
432 // Remove all user bets on this offer as well
433 db_query('DELETE FROM {bookmaker_bets} WHERE nid = %d', $node->nid);
434 }
435
436
437
438 /**
439 * Implementation of hook_load().
440 */
441 function bookmaker_betoffer_load($node) {
442 $sql = "SELECT
443 closes_on,
444 outcome,
445 points
446 FROM {bookmaker} WHERE vid = %d";
447
448 $additions = db_fetch_object(db_query($sql, $node->vid));
449 return $additions;
450 }
451
452
453
454 /**
455 * Implementation of hook_view().
456 */
457 function bookmaker_betoffer_view(&$node, $teaser = FALSE, $page = FALSE) {
458 $user_bets = array();
459
460 $node = node_prepare($node, $teaser);
461
462 // get the results of the bet offer
463 $sql = "SELECT * FROM {bookmaker_bets} WHERE nid=%d\n";
464 $sql .= "ORDER BY user_points DESC";
465 $result = db_query($sql, $node->nid);
466 while ($user_bet = db_fetch_array($result)) {
467 $user_bets[] = $user_bet;
468 }
469
470 $node->content['bookmaker_betoffer'] = array(
471 '#value' => theme('bookmaker_betoffer', $node, $user_bets),
472 '#weight' => 1,
473 );
474
475 return $node;
476 }
477
478
479
480 /**
481 * Implementation of hook_theme().
482 */
483 function bookmaker_theme() {
484 return array(
485 // Custom theme function
486 'bookmaker_betoffer' => array(
487 'arguments' => array('node' => NULL, 'user_bets' => NULL),
488 )
489 );
490 }
491
492
493
494 /**
495 * Custom theme function
496 */
497 function theme_bookmaker_betoffer($node, $user_bets) {
498 $points = _bookmaker_betoffer_points_array($node->points);
499 $output = '';
500
501 // show the results after the closing time
502 if ($node->closes_on <= gmmktime() && $node->outcome != '') {
503 $output .= '<p>'. t('Outcome: @outcome',
504 array('@outcome' => $node->outcome)) .'</p>';
505 if (count($user_bets) > 0) {
506 $output .= '<p>'. t('Results:') .'</p>';
507 $output .= '<ul>';
508 foreach ($user_bets as $user_bet) {
509 $output .= '<li>';
510 $user = user_load(array('uid' => $user_bet['uid']));
511 $user_points = format_plural($user_bet['user_points'],
512 '@count point', '@count points');
513 $output .= t('!username: @bet, @user_points',
514 array('!username' => theme('username', $user),
515 '@bet' => $user_bet['bet'],
516 '@user_points' => $user_points));
517 $output .= '</li>';
518 }
519 $output .= '</ul>';
520 }
521 }
522
523 // adjust the output based on the time
524 $output .= '<div class="messages status">';
525 if ($node->closes_on > gmmktime()) {
526 $output .= '<p>'. t('This bet offer closes on @date, time left: @time.',
527 array('@date' => format_date($node->closes_on),
528 '@time' => format_interval($node->closes_on - gmmktime()))) .'<br />';
529 }
530 else {
531 $output .= '<p>'. t('This bet offer has closed on @date.',
532 array('@date' => format_date($node->closes_on))) .'<br />';
533 }
534
535 $output .= format_plural(count($user_bets),
536 '@count user has placed a bet on this offer.',
537 '@count users have placed a bet on this offer.');
538 $output .= '<br />';
539
540 // show the available points for the bet offer
541 if (count($points) != 0) {
542 $output .= t('The following points are assigned:') .'</p>';
543 $output .= '<ul>';
544 if ($points['exact'] != 0) {
545 $output .= '<li>';
546 $output .= format_plural($points['exact'],
547 'Exact match: @count point', 'Exact match: @count points');
548 $output .= '</li>';
549 }
550 if ($points['tendency'] != 0) {
551 $output .= '<li>';
552 $output .= format_plural($points['tendency'],
553 'Correct tendency: @count point', 'Correct tendency: @count points');
554 $output .= '</li>';
555 }
556 $output .= '</ul>';
557 }
558 else {
559 $output .= t('No points have been assigned.') .'</p>';
560 }
561
562 $output .= '</div>';
563 return $output;
564 }
565
566
567
568 /*********************************************************************
569 * Management of bet offers
570 ********************************************************************/
571
572
573
574 /**
575 * Provide a list of all open bet offers
576 */
577 function bookmaker_show_betoffers() {
578 // get all open bet offers
579 $sql = "SELECT n.nid FROM {node} AS n\n";
580 $sql .= "LEFT JOIN {bookmaker} AS bm ON n.vid=bm.vid\n";
581 $sql .= "WHERE n.type='bet_offer' AND n.status=1 AND bm.closes_on>UNIX_TIMESTAMP()\n";
582 $sql .= "ORDER BY bm.closes_on ASC, n.title ASC";
583
584 $output = '';
585 $result = pager_query(db_rewrite_sql($sql), 10);
586 while ($node = db_fetch_object($result)) {
587 $output .= node_view(node_load($node->nid), 1);
588 }
589 $output .= theme('pager', NULL, 10);
590 return $output;
591 }
592
593
594
595 /*********************************************************************
596 * Drupal hooks and private functions for managing the user bets
597 ********************************************************************/
598
599
600
601 /**
602 * Implementation of hook_link().
603 */
604 function bookmaker_link($type, $node = NULL, $teaser = FALSE) {
605 global $user;
606
607 $links = array();
608 if ($type == 'node' and $node->type == 'bet_offer'
609 and user_access('place a bet') and $node->closes_on > gmmktime()) {
610 // change the link text if there is already a bet
611 $sql = "SELECT nid FROM {bookmaker_bets} WHERE nid=%d AND uid=%d";
612 if (db_result(db_query($sql, $node->nid, $user->uid))) {
613 $text = t('Edit bet');
614 }
615 else {
616 $text = t('Place a bet');
617 }
618 $links['bookmaker_betoffer'] = array(
619 'title' => $text,
620 'href' => "bookmaker/place_bet/$node->nid",
621 );
622 }
623 return $links;
624 }
625
626
627
628 /**
629 * Enable the user to place a bet
630 */
631 function bookmaker_userbet_place_bet($node = NULL) {
632 global $user;
633
634 // perform security check
635 if ($node->type != 'bet_offer') {
636 drupal_set_message(t('The bet offer you specified does not exist.'), 'error');
637 drupal_goto();
638 }
639
640 // is the bet offer still accepting bets?
641 if ($node->closes_on < gmmktime()) {
642 drupal_set_message(t('This bet offer has closed, you can no longer place a bet on it.'), 'error');
643 drupal_goto("node/$node->nid");
644 }
645
646 // get the user's bet
647 $sql = 'SELECT * FROM {bookmaker_bets} ';
648 $sql .= "WHERE nid=%d AND uid=%d";
649 $result = db_query($sql, $node->nid, $user->uid);
650 $row = db_fetch_object($result);
651
652 $output = node_view($node);
653 $output .= theme('box', t('Place a bet'),
654 drupal_get_form('bookmaker_userbet_place_bet_form', $node->nid, $row));
655 return $output;
656 }
657
658
659
660 /**
661 * Create a form for entering user bets
662 */
663 function bookmaker_userbet_place_bet_form($form_state, $nid, $row) {
664 // prepare a form for the entering of a user bet
665 $form['bet'] = array(
666 '#type' => 'textfield',
667 '#title' => t('Your bet'),
668 '#default_value' => $row->bet,
669 '#required' => TRUE,
670 );
671 $form['nid'] = array(
672 '#type' => 'value',
673 '#value' => $nid,
674 );
675 $form['submit'] = array(
676 '#type' => 'submit',
677 '#value' => t('Place this bet'),
678 );
679 // show a delete button if there's a user bet placed
680 if (isset($row->bet)) {
681 $form['delete'] = array(
682 '#type' => 'submit',
683 '#value' => t('Delete this bet'),
684 );
685 }
686 return $form;
687 }
688
689
690
691 /**
692 * Inserts, updates or deletes the user's bet
693 *
694 * This function explicitely set the points for the user back to 0.
695 */
696 function bookmaker_userbet_place_bet_form_submit($form, &$form_state) {
697 global $user;
698
699 // first, delete the bet
700 $sql = "DELETE FROM {bookmaker_bets} WHERE nid=%d AND uid=%d";
701 db_query($sql, $form_state['values']['nid'], $user->uid);
702
703 // check if the user only wants to delete the bet
704 $op = $_POST['op'];
705 if ($op == t('Delete this bet')) {
706 drupal_set_message(t('Your bet has been deleted.'));
707 }
708 else {
709 // create a new bet
710 $sql = "INSERT INTO {bookmaker_bets}\n";
711 $sql .= "(nid, uid, bet, placed_on, user_points)\n";
712 $sql .= "VALUES (%d, %d, '%s', %d, 0)";
713 db_query($sql, $form_state['values']['nid'], $user->uid,
714 $form_state['values']['bet'], gmmktime());
715 drupal_set_message(t('Your bet has been saved.'));
716 }
717 drupal_goto('node/'. $form_state['values']['nid']);
718 }

  ViewVC Help
Powered by ViewVC 1.1.2