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

Contents of /contributions/modules/banner/banner.module

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


Revision 1.74 - (show annotations) (download) (as text)
Tue Jan 30 23:19:33 2007 UTC (2 years, 9 months ago) by wulff
Branch: MAIN
CVS Tags: HEAD
Changes since 1.73: +48 -28 lines
File MIME type: text/x-php
#102677 - rename content field - thanks heine
#105334 - some fixes for remote banners - thanks bdragon
1 <?php
2 // $Id: banner.module,v 1.73 2007/01/30 21:53:10 wulff Exp $
3
4 /**
5 * TOC
6 * Defines
7 * Hook implementations
8 * Page handlers
9 * Menu callbacks
10 * Public functions
11 * Validation functions
12 * Utility functions
13 * Form functions
14 * Theme functions
15 */
16
17 // FIXME: we need a way to update the internal banner cache ($node->cache)
18 // FIXME: call _banner_refresh_cache when updating nodes
19 // FIXME: add admin notification on new banner submission
20 // FIXME: check that 'administer banners' role can delete/edit all banners.
21 // FIXME: check: only show stats to admin or banner owner
22 // FIXME: add banner type to admin overviews?
23 // FIXME: test availability of remote banner (do it when trying to get size)?
24 // FIXME: add help text on admin/banner and admin/help/banner
25
26 // TODO: 'text banner', 'image banner', etc. as individual node types
27
28 // die('<pre>'. print_r($foo, true) .'</pre>');
29 // die('<pre>'. $foo .'</pre>');
30
31 /**
32 * @file
33 * Banner system for rotating ads and other content.
34 */
35
36 define('BANNER_PENDING', 0);
37 define('BANNER_ENABLED', 1);
38 define('BANNER_DAY_LIMIT', 2);
39 define('BANNER_WEEK_LIMIT', 3);
40 define('BANNER_DISABLED', 4);
41 define('BANNER_BLOCKED', 5);
42 define('BANNER_DENIED', 6);
43
44 define('BANNER_UPLOAD', 0);
45 define('BANNER_TEXT', 1);
46 define('BANNER_JAVASCRIPT', 2);
47 define('BANNER_REMOTE', 3);
48
49 define('BANNER_CACHE_DB', 0);
50 define('BANNER_CACHE_FILE', 1);
51
52 /**
53 * Hook implementations
54 */
55
56 /**
57 * Implementation of hook_access().
58 */
59 function banner_access($op, $node) {
60 global $user;
61
62 if ($op == 'view') {
63 // if the user owns this banner, let them see it
64 // this is necessary because publishing status is set according to workflow
65 if ($user->uid == $node->uid) {
66 return TRUE;
67 }
68 }
69
70 if ($op == 'create') {
71 return user_access('create banners');
72 }
73
74 if ($op == 'update' || $op == 'delete') {
75 if (user_access('administer banners') || ($user->uid == $node->uid)) {
76 return TRUE;
77 }
78 }
79 }
80
81 /**
82 * Implementation of hook_block().
83 */
84 function banner_block($op = 'list', $delta = 0, $edit = array()) {
85 switch ($op) {
86 case 'list':
87 $vid = variable_get('banner_vocabulary', 0);
88 $terms = taxonomy_get_tree($vid);
89 foreach($terms as $term) {
90 $blocks[$term->tid]['info'] = $term->name;
91 }
92 return $blocks;
93 case 'configure':
94 $form['banner_block_'. $delta .'_count'] = array(
95 '#type' => 'select',
96 '#title' => t('Banner count'),
97 '#default_value' => variable_get('banner_block_'. $delta .'_count', 1),
98 '#options' => drupal_map_assoc(range(1, 5)),
99 '#description' => t('Number of banners to show from the selected banner group.'),
100 );
101 return $form;
102 case 'save':
103 variable_set('banner_block_'. $delta .'_count', $edit['banner_block_'. $delta .'_count']);
104 return;
105 case 'view':
106 $count = variable_get('banner_block_'. $delta .'_count', 1);
107 $block['subject'] = variable_get('banner_block_'. $delta .'_title', '');
108 $block['content'] = banner_display($delta, $count);
109 return $block;
110 }
111 }
112
113 /**
114 * Implementation of hook_cron().
115 */
116 function banner_cron() {
117 $notifications = array();
118 $time = time();
119
120 // reset 1 day counters (1 day minus 5 minutes to prevent loosing an hour)
121 if (($time - variable_get('banner_timestamp_day', '0')) >= 86100) {
122 variable_set('banner_timestamp_day', $time);
123 $param['reset_day_counters'] = 1;
124
125 // add notification e-mails to the queue
126 $result = db_query('SELECT n.nid, n.title, b.views_day, b.views_week, b.views, b.clicks_day, b.clicks_week, b.clicks, n.uid, b.notify_failed FROM {node} n INNER JOIN {banner} b ON n.vid = b.vid WHERE notify_day = 1 AND workflow < 4');
127 while ($banner = db_fetch_object($result)) {
128 $notifications[] = array(
129 'subject' => variable_get('site_name', 'drupal') .':'. t("'%banner' banner daily notification", array('%banner' => "$banner->title")),
130 'message' => variable_get('banner_daily_notification', _banner_get_body('daily_notify')),
131 'banner' => $banner,
132 );
133 }
134
135 // FIXME: only run this if the scheduler module is enabled
136 /*
137 if (variable_get('banner_renewal', 0)) {
138 // add renewal reminder e-mails to the queue
139 $result = db_query('SELECT title, day_views, week_views, views, day_clicks, week_clicks, clicks, uid, unpublish_date, failed_notify, id FROM {banner} WHERE reminder_sent = 0 AND unpublish_date > 0 AND unpublish_date <= %d AND (status = 1 OR status = 2 OR status = 3 OR status = 4)', ($time + variable_get('banner_renewal_time', 0)));
140 while ($banner = db_fetch_object($result)) {
141 $notifications[] = array(
142 'subject' => variable_get('site_name', 'drupal') .': '. t("'%banner' banner expiring soon", array('%banner' => "$banner->title")),
143 'message' => variable_get('banner_renewal_message', _banner_get_body('renewal')),
144 'banner' => $banner,
145 );
146 db_query('UPDATE {banner} SET notify_sent = 1 WHERE nid = '. $banner->nid);
147 }
148 }
149 */
150 }
151
152 // reset 7 day counters (1 week minus 5 minutes to prevent loosing an hour)
153 if (($time - variable_get('banner_timestamp_week', '0')) >= 604500) {
154 variable_set('banner_timestamp_week', $time);
155 $param['reset_week_counters'] = 1;
156
157 // add notification e-mails to the queue
158 $result = db_query('SELECT n.nid, n.title, b.views_day, b.views_week, b.views, b.clicks_day, b.clicks_week, b.clicks, n.uid FROM {node} n INNER JOIN {banner} b ON n.vid = b.vid WHERE notify_day = 1 AND workflow < 4');
159 while ($banner = db_fetch_object($result)) {
160 $notifications[] = array(
161 'subject' => variable_get('site_name', 'drupal') .': '. t("'%banner' banner weekly notification", array('%banner' => "$banner->title")),
162 'message' => variable_get('banner_weekly_notification', _banner_get_body('weekly_notify')),
163 'banner' => $banner,
164 );
165 }
166 }
167
168 // limit general cron functionality to once every 5 minutes
169 if (($time - variable_get('banner_timestamp_gc', '0')) >= 300) {
170 variable_set('banner_timestamp_gc', $time);
171 $param['refresh_cache'] = true; // we set this because we do an isset check below
172 }
173
174 // reset counters
175 if (isset($param)) {
176 _banner_refresh_cache($param);
177 }
178
179 // send notification e-mails
180 foreach ($notifications as $notification) {
181 _banner_mail($notification['subject'], $notification['message'], $notification['banner']);
182 }
183 }
184
185 /**
186 * Implementation of hook_delete().
187 */
188 function banner_delete($node) {
189 db_query('DELETE FROM {banner} WHERE nid=%d', $node->nid);
190 }
191
192 /**
193 * Implementation of hook_form().
194 */
195 function banner_form(&$node) {
196 // general settings
197 $form['title'] = array(
198 '#type' => 'textfield',
199 '#title' => t('Title'),
200 '#required' => TRUE,
201 '#default_value' => $node->title,
202 '#description' => t("A name for this banner, used in administration only, e.g. 'job opening', 'product launch'."),
203 '#required' => TRUE,
204 '#weight' => -9,
205 );
206 $form['body'] = array(
207 '#type' => 'textarea',
208 '#title' => t('Description'),
209 '#default_value' => $node->body,
210 '#rows' => 5,
211 '#description' => t('Enter a description of the banner. This will be shown in the administrator overview and on banner comment pages.'),
212 '#weight' => -8,
213 );
214
215 $form['format'] = filter_form($node->format);
216 $form['format']['#weight'] = -7;
217
218 // add forms for banner settings
219 $form['banner'] = _banner_form_banner($node);
220
221 $form['notifications'] = _banner_form_notifications($node);
222 $form['limits'] = _banner_form_limits($node);
223 if (user_access('administer banners') && $node->nid) {
224 $form['statistics'] = _banner_form_statistics($node);
225 }
226
227 return $form;
228 }
229
230 /**
231 * Implementation of hook_form_alter().
232 */
233 function banner_form_alter($form_id, &$form) {
234 if ($form_id == 'banner_node_form') {
235 // hide 'published' checkbox, status is set in hook_validate
236 $form['options']['status']['#type'] = 'hidden';
237 }
238
239 // hide options from banner vocabulary editing form
240 if ($form_id == 'taxonomy_form_vocabulary') {
241 if ($form['vid']['#value'] == variable_get('banner_vocabulary', '')) {
242 $form['help_forum_vocab'] = array(
243 '#value' => t('This is the designated banner group vocabulary. Some of the normal vocabulary options have been removed.'),
244 '#weight' => -1,
245 );
246 $form['nodes']['banner'] = array('#type' => 'checkbox', '#value' => 1, '#title' => t('banner'), '#attributes' => array('disabled' => '' ));
247 unset($form['hierarchy']);
248 unset($form['relations']);
249 unset($form['tags']);
250 unset($form['multiple']);
251 $form['required'] = array('#type' => 'value', '#value' => 1);
252 }
253 }
254 }
255
256 /**
257 * Implementation of hook_help().
258 */
259 function banner_help($section) {
260 switch ($section) {
261 case 'admin/help#banner':
262 return t("
263 <h2>Banner modes</h2>
264 <dl>
265 <dt>Upload</dt><dd>Use the upload mode if you want to use an uploaded image or a text file
266 as a banner. Enter the basic banner information and click 'File attachments' on
267 the node edit form. You can add as many files as you wish to your banner, but
268 only the first file on the list will be used. If you are uploading a text file,
269 the text '%url' is replaced by the banner URL (e.g. banner/7) when the banner
270 is displayed.</dd>
271 <dt>Text</dt><dd>Use the text mode if you wish to enter the banner text directly on the
272 node edit form. Enter the banner text in the 'Content' field. The text '%url'
273 is replaced by the banner URL (e.g. banner/7) when the banner is displayed.</dd>
274 <dt>JavaScript</dt><dd>Use the JavaScript mode if you wish to use a piece of JavaScript to
275 display your banner. Enter the JavaScript in the 'Content' field. This won't
276 support Google ads without manual modification of your site template to include
277 Google's JavaScript.</dd>
278 <dt>Remote</dt><dd>Use the remote mode to use a remote image as a banner. Enter the URL of
279 the remote image in the 'Content' field.</dd>
280 </dl>
281 ");
282 case 'admin/modules#description':
283 return t('An advertising system.');
284 case 'node/add#banner':
285 return t('Banners are texts or images which are shown in fixed locations on each page.');
286 case 'admin/banner':
287 return '<p>'. t('This is a list of your banners.') .'</p>';
288 case 'admin/settings/banner':
289 return '<p>'. t('Choose banner cache and notification settings.') .'</p>';
290 }
291 }
292
293 /**
294 * Implementation of hook_insert().
295 */
296 function banner_insert($node) {
297 $fields = _banner_get_fields();
298 $keys = array_keys($fields);
299 foreach ($node as $key => $value) {
300 if ($value && in_array($key, $keys)) {
301 // this is a banner field, insert it
302 $k[] = $key;
303 $v[] = $value;
304 $s[] = is_int($fields[$key]) ? '%d' : "'%s'";
305 }
306 }
307
308 $status = _banner_get_status($node->workflow);
309
310 // FIXME: this and the code in _menu() has race condition written all over it...
311 $cache_todo = variable_get('banner_cache_todo', array());
312 array_push($cache_todo, $node->nid);
313 variable_set('banner_cache_todo', $cache_todo);
314
315 db_query('INSERT INTO {banner} ('. implode(',', $k) .') VALUES ('. implode(',', $s) .')', $v);
316 db_query('UPDATE {node} SET status = %d WHERE nid = %d', $status, $node->nid);
317 }
318
319 /**
320 * Implementation of hook_load().
321 */
322 function banner_load($node) {
323 $fields = array_keys(_banner_get_fields());
324
325 // remove nid and vid from field list
326 unset($fields[0]);
327 unset($fields[1]);
328
329 $banner = db_fetch_object(db_query('SELECT '. implode(',', $fields) .' FROM {banner} WHERE vid=%d', $node->vid));
330 return $banner;
331 }
332
333 /**
334 * Implementation of hook_menu().
335 */
336 function banner_menu($may_cache) {
337 $items = array();
338
339 if ($may_cache) {
340 // create content
341 $items[] = array(
342 'path' => 'node/add/banner',
343 'title' => t('Banner'),
344 'access' => user_access('create banners'),
345 );
346
347 // 'my banners' page
348 $items[] = array(
349 'path' => 'banner/view',
350 'title' => t('My banners'),
351 'access' => user_access('create banners'),
352 'callback' => 'banner_page',
353 'weight' => 1,
354 );
355 $items[] = array(
356 'path' => 'banner/view/mine',
357 'title' => t('All'),
358 'type' => MENU_DEFAULT_LOCAL_TASK,
359 'weight' => 1,
360 );
361 $items[] = array(
362 'path' => 'banner/view/active',
363 'title' => t('Active'),
364 'type' => MENU_LOCAL_TASK,
365 'weight' => 2,
366 );
367 $items[] = array(
368 'path' => 'banner/view/enabled',
369 'title' => t('Enabled'),
370 'type' => MENU_LOCAL_TASK,
371 'weight' => 3,
372 );
373 $items[] = array(
374 'path' => 'banner/view/pending',
375 'title' => t('Pending'),
376 'type' => MENU_LOCAL_TASK,
377 'weight' => 4,
378 );
379 $items[] = array(
380 'path' => 'banner/view/blocked',
381 'title' => t('Blocked'),
382 'type' => MENU_LOCAL_TASK,
383 'weight' => 5,
384 );
385
386 // admin menu
387 $items[] = array(
388 'path' => 'admin/content/banner',
389 'title' => t('Banners'),
390 'description' => t('List and edit banners.'),
391 'access' => user_access('administer banners'),
392 'callback' => 'banner_admin',
393 );
394 $items[] = array(
395 'path' => 'admin/content/banner/list',
396 'title' => t('List'),
397 'access' => user_access('administer banners'),
398 'type' => MENU_DEFAULT_LOCAL_TASK,
399 'weight' => -1,
400 );
401 $items[] = array(
402 'path' => 'admin/content/banner/refresh',
403 'title' => t('Refresh cache'),
404 'access' => user_access('administer banners'),
405 'type' => MENU_LOCAL_TASK,
406 'callback' => 'banner_admin',
407 );
408
409 // settings
410 $items[] = array(
411 'path' => 'admin/settings/banner',
412 'title' => t('Banner'),
413 'description' => t('Manage banner settings.'),
414 'callback' => 'drupal_get_form',
415 'callback arguments' => array('banner_admin_settings'),
416 'access' => user_access('administer site configuration'),
417 );
418 }
419 else {
420 if (arg(0) == 'banner' && is_numeric(arg(1))) {
421 // redirect
422 $items[] = array(
423 'path' => 'banner/'. arg(1),
424 'title' => t('Banner'),
425 'access' => user_access('view banners'),
426 'type' => MENU_CALLBACK,
427 'callback' => 'banner_redirect',
428 'callback arguments' => array(arg(1)),
429 );
430 }
431
432 // from http://drupal.org/node/35739
433 $stylesheet = drupal_get_path('theme', $GLOBALS['theme_key'] .'/banner.css');
434 if (!file_exists($stylesheet)) {
435 $stylesheet = drupal_get_path('module', 'banner') .'/banner.css';
436 }
437 drupal_add_css($stylesheet);
438 }
439
440 // render cache content for new banners
441 // FIXME: simply re-render cache for banners with empty caches (with a limit of <n> per run)
442 // this should also make it simple to add a 'clear cache' function
443 $cache_todo = variable_get('banner_cache_todo', array());
444 foreach ($cache_todo as $nid) {
445 $node = node_load($nid);
446 $cache = "document.write('". str_replace(array("'", "\n", "\r"), array("\"", " ", " "), _banner_view($node)) ."');";
447 db_query("UPDATE {banner} SET cache='%s' WHERE vid=%d", $cache, $node->vid);
448 }
449 variable_del('banner_cache_todo');
450
451 return $items;
452 }
453
454 /**
455 * Implementation of hook_node_info().
456 */
457 function banner_node_info() {
458 return array(
459 'banner' => array(
460 'name' => t('banner'),
461 'module' => 'banner',
462 'description' => t('Advertising banners or other rotating content.'),
463 'body_label' => t('Description'),
464 )
465 );
466 }
467
468 /**
469 * Implementation of hook_nodeapi().
470 */
471 function banner_nodeapi(&$node, $op, $teaser, $page) {
472 switch ($op) {
473 case 'delete revision':
474 db_query('DELETE FROM {banner} WHERE vid=%d', $node->vid);
475 break;
476 }
477 }
478
479 /**
480 * Implementation of hook_perm().
481 */
482 function banner_perm() {
483 return array('administer banners', 'create banners', 'view banners');
484 }
485
486 /**
487 * Implementation of hook_taxonomy().
488 */
489 function banner_taxonomy($op, $type, $term = NULL) {
490 if ($op == 'delete' && $term['vid'] == variable_get('banner_vocabulary', '')) {
491 switch ($type) {
492 case 'vocabulary':
493 variable_del('banner_vocabulary');
494 }
495 }
496 }
497
498 /**
499 * Implementation of hook_validate().
500 */
501 function banner_validate(&$node, $form) {
502 // check target url
503 if ($node->mode != BANNER_JAVASCRIPT) {
504 if (!valid_url($node->url, TRUE)) {
505 form_set_error('url', t('You must enter a valid target URL for the banner.'));
506 }
507 }
508
509 // check that required content for selected mode is present
510 switch ($node->mode) {
511 case BANNER_UPLOAD:
512 if (!is_array($node->files)) {
513 form_set_error('upload', t('You need to upload at least one file to use the selected mode.'));
514 }
515 break;
516 case BANNER_TEXT:
517 if (empty($node->banner_content)) {
518 form_set_error('banner_content', t('You need to enter the banner text in the content field to use the selected mode.'));
519 }
520 break;
521 case BANNER_JAVASCRIPT:
522 if (empty($node->banner_content)) {
523 form_set_error('banner_content', t('You need to enter the script in the content field to use the selected mode.'));
524 }
525 break;
526 case BANNER_REMOTE:
527 if (valid_url($node->banner_content, TRUE)) {
528 // Have Drupal fetch a copy of the remote file for us.
529 $result = drupal_http_request($node->banner_content, array(), 'GET');
530 if ($result->code == 200) { // Status: OK
531 // Acquire a temporary file.
532 $file = tempnam(file_directory_temp(), 'file');
533 $handle = fopen($file, 'wb');
534 if ($handle) {
535 // Copy our data into the temporary file
536 fwrite($handle, $result->data);
537 fclose($handle);
538 if ((list($node->width, $node->height) = getimagesize($file))) {
539 form_set_value($form['banner']['width'], $node->width);
540 form_set_value($form['banner']['height'], $node->height);
541 }
542 else {
543 drupal_set_message(t('Could not determine size of remote image.'), 'error');
544 }
545 // Delete our temporary file
546 file_delete($file);
547 }
548 else {
549 drupal_set_message(t('Could not create temporary file while attempting to determine size of remote image.'), 'error');
550 }
551 }
552 else {
553 drupal_set_message(t('Could not determine size of remote image: Unexpected response from remote webserver.'), 'error');
554 }
555 }
556 else {
557 form_set_error('banner_content', t('You need to enter a valid URL in the content field to use the selected mode.'));
558 }
559 break;
560 }
561
562 // get information from uploaded file
563 if ($node->files) {
564 foreach ($node->files as $file) {
565 $file = (object)$file;
566
567 if (!isset($active)) {
568 // we use the first file on the list...
569 $active = $file;
570 }
571
572 // ...and make sure that no files are listed
573 $file->list = 0;
574 }
575
576 switch (_banner_type($file->filemime)) {
577 case 'image':
578 case 'swf':
579 list($width, $height) = getimagesize($file->filepath);
580 form_set_value($form['banner']['width'], $width);
581 form_set_value($form['banner']['height'], $height);
582 break;
583 case 'text':
584 break;
585 default:
586 form_set_error('upload', t('Unsupported file type.'));
587 }
588 }
589 }
590
591 /**
592 * Implementation of hook_view().
593 */
594 function banner_view(&$node, $teaser = FALSE, $page = FALSE) {
595 global $user;
596
597 $node = node_prepare($node, $teaser);
598
599 $output = '';
600 switch ($node->mode) {
601 case BANNER_UPLOAD:
602 $output .= theme('banner_view_upload', $node);
603 break;
604 case BANNER_TEXT:
605 $output .= '<blockquote>';
606 $output .= theme('banner_view_text', $node);
607 $output .= '</blockquote>';
608 break;
609 case BANNER_JAVASCRIPT:
610 $output .= theme('banner_view_javascript', $node); // FIXME: theme function missing?
611 break;
612 case BANNER_REMOTE:
613 $output .= theme('banner_view_remote', $node);
614 }
615
616 if ($teaser == TRUE) {
617 if (user_access('administer banners') || (user_access('create banners') && $user->uid == $node->uid)) {
618 $output .= _banner_view_statistics($node);
619 }
620
621 // $node->teaser .= $output;
622 $node->banner_content['teaser']['#value'] .= $output;
623 }
624 else {
625 if (user_access('administer banners') || (user_access('create banners') && $user->uid == $node->uid)) {
626 // don't show full info on node preview
627 if ($node->nid) {
628 $output .= _banner_view_overview($node);
629 $output .= _banner_view_statistics($node);
630 $output .= _banner_view_limits($node);
631
632 // if (module_exists('scheduler')) {} // FIXME: display scheduling information
633
634 $output .= _banner_view_notifications($node);
635 }
636 }
637
638 $node->banner_content['banner_body'] = array(
639 '#value' => $output,
640 '#weight' => 1,
641 );
642 }
643
644 return $node;
645 }
646
647 /**
648 * Implementation of hook_update().
649 */
650 function banner_update($node) {
651 // FIXME: if limits have changed, update workflow & status to match - do it here to catch revisions
652
653 if ($node->revision) {
654 banner_insert($node);
655 }
656 else {
657 $fields = _banner_get_fields();
658 $keys = array_keys($fields);
659
660 $node->cache = "document.write('". str_replace(array("'", "\n", "\r"), array("\"", " ", " "), _banner_view($node)) ."');";
661
662 foreach ($node as $key => $value) {
663 if (in_array($key, $keys)) {
664 // this is a banner field, update it
665 $param = $key;
666 $param .= is_int($fields[$key]) ? '=%d' : "='%s'";
667 $q[] = $param;
668 if ($value) {
669 $v[] = $value;
670 }
671 else {
672 $v[] = $fields[$key];
673 }
674 }
675 }
676 $v[] = $node->vid;
677
678 $status = _banner_get_status($node->workflow);
679
680 db_query('UPDATE {banner} SET '. implode(',', $q) .' WHERE vid=%d', $v);
681 db_query('UPDATE {node} SET status = %d WHERE nid = %d', $status, $node->nid);
682
683 // reset stats if requested
684 if ($node->reset_day) {
685 db_query('UPDATE {banner} SET views_day=0, clicks_day=0 WHERE vid=%d', $v, $node->vid);
686 }
687 if ($node->reset_week) {
688 db_query('UPDATE {banner} SET views_week=0, clicks_week=0 WHERE vid=%d', $v, $node->vid);
689 }
690 if ($node->reset_total) {
691 db_query('UPDATE {banner} SET views=0, clicks=0 WHERE vid=%d', $v, $node->vid);
692 }
693 }
694 }
695
696 /**
697 * Page handlers
698 */
699
700 /**
701 * Page handler for admin page
702 */
703 function banner_admin() {
704 $op = arg(2);
705
706 switch ($op) {
707 case 'refresh':
708 $output = _banner_admin_refresh();
709 break;
710 default: // list
711 $output = _banner_admin_overview();
712 }
713
714 return $output;
715 }
716
717 /**
718 * Page handler for settings page
719 */
720 function banner_admin_settings() {
721 // FIXME: reset number of cache files (see original module) -- but do it in a _validate function
722
723 // check for required modules
724 $requirements = '';
725 if (!module_exists('upload')) {
726 $requirements .= '<p>'. t('The <a href="@url">upload module</a> must be enabled and configured if you wish to use uploaded images or text files as banners.', array('@url' => 'http://drupal.org/handbook/modules/upload')) .'</p>';
727 }
728 if (!module_exists('scheduler')) {
729 $requirements .= '<p>'. t('The <a href="@url">scheduler module</a> must be installed and configured if you wish to set publishing and unpublishing dates for banners.', array('@url' => 'http://drupal.org/project/scheduler')) .'</p>';
730 }
731 if (!module_exists('taxonomy')) {
732 $requirements .= '<p>'. t('The <a href="@url">taxonomy module</a> must be installed and configured.', array('@url' => 'http://drupal.org/handbook/modules/taxonomy')) .'</p>';
733 }
734
735 if (!empty($requirements)) {
736 $form['requirements'] = array(
737 '#type' => 'fieldset',
738 '#title' => t('Requirements'),
739 );
740 $form['requirements']['scheduler'] = array(
741 '#type' => 'markup',
742 '#value' => $requirements,
743 );
744 }
745
746 $mail_variables = '%username, %bannername, %sitename, %day_views, %day_clicks, %week_views, %week_clicks, %total_views, %total_clicks, %url, %expire';
747
748 // cache settings
749 $form['cache'] = array(
750 '#type' => 'fieldset',
751 '#title' => t('Cache settings'),
752 );
753 $form['cache']['banner_cache'] = array(
754 '#type' => 'radios',
755 '#title' => t('Cache handler'),
756 '#default_value' => variable_get('banner_cache', 0),
757 '#options' => array(
758 BANNER_CACHE_DB => t('database'),
759 BANNER_CACHE_FILE => t('file'),
760 ),
761 '#description' => t('Choose a cache handler. Database caching has lower performance but always works.'),
762 '#validate' => array('_banner_validate_cache' => array()),
763 );
764 $form['cache']['banner_cache_max'] = array(
765 '#type' => 'select',
766 '#title' => t('Maximum cache files'),
767 '#default_value' => variable_get('banner_cache_max', 1),
768 '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 10, 25)),
769 '#description' => t('If using the file cache handler, specify here the maximum number of cache files that should be created. Using only one cache file guarantees that banners will be disabled exactly when they cross a threshold such as being displayed the maximum allowed number of times. On busier sites, it can boost performance to use multiple cache files.'),
770 );
771
772 // user notification settings
773 $form['notification'] = array(
774 '#type' => 'fieldset',
775 '#title' => t('User notification settings'),
776 '#collapsible' => TRUE,
777 '#collapsed' => TRUE,
778 );
779 $form['notification']['banner_daily_notification'] = array(
780 '#type' => 'textarea',
781 '#title' => t('Body of daily notification e-mail'),
782 '#default_value' => variable_get('banner_daily_notification', _banner_get_body('daily_notify')),
783 '#cols' => 70,
784 '#rows' => 10,
785 '#description' => t('Customize the body of the daily notification e-mail, only sent to banner owners when they enable daily notification. Available variables are: %mail_variables.', array('%mail_variables' => $mail_variables)),
786 );
787 $form['notification']['banner_weekly_notification'] = array(
788 '#type' => 'textarea',
789 '#title' => t('Body of weekly notification e-mail'),
790 '#default_value' => variable_get('banner_weekly_notification', _banner_get_body('weekly_notify')),
791 '#cols' => 70,
792 '#rows' => 10,
793 '#description' => t('Customize the body of the weekly notification e-mail, only sent to banner owners when they enable weekly notification. Available variables are: %mail_variables.', array('%mail_variables' => $mail_variables)),
794 );
795 $form['notification']['banner_failed_notify'] = array(
796 '#type' => 'select',
797 '#title' => t('Disable notifications'),
798 '#default_value' => variable_get('banner_failed_notify', 5),
799 '#options' => array(t('Disabled'), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20),
800 '#description' => t('Disable notifications to user after this many failed mail attempts.'),
801 );
802
803 // reminder settings
804 $form['reminder'] = array(
805 '#type' => 'fieldset',
806 '#title' => t('Renewal reminder settings'),
807 '#collapsible' => TRUE,
808 '#collapsed' => TRUE,
809 );
810 $form['reminder']['banner_renewal'] = array(
811 '#type' => 'radios',
812 '#title' => t('Automatic renewal reminder'),
813 '#default_value' => variable_get('banner_renewal', 0),
814 '#options' => array(t('Disabled'), t('Enabled')),
815 '#description' => t('If enabled, an automatic reminder can be e-mailed when an advertisement is about to expire. This e-mail can remind your customers that they need to renew their advertisement on your site.'),
816 );
817 $form['reminder']['banner_renewal_time'] = array(
818 '#type' => 'select',
819 '#title' => t('Send reminder'),
820 '#default_value' => variable_get('banner_renewal_time', 0),
821 '#options' => _banner_get_periods(),
822 '#description' => t('Send the renewal reminder this long before the advertisement expires.'),
823 );
824 $form['reminder']['banner_renewal_message'] = array(
825 '#type' => 'textarea',
826 '#title' => t('Body of renewal reminder e-mail'),
827 '#default_value' => variable_get('banner_renewal_message', _banner_get_body('renewal')),
828 '#cols' => 70,
829 '#rows' => 10,
830 '#description' => t('Customize the body of the renewal reminder e-mail, only sent once to banner owners when their ad is about to expire. Available variables are: %mail_variables', array('%mail_variables' => $mail_variables)),
831 );
832
833 return system_settings_form($form);
834 }
835
836 /**
837 * Page handler for 'my banners' page
838 */
839 function banner_page() {
840 global $user;
841
842 // get latest versions of the banner node
843 $query = 'SELECT n.nid FROM {node} n INNER JOIN {banner} b ON n.vid=b.vid WHERE n.uid='. $user->uid;
844 switch (arg(2)) {
845 case 'active':
846 $active = array(BANNER_PENDING, BANNER_ENABLED, BANNER_DAY_LIMIT, BANNER_WEEK_LIMIT, BANNER_DISABLED);
847 $query .= ' AND b.workflow IN ('. implode(',', $active) .')';
848 break;
849 case 'enabled':
850 $query .= ' AND b.workflow='. BANNER_ENABLED;
851 break;
852 case 'pending':
853 $query .= ' AND b.workflow='. BANNER_PENDING;
854 break;
855 case 'blocked':
856 $query .= ' AND b.workflow='. BANNER_BLOCKED;
857 break;
858 default: // 'mine'
859 }
860 $query .= ' ORDER BY n.sticky DESC, n.created DESC';
861
862 $result = pager_query(db_rewrite_sql($query), variable_get('default_nodes_main', 10));
863
864 $output = '';
865 if (db_num_rows($result)) {
866 while ($node = db_fetch_object($result)) {
867 $output .= node_view(node_load($node->nid), TRUE);
868 }
869 $output .= theme('pager', NULL, variable_get('default_nodes_main', 10));
870 }
871 else {
872 $output .= theme('box', t('No banners found.'), '');
873 }
874
875 return $output;
876 }
877
878 /**
879 * Menu callbacks
880 */
881
882 /**
883 * Update banner statistics and redirect user to target URL
884 */
885 function banner_redirect() {
886 global $user;
887
888 $banner = node_load(arg(1));
889
890 if ($banner) {
891 // don't update banner stats for site admin or banner owner
892 if ($user->uid != 1 && $user->uid != $banner->uid) {
893 db_query('UPDATE {banner} SET clicks = clicks + 1, clicks_day = clicks_day + 1, clicks_week = clicks_week + 1 WHERE nid = %d', arg(1));
894 }
895
896 if ($banner->clicks_max > 0 && ($banner->clicks + 1) == $banner->clicks_max) {
897 watchdog('special', t("banner: '%banner' %status, reached max of %clicks clicks.", array('%banner' => l($banner->title, 'node/'. $banner->id, NULL, NULL, FALSE, TRUE), '%status' => _banner_workflow(BANNER_BLOCKED), '%clicks' => $banner->max_clicks)));
898 db_query('UPDATE {banner} SET workflow = %d WHERE vid = %d', BANNER_BLOCKED, $banner->vid);
899 // _banner_refresh_cache(); // FIXME
900 }
901
902 // redirect to the banner's target url
903 header('Location: '. $banner->url);
904 exit();
905 }
906 else {
907 drupal_not_found();
908 }
909 }
910
911 /**
912 * Public functions
913 */
914
915 /**
916 * Display one or more banners from the selected group.
917 *
918 * @param $group
919 * The banner group to display
920 * @param $count
921 * The number of banners to show from the selected group
922 * @return
923 * <script> tag for use in HTML
924 */
925 function banner_display($group = 0, $count = 1) {
926 global $base_url;
927
928 if (user_access('view banners')) {
929 $files = array('banner_db.php', 'banner_file.php');
930 $tids = array();
931
932 // Do we have *any* banners in this group?
933 $banners = db_result(db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {term_node} tn on n.nid = tn.nid INNER JOIN {banner} b ON b.nid = n.nid WHERE n.type = 'banner' AND tn.tid = %d AND b.workflow = %d", $group, BANNER_ENABLED));
934 if($banners == 0) {
935 return;
936 }
937
938 // only do taxonomy checking on nodes
939 if (arg(0) == 'node' && is_numeric(arg(1))) {
940 // get vocabularies associated with banners
941 $vocabularies = taxonomy_get_vocabularies('banner');
942
943 // ...but ignore the banner group vocabulary
944 $vid = variable_get('banner_vocabulary', '');
945 unset($vocabularies[$vid]);
946
947 // get terms from current node associated with these vocabs
948 foreach($vocabularies as $vocabulary) {
949 if($terms = taxonomy_node_get_terms_by_vocabulary(arg(1), $vocabulary->vid)) {
950 foreach ($terms as $term) {
951 $tids[] = $term->tid;
952 }
953 }
954 }
955 }
956
957 // see if we have any banners in the current group with one of these terms
958 $comp = array();
959 $result = db_query('SELECT tn2.tid FROM {node} n INNER JOIN {term_node} tn ON n.nid = tn.nid INNER JOIN {term_node} tn2 ON n.nid = tn2.nid INNER JOIN {banner} b ON n.vid = b.vid WHERE tn.tid = %d AND b.workflow = 1', $group);
960 while ($row = db_fetch_array($result)) {
961 $comp[] = $row['tid'];
962 }
963
964 $tids = array_intersect($tids, $comp);
965 if (!$tids) {
966 $tids[] = 0;
967 }
968
969 $url = $base_url .'/'. $files[variable_get('banner_cache', '0')];
970 $qry = array(
971 'group' => $group,
972 'count' => $count,
973 'terms' => implode(',', $tids),
974 'max' => variable_get('banner_cache_max', 1),
975 'path' => file_create_path(),
976 );
977 foreach ($qry as $key => $value) {
978 $query[] = $key .'='. $value;
979 }
980 $source = url($url, implode('&amp;', $query));
981
982 // FIXME: do something else for javascript banners?
983 return '<script type="text/javascript" src="'. $source .'"></script>';
984 }
985 }
986
987 /**
988 * Validation functions
989 */
990
991 /**
992 * Make sure that the selected cache driver is present
993 */
994 function _banner_validate_cache($form) {
995 global $form_values;
996
997 $cache_type = $form_values['banner_cache'];
998 $filename = $cache_type ? 'banner_file.php' : 'banner_db.php';
999 if (file_exists($filename)) {
1000 $output = @file_get_contents($filename);
1001 if (!$output) {
1002 form_set_error('banner_cache', t('Failed to access "%filename" due to improper file permissions. Unable to display banners.', array('%filename' => $filename)));
1003 }
1004 }
1005 else {
1006 form_set_error('banner_cache', t('Failed to access "%filename", file does not exist. Unable to display banners.', array('%filename' => $filename)));
1007 }
1008 }
1009
1010 /**
1011 * Utility functions
1012 */
1013
1014 /**
1015 * Show list of all banners
1016 */
1017 function _banner_admin_overview() {
1018 $headers = array(
1019 array('data' => t('Title'), 'field' => 'title'),
1020 array('data' => t('Status'), 'field' => 'workflow', 'sort' => 'asc'),
1021 array('data' => t('Owner'), 'field' => 'uid'),
1022 array('data' => t('Views'), 'field' => 'views'),
1023 array('data' => t('Clicks'), 'field' => 'clicks'),
1024 array('data' => t('Operations'), 'colspan' => 2)
1025 );
1026
1027 $sql = 'SELECT n.*, b.*, u.name FROM {node} n INNER JOIN {banner} b ON n.vid=b.vid INNER JOIN {users} u ON n.uid=u.uid';
1028 $sql .= tablesort_sql($headers);
1029
1030 $result = pager_query($sql, 25);
1031
1032 if (db_num_rows($result) > 0) {
1033 $destination = drupal_get_destination();
1034 while ($node = db_fetch_object($result)) {
1035 $row = array();
1036
1037 $row[] = $node->title;
1038 $row[] = _banner_workflow($node->workflow);
1039 $row[] = theme('username', $node);
1040 $row[] = $node->views;
1041 $row[] = $node->clicks;
1042 $row[] = array('data' => l(t('view'), 'node/'. $node->nid));
1043 $row[] = array('data' => l(t('edit'), 'node/'. $node->nid .'/edit', array(), $destination));
1044
1045 $rows[] = $row;
1046 }
1047 if($pager = theme('pager', NULL, 25, 0)) {
1048 $rows[] = array(array('data' => $pager, 'colspan' => 9));
1049 }
1050
1051 return theme('table', $headers, $rows);
1052 }
1053 else {
1054 return theme('box', t('No banners found.'), '');
1055 }
1056 }
1057
1058 /**
1059 * Refresh banner cache
1060 */
1061 function _banner_admin_refresh($param = array(), $max = 0) {
1062 $cache_type = variable_get('banner_cache', '0');
1063
1064 if ($cache_type == BANNER_CACHE_FILE) {
1065 _banner_refresh_cache();
1066 drupal_set_message(t('File cache refreshed.'), 'status');
1067 }
1068 else {
1069 drupal_set_message(t("You are using the database cache. The database cache can't be refreshed."), 'error');
1070 drupal_goto('admin/banner');
1071 }
1072
1073 return '';
1074 }
1075
1076 /**
1077 * Return message body
1078 */
1079 function _banner_get_body($message) {
1080 switch ($message) {
1081 case 'daily_notify':
1082 return t("Hello %username,\n\nHere is your daily summary for your '%bannername' banner on %sitename:\n\nToday:\n Views: %day_views\n Clicks: %day_clicks\n\nThis week:\n Views: %week_views\n Clicks: %week_clicks\n\nAll time:\n Views: %total_views\n Clicks: %total_clicks\n\n--\nThis e-mail was automatically generated at your request.\nUnsubscribe here: %url");
1083 case 'weekly_notify':
1084 return t("Hello %username,\n\nHere is your weekly summary for your '%bannername' banner on %sitename:\n\nToday:\n Views: %day_views\n Clicks: %day_clicks\n\nThis week:\n Views: %week_views\n Clicks: %week_clicks\n\nAll time:\n Views: %total_views\n Clicks: %total_clicks\n\n--\nThis e-mail was automatically generated at your request.\nUnsubscribe here: %url");
1085 case 'renewal':
1086 return t("Hello %username,\n\nYour advertisement '%bannername' on %sitename will expire on %expire. Please let us know if you are interested in renewing.\n\nTo date, this ad has been viewed %total_views times and clicked %total_clicks times.\n\nThank you!\n%sitename team");
1087 case 'upload_notify':
1088 return t("Hello %admin,\n\n%sitename user '%username' has uploaded a new banner titled, '%bannername'.\n\n%pending_url");
1089 }
1090 }
1091
1092 /**
1093 * Return an array of banner database fields
1094 */
1095 function _banner_get_fields() {
1096 return array(
1097 'nid' => 0,
1098 'vid' => 0,
1099
1100 // basic information
1101 'url' => '',
1102 'target' => '',
1103 'workflow' => 0,
1104 'mode' => 0,
1105 'banner_content' => '',
1106 'cache' => '',
1107
1108 // notifications
1109 'notify_day' => 0,
1110 'notify_week' => 0,
1111 'notify_failed' => 0,
1112 'notify_send' => 0,
1113 'notify_sent' => 0,
1114
1115 // limits
1116 'chance' => 0,
1117 'clicks_max' => 0,
1118 'views_max' => 0,
1119 'views_week_max' => 0,
1120 'views_day_max' => 0,
1121
1122 // statistics
1123 'views' => 0,
1124 'views_week' => 0,
1125 'views_day' => 0,
1126 'clicks' => 0,
1127 'clicks_week' => 0,
1128 'clicks_day' => 0,
1129
1130 // file
1131 'width' => 0,
1132 'height' => 0,
1133 );
1134 }
1135
1136 /**
1137 * Return description of banner status.
1138 */
1139 function _banner_get_mode_options() {
1140 $modes = array();
1141
1142 if (user_access('upload files')) {
1143 $modes[BANNER_UPLOAD] = t('upload');
1144 }
1145 $modes[BANNER_TEXT] = t('text');
1146 if (user_access('administer banners')) {
1147 $modes[BANNER_JAVASCRIPT] = t('javascript');
1148 }
1149 $modes[BANNER_REMOTE] = t('remote');
1150
1151 return $modes;
1152 }
1153
1154 /**
1155 * Return an array of reminder periods
1156 */
1157 function _banner_get_periods() {
1158 return array(
1159 86400 => format_interval(86400),
1160 172800 => format_interval(172800),
1161 259200 => format_interval(259200),
1162 345600 => format_interval(345600),