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

Contents of /contributions/modules/ad/ad.module

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


Revision 1.2 - (show annotations) (download) (as text)
Tue Dec 26 08:10:19 2006 UTC (2 years, 11 months ago) by jeremy
Branch: MAIN
CVS Tags: DRUPAL-4-7--0-5, HEAD
Branch point for: DRUPAL-4-7
Changes since 1.1: +186 -58 lines
File MIME type: text/x-php
Implement schema for owners, permissions, notifications, and remote
hosts.  Move from Alpha to Beta status, meaning going forward an upgrade
path will be provided for schema changes.
 - ad.install
    o define ad_permissions table
    o define ad_owners table
    o define ad_notifications table
    o define ad_hosts table
 - ad.module
    o fix ad statistics to display even if none for current hour
    o implement statistics for current week
    o register unique hostid for each add owner for displaying ads
      remotely
    o enforce matching ad status and node status
    o only display clicks and statistics for ads, not all ad types
    o stub in support for ad owners, ad owner permissions and add owner
      notifications
    o display group name instead of just group id when listing ads
    o add regex in search for adserve.php to not match ie .swp files
 - adserve.php
    o implement cache types in switch statement
    o track hostid when viewing ads for displaying on remote sites
 - ad_text.module
    o use htmlentities() to encode ad text
1 <?php
2 // $Id: ad.module $
3
4 /**
5 * @file
6 * An advertising system for Drupal powered websites.
7 *
8 * Ad Module 0.3.1
9 * Copyright (c) 2005-2006.
10 * Jeremy Andrews <jeremy@kerneltrap.org>. All rights reserved.
11 */
12
13 /**
14 * Use this function to display an ad from a specified group.
15 */
16 function ad($group = 'default') {
17 global $base_url;
18
19 /**
20 * This function will be called to display advertisements. It will start
21 * simple but will rapidly evolve to handle the following list of TODO's.
22 *
23 * TODO: support taxonomy, ie only display ad in specified taxonomies
24 * TODO: support roles, display certain ads only to certain roles
25 * TODO: support multiple display methods (local and remote)
26 * TODO: track who is displaying the ad (local versus remote)
27 */
28
29 $adserve = variable_get('adserve', '');
30 if (file_exists($adserve)) {
31 if (user_access('show advertisements')) {
32 // Count how many dirs above the adserve.php dir to the index.php dir.
33 $up = (sizeof(explode('/', $adserve)) - 1);
34
35 $cache = variable_get('ad_cache', 'database');
36 $src = url("$base_url/$adserve?group=$group&up=$up&cache=$cache");
37 }
38 }
39 else {
40 drupal_set_message(t('Ads cannot be displayed. The ad module is %misconfigured, failed to locate the required adserve.php file "<em>%adserve</em>".', array('%misconfigured' => l(t('misconfigured'), 'admin/ad/configure'), '%adserve' => $adserve)), 'error');
41 }
42
43 /**
44 * TODO: Support other display methods than our own JavaScript. For example,
45 * we may want to serve ads using externally provided JavaScripts, or straight
46 * urls, etc...
47 */
48 $output = "<script type=\"text/javascript\" src=\"$src\"></script>\n";
49
50 return theme('ad_display', $group, $output);
51 }
52
53 /**
54 * Function to display the actual advertisement to the screen. Wrap it in a
55 * theme function to make it possible to customize in your own theme.
56 */
57 function theme_ad_display($group, $display) {
58 $output = "<div class=\"advertisement\" id=\"group-$group\">\n";
59 $output .= $display;
60 $output .= "</div>";
61 return $output;
62 }
63
64 /**
65 * Update click counter then redirect host to ad's target URL.
66 */
67 function ad_redirect($aid, $optional = NULL) {
68 global $user;
69 ad_statistics_increment($aid, 'click');
70 db_query("INSERT INTO {ad_clicks} (aid, uid, hostname, url, timestamp) VALUES (%d, %d, '%s', '%s', %d)", $aid, $user->uid, $_SERVER['REMOTE_ADDR'], referer_uri(), time());
71
72 // Determine where we're supposed to redirect the user.
73 $adtype = db_result(db_query('SELECT adtype FROM {ads} WHERE aid = %d', $aid));
74 $node->aid = $aid;
75 $node->optional = $optional;
76 $url = module_invoke('ad_'. $adtype, 'adapi', 'redirect', $node);
77 if (isset($url)) {
78 header('Location: '. $url);
79 }
80 else {
81 drupal_goto('');
82 }
83 }
84
85 /**
86 * Increment action counter.
87 */
88 function ad_statistics_increment($aid, $action) {
89 // Update action statistics.
90 db_query("UPDATE {ad_statistics} SET count = count + 1 WHERE aid = %d AND action = '%s' AND date = %d", $aid, $action, date('YmdH'));
91 // If column doesn't already exist, we need to add it.
92 if (!db_affected_rows()) {
93 db_query("INSERT INTO {ad_statistics} (aid, date, action, count) VALUES(%d, %d, '%s', 1)", $aid, date('YmdH'), $action);
94 // If another process already added this row our INSERT will fail, if so we
95 // still need to increment it so we don't loose an action.
96 if (!db_affected_rows()) {
97 db_query("UPDATE {ad_statistics} SET count = count + 1 WHERE aid = %d AND action = '%s' AND date = %d", $aid, $action, date('YmdH'));
98 }
99 }
100 }
101
102 function ad_status_array($admin = TRUE) {
103 if ($admin) {
104 // status options for administrators
105 return array(
106 t('pending') => t('This advertisement is currently waiting for adminsitrative approval.'),
107 t('active') => t('This advertisement is actively being displayed.'),
108 t('offline') => t('This advertisement has been temporarily disabled by its owner and is not currently being displayed.'),
109 t('unpublished') => t('This advertisement has been unpublished and is not currently being displayed.'),
110 t('expired') => t('This advertisement has expired or was otherwise disabled by an administrator.'),
111 t('denied') => t('This advertisement was refused by the site administrator, it will not be displayed.'));
112 }
113 else {
114 // status options for advertisement owners
115 return array(
116 t('active') => t('This advertisement is actively being displayed.'),
117 t('offline') => t('This advertisement has been temporarily disabled and is not currently being displayed.'));
118 }
119 }
120
121 /**
122 * Calculate statistics for the given advertisements.
123 * TODO: Introduce caching to make this more efficient.
124 */
125 function ad_statistics($aid) {
126 // Get global statistics.
127 $statistics['global']['views'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'view'", $aid));
128 $statistics['global']['clicks'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'click'", $aid));
129
130 // No sense in making further queries if the ad has no global statistics.
131 if (!$statistics['global']['views'] && !$statistics['global']['clicks']) {
132 return $statistics;
133 }
134
135 // Get statistics for this year and last year.
136 $this_year = date('Y000000');
137 $last_year = date('Y') - 1 .'000000';
138 $statistics['last_year']['views'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'view' AND date >= %d AND date <= %d", $aid, $last_year, $this_year));
139 $statistics['last_year']['clicks'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'click' AND date >= %d AND date <= %d", $aid, $last_year, $this_year));
140 $statistics['this_year']['views'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'view' AND date >= %d", $aid, $this_year));
141 $statistics['this_year']['clicks'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'click' AND date >= %d", $aid, $this_year));
142
143 // No sense in making further queries if the ad has no statistics this year.
144 if (!$statistics['this_year']['views'] && !$statistics['this_year']['clicks']) {
145 return $statistics;
146 }
147
148 // Get statistics for this month and last month.
149 $this_month = date('Ym0000');
150 $last_month = date('m') - 1;
151 if ($last_month == 0) {
152 $last_month = date('Y') - 1 .'120000';
153 }
154 else {
155 $last_month = date('Y'). $last_month .'0000';
156 }
157 $statistics['last_month']['views'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'view' AND date >= %d AND date <= %d", $aid, $last_month, $this_month));
158 $statistics['last_month']['clicks'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'click' AND date >= %d AND date <= %d", $aid, $last_month, $this_month));
159 $statistics['this_month']['views'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'view' AND date >= %d", $aid, $this_month));
160 $statistics['this_month']['clicks'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'click' AND date >= %d", $aid, $this_month));
161
162 // No sense in making further queries if the ad has no statistics this month.
163 if (!$statistics['this_month']['views'] && !$statistics['this_month']['clicks']) {
164 return $statistics;
165 }
166
167 // Get statistics for this week.
168 $statistics['this_week']['views'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'view' AND date > %d AND date <= %d", $aid, date('Ymd00', time() - 60*60*24*7), date('YmdH', time())));
169 $statistics['this_week']['clicks'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'click' AND date > %d AND date <= %d", $aid, date('Ymd00', time() - 60*60*24*7), date('YmdH', time())));
170
171 // No sense in making further queries if the ad has no statistics this week.
172 if (!$statistics['this_week']['views'] && !$statistics['this_week']['clicks']) {
173 return $statistics;
174 }
175
176 // Get statistics for yesterday and today.
177 $statistics['yesterday']['views'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'view' AND date >= %d AND date <= %d", $aid, date('Ymd00', time() - 60*60*24), date('Ymd00', time())));
178 $statistics['yesterday']['clicks'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'click' AND date >= %d AND date <= %d", $aid, date('Ymd00', time() - 60*60*24), date('Ymd00', time())));
179 $statistics['today']['views'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'view' AND date >= %d", $aid, date('Ymd00', time())));
180 $statistics['today']['clicks'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'click' AND date >= %d", $aid, date('Ymd00', time())));
181
182 // No sense in making further queries if the ad has no statistics today.
183 if (!$statistics['today']['views'] && !$statistics['today']['clicks']) {
184 return $statistics;
185 }
186
187 // Get statistics for this hour and the last hour.
188 $statistics['this_hour']['views'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'view' AND date = %d", $aid, date('YmdH', time())));
189 $statistics['this_hour']['clicks'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'click' AND date = %d", $aid, date('YmdH', time())));
190 $statistics['last_hour']['views'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'view' AND date = %d", $aid, date('YmdH', time() - (60*60))));
191 $statistics['last_hour']['clicks'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'click' AND date = %d", $aid, date('YmdH', time() - (60*60))));
192
193 return $statistics;
194 }
195
196 function theme_ad_statistics_display($aid) {
197 $statistics = ad_statistics($aid);
198 $headers = array('', t('Views'), t('Clicks'), t('Click-thru'));
199 $rows = array();
200
201 $data = array('this_hour' => t('This hour'), 'last_hour' => t('Last hour'), 'today' => t('Today'), 'yesterday' => t('Yesterday'), 'this_week' => t('Last seven days'), 'last_week' => t('Last week'), 'this_month' => t('This month'), 'last_month' => t('Last month'), 'this_year' => t('This year'), 'last_year' => t('Last year'), 'global' => t('All time'));
202
203 foreach ($data as $key => $value) {
204 if ($statistics[$key]['views'] || $statistics[$key]['clicks'] || $key == 'global') {
205 $rows[] = array(
206 array('data' => $value),
207 array('data' => (int)$statistics[$key]['views']),
208 array('data' => (int)$statistics[$key]['clicks']),
209 array('data' => $statistics[$key]['views'] ? sprintf('%1.2f', ((int)$statistics[$key]['clicks'] / (int)$statistics[$key]['views']) * 100) .'%' : '0.00%'),
210 );
211 }
212 }
213 return theme('box', '', theme('table', $headers, $rows));
214 }
215
216 /****************
217 * Drupal hooks *
218 ****************/
219
220 /**
221 * Drupal _help hook. Provides help and information text about the ad module.
222 *
223 * @path Current display path.
224 * @return Text appropriate for the current $path.
225 */
226 function ad_help($path) {
227 switch ($path) {
228 case 'admin/modules#description':
229 $output = t('An advertising system for Drupal powered websites.');
230 break;
231 case 'admin/help#ad':
232 $output = '<p>'. t('The ad module provides a complete advertising system for Drupal powered websites. It does this through an API that allow other modules to handle various types of advertising content. For example, if enabled together with the ad_image module you will be able to display image based advertisements such as banner ads.') .'</p>';
233 break;
234 case 'node/add#ad':
235 $output = t('Advertisements can be randomly displayed to visitors of your website. Statistics for how often each advertisement is viewed and clicked are collected.');
236 break;
237 }
238 return $output;
239 }
240
241 /**
242 * Drupal _perm hook. Establishes permissions used by this module.
243 *
244 * @return An array of permissions used by this module.
245 */
246 function ad_perm() {
247 return (
248 array('administer advertisements',
249 'create advertisements',
250 'edit own advertisements',
251 'show advertisements')
252 );
253 }
254
255 /**
256 */
257 function ad_node_info() {
258 return array('ad' => array('name' => t('advertisement'), 'base' => 'ad'));
259 }
260
261 /**
262 */
263 function ad_access($op, $node) {
264 global $user;
265
266 if ($op == 'create') {
267 return user_access('create advertisements');
268 }
269
270 if ($op == 'update' || $op == 'delete') {
271 return (user_access('administer advertisements') || ($node->uid == $user->uid && user_access('edit own advertisements')));
272 }
273 }
274
275 /**
276 * Drupal _form hook.
277 */
278 function ad_form(&$node) {
279 $form = array();
280 $edit = $_POST['edit'];
281
282 $type = arg(3);
283 if (function_exists("ad_$type". '_type')) {
284 $adtype = $type;
285 }
286 else {
287 $adtype = $node->adtype;
288 }
289
290 $form['aid'] = array(
291 '#type' => 'value',
292 '#value' => $node->nid,
293 );
294
295 $form['title'] = array(
296 '#type' => 'textfield',
297 '#title' => t('Title'),
298 '#required' => TRUE,
299 '#default_value' => $node->title,
300 );
301 $form['body'] = array(
302 '#type' => 'textarea',
303 '#title' => t('Description'),
304 '#required' => TRUE,
305 '#default_value' => $node->body,
306 '#rows' => 3
307 );
308
309 // determine the current ad type
310 if (!isset($adtype)) {
311 $adtypes = module_invoke_all('adapi', 'type');
312 switch (sizeof($adtypes)) {
313 case 0:
314 drupal_set_message(t('At least one ad type module must be enabled before you can create advertisements. For example, try %enabling the ad_text or ad_image module.', array('%enabling' => l('enabling', 'admin/modules'))), 'error');
315 break;
316 case 1:
317 $adtype = $adtypes[0];
318 break;
319 default:
320 $adtype = arg(3) ? arg(3) : $edit['adtype'];
321 $form['adtype'] = array(
322 '#type' => 'radios',
323 '#title' => t('Style of ad'),
324 '#options' => drupal_map_assoc($adtypes),
325 '#default_value' => $adtype ? $adtype : $adtypes[0],
326 '#required' => TRUE,
327 '#description' => t('Select the type of ad that you wish to create from the above options.')
328 );
329 break;
330 }
331 }
332
333 // display type-specific options
334 if (isset($adtype)) {
335 $elements = module_invoke('ad_'. $adtype, 'adapi', 'form', $node);
336 foreach ($elements as $element => $values) {
337 $form["$element"] = $values;
338 }
339 $form['adtype'] = array(
340 '#type' => 'hidden',
341 '#value' => $adtype,
342 );
343 }
344
345 // display group selection menu
346 $form['group'] = array(
347 '#type' => 'fieldset',
348 '#title' => t('Group'),
349 '#collapsible' => TRUE,
350 );
351 $form['group']['gid'] = array(
352 '#type' => 'radios',
353 '#default_value' => $node->gid ? $node->gid : 1,
354 '#options' => ad_groups_list(),
355 '#description' => t('Assign your advertisement to a group. When you wish to display advertisements, you will choose a group of advertisements to display in one position on your screen.'),
356 );
357
358 if (user_access('administer advertisements')) {
359 // admins can set any status on advertisements
360 $form['adstatus'] = array(
361 '#type' => 'fieldset',
362 '#title' => t('Status'),
363 '#collapsible' => TRUE
364 );
365 foreach (ad_status_array() as $status => $description) {
366 $form['adstatus']["ad$status"] = array(
367 '#type' => 'radio',
368 '#title' => t("$status"),
369 '#return_value' => $status,
370 '#default_value' => $node->adstatus ? $node->adstatus : t('pending'),
371 '#description' => "$description",
372 '#parents' => array("adstatus")
373 );
374 }
375 }
376 else {
377 // display status options
378 $adstatus = ad_status_array(FALSE);
379 if (isset($node->adstatus) && isset($adstatus["$node->adstatus"])) {
380 $form['adstatus'] = array(
381 '#type' => 'fieldset',
382 '#title' => t('Status'),
383 '#collapsible' => TRUE
384 );
385 foreach ($adstatus as $status => $description) {
386 $form['adstatus']["ad$status"] = array(
387 '#type' => 'radio',
388 '#title' => t("$status"),
389 '#return_value' => $status,
390 '#default_value' => $node->adstatus,
391 '#description' => "$description", '#parents' => array("adstatus")
392 );
393 }
394 }
395 else {
396 $adstatus = ad_status_array();
397 if (!isset($node->adstatus)) {
398 $node->adstatus = t('pending');
399 }
400 $form['adstatus_display'] = array(
401 '#type' => 'markup',
402 '#value' => '<p><b>'. t('Status') .':</b> '. $node->adstatus .'<br />'. $adstatus["$node->adstatus"]
403 );
404 $form['adstatus'] = array(
405 '#type' => 'value',
406 '#value' => $node->adstatus
407 );
408 }
409 }
410
411 // display statistics
412 $form['statistics'] = array(
413 '#type' => 'fieldset',
414 '#title' => t('Statistics'),
415 '#collapsible' => TRUE,
416 );
417
418 $form['statistics']['data'] = array(
419 '#type' => 'markup',
420 '#prefix' => '<div>',
421 '#suffix' => '</div>',
422 '#value' => theme_ad_statistics_display($node->nid),
423 );
424
425 // display scheduling options
426 $form['schedule'] = array(
427 '#type' => 'fieldset',
428 '#title' => t('Scheduling'),
429 '#collapsible' => TRUE,
430 '#collapsed' => TRUE,
431 );
432 $form['schedule']['current'] = array(
433 '#type' => 'markup',
434 '#prefix' => '<div>',
435 '#suffix' => '</div>',
436 '#value' => t('The current date and time is "%date".', array('%date' => format_date(time(), 'custom', 'F j, Y H:i')))
437 );
438 $form['schedule']['autoactivate'] = array(
439 '#type' => 'textfield',
440 '#title' => t('Automatically activate ad'),
441 '#required' => FALSE,
442 '#default_value' => $node->autoactivate ? format_date((int)$node->autoactivate, 'custom', 'F j, Y H:i') : '',
443 '#description' => t('You can specify a date and time for this advertisement to be automatically activated. The advertisement needs to be in an "offline" state before it can be automatically activated. If you prefer to activate the advertisement immediately, leave this field empty.')
444 );
445
446 if (user_access('administer advertisements')) {
447 // admins can expire advertisements
448 $form['schedule']['autoexpire'] = array(
449 '#type' => 'textfield',
450 '#title' => t('Automatically expire ad'),
451 '#required' => FALSE,
452 '#default_value' => $node->autoexpire ? format_date((int)$node->autoexpire, 'custom', 'F j, Y H:i') : '',
453 '#description' => t('You can specify a date and time for this advertisement to be automatically expired. If you don\'t want the advertisement to expire, leave this field empty.')
454 );
455 }
456 else {
457 // display expiration time
458 if (isset($node->autoexpire)) {
459 $form['schedule']['autoexpire_display'] = array(
460 '#type' => 'markup',
461 '#value' => t('This ad will automatically expire in %date.', array('%date' => format_interval($node->autoexpire - time())))
462 );
463 $form['schedule']['autoexpire'] = array(
464 '#type' => 'hidden',
465 '#value' => $node->autoexpire
466 );
467 }
468 }
469
470 return $form;
471 }
472
473 /**
474 * Drupal _nodeapi hook.
475 */
476 function ad_nodeapi(&$node, $op, $teaser, $page) {
477 global $user;
478
479 switch ($op) {
480
481 case 'load':
482 $ad = db_fetch_array(db_query('SELECT * FROM {ads} WHERE aid = %d', $node->nid));
483 $adtype = module_invoke('ad_'. $ad['adtype'], 'adapi', 'load', $ad);
484 if (is_array($adtype)) {
485 return array_merge($ad, $adtype);
486 }
487 else {
488 return $ad;
489 }
490 break;
491
492 case 'insert':
493 if ($node->adtype) {
494 if ($node->status != 1 && $node->adstatus == 'active') {
495 $node->adstatus = t('unpublished');
496 ad_statistics_increment($node->nid, 'unpublish');
497 }
498 db_query("INSERT INTO {ads} (aid, uid, gid, adstatus, adtype, redirect, autoactivate, autoexpire) VALUES(%d, %d, %d, '%s', '%s', '%s', %d, %d)", $node->nid, $node->uid, $node->gid, $node->adstatus, $node->adtype, url("ad/redirect/$node->nid"), strtotime($node->autoactivate), strtotime($node->autoexpire));
499 ad_host_id_create($node->uid);
500 ad_statistics_increment($node->nid, 'create');
501 }
502 break;
503
504 case 'update':
505 if ($node->adtype) {
506 if ($node->status != 1 && $node->adstatus == 'active') {
507 $node->adstatus = t('unpublished');
508 ad_statistics_increment($node->nid, 'unpublish');
509 }
510 else if ($node->status == 1 && $node->adstatus == 'unpublished') {
511 $node->adstatus = t('active');
512 ad_statistics_increment($node->nid, 'publish');
513 }
514 db_query("UPDATE {ads} SET uid = %d, gid = %d, adstatus = '%s', adtype = '%s', autoactivate = %d, autoexpire = %d WHERE aid = %d", $node->uid, $node->gid, $node->adstatus, $node->adtype, strtotime($node->autoactivate), strtotime($node->autoexpire), $node->nid);
515 ad_host_id_create($node->uid);
516 ad_statistics_increment($node->nid, 'update');
517 }
518 break;
519
520 case 'delete':
521 db_query("DELETE FROM {ads} WHERE aid = %d", $node->nid);
522 db_query("DELETE FROM {ad_statistics} WHERE aid = %d", $node->nid);
523 break;
524
525 case 'view':
526 if ($node->adtype) {
527 $node = node_prepare($node, $teaser);
528 if (user_access('administer advertisements')) {
529 $node->body .= theme_ad_statistics_display($node->nid);
530
531 $header = array(
532 array('data' => t('Time'), 'field' => 'timestamp', 'sort' => 'desc'),
533 array('data' => t('Username'), 'field' => 'uid'),
534 array('data' => t('IP address'), 'field' => 'hostname'),
535 array('data' => t('URL where clicked'), 'field' => 'url'),
536 );
537
538 if ($node->nid) {
539 $sql = "SELECT timestamp,uid,hostname,url FROM {ad_clicks} WHERE aid = $node->nid";
540 $sql .= tablesort_sql($header);
541 $result = pager_query($sql, 15);
542
543 while ($ad = db_fetch_object($result)) {
544 $row = array();
545 $click_user = user_load(array('uid' => $ad->uid));
546 $row[] = format_date($ad->timestamp, 'custom', 'F j, Y H:i');
547 $row[] = $ad->uid ? l($click_user->name, "user/$ad->uid") : variable_get('anonymous', 'Anonymous');
548 $row[] = $ad->hostname;
549 $row[] = l($ad->url, $ad->url);
550 $rows[] = $row;
551 }
552
553 $output = theme('table', $header, $rows);
554 $output .= theme('pager', NULL, 50, 0);
555 $node->body .= theme('box', t('Click history'), $output);
556 }
557 }
558 }
559 break;
560
561
562 case 'validate':
563 break;
564
565 }
566 // insert ad type data
567 if ($node->adtype) {
568 module_invoke('ad_'. $node->adtype, 'adapi', $op, $node);
569 }
570 }
571
572 /**
573 * Drupal _menu hook.
574 */
575 function ad_menu($may_cache) {
576 global $user;
577 $items = array();
578
579 if ($may_cache) {
580 // menu items
581 $items[] = array('path' => 'admin/ad',
582 'title' => t('ads'),
583 'callback' => 'ad_admin_list',
584 'access' => user_access('administer advertisements'));
585
586 // tabs
587 $items[] = array('path' => 'admin/ad/list',
588 'title' => t('list'),
589 'callback' => 'ad_admin_list',
590 'type' => MENU_DEFAULT_LOCAL_TASK);
591 $items[] = array('path' => 'admin/ad/configure',
592 'title' => t('settings'),
593 'callback' => 'ad_admin_configure_settings',
594 'type' => MENU_LOCAL_TASK,
595 'weight' => 3);
596 $items[] = array('path' => 'admin/ad/groups',
597 'title' => t('groups'),
598 'callback' => 'ad_admin_groups_configure',
599 'type' => MENU_LOCAL_TASK,
600 'weight' => 5);
601
602 // configure sub tabs
603 $items[] = array('path' => 'admin/ad/configure/global',
604 'title' => t('global settings'),
605 'callback' => 'ad_admin_configure_settings',
606 'type' => MENU_DEFAULT_LOCAL_TASK,
607 'weight' => 0);
608 $items[] = array('path' => 'admin/ad/groups/list',
609 'title' => t('list'),
610 'callback' => 'ad_admin_groups_configure',
611 'type' => MENU_DEFAULT_LOCAL_TASK,
612 'weight' => 0);
613
614
615 $items[] = array('path' => 'node/add/ad',
616 'title' => t('ad'),
617 'callback' => 'ad_add',
618 'access' => user_access('create advertisements'));
619 }
620 else {
621 // callbacks
622
623 if (arg(0) == 'ad' && arg(1) == 'redirect' && is_numeric(arg(2))) {
624 $aid = arg(2);
625 $optional = arg(3);
626 $items[] = array('path' => "ad/redirect/$aid",
627 'access' => user_access('show advertisements'),
628 'type' => MENU_CALLBACK,
629 'callback' => 'ad_redirect',
630 'callback arguments' => array($aid, $optional));
631 }
632 elseif (arg(1) == 'ad' && arg(2) == 'groups' && is_numeric(arg(3))) {
633 $gid = arg(3);
634 $items[] = array('path' => "admin/ad/groups/$gid/delete",
635 'title' => t('delete'),
636 'callback' => 'ad_admin_groups_delete',
637 'type' => MENU_CALLBACK,
638 'weight' => 1);
639 }
640 elseif (arg(0) == 'node' && is_numeric(arg(1)) &&
641 (user_access('administer advertisements') || user_access('edit own advertisements'))) {
642 $node = node_load(array('nid' => arg(1)));
643
644 $items[] = array('path' => "node/$node->nid/ad",
645 'access' => user_access('administer advertisements') ||
646 ($node->uid == $user->uid && user_access('edit own advertisements')),
647 'title' => t('ad owners'),
648 'callback' => 'ad_owners_overview',
649 'callback arguments' => array($node),
650 'type' => MENU_LOCAL_TASK,
651 'weight' => 5);
652
653 if (is_numeric(arg(3))) {
654 $uid = arg(3);
655 $ad_user = user_load(array('uid' => $uid));
656 $items[] = array('path' => "node/$node->nid/ad/$uid/permissions",
657 'title' => t('%owner\'s permissions', array('%owner' => $ad_user->name)),
658 'callback' => 'ad_owner_permissions',
659 'callback arguments' => array($node->nid, $uid),
660 'type' => MENU_LOCAL_TASK,
661 'weight' => 0);
662 $items[] = array('path' => "node/$node->nid/ad/$uid/notifications",
663 'title' => t('%owner\'s notifications', array('%owner' => $ad_user->name)),
664 'callback' => 'ad_owner_notifications',
665 'callback arguments' => array($node->nid, $uid),
666 'type' => MENU_LOCAL_TASK,
667 'weight' => 1);
668 }
669 }
670 }
671
672 return $items;
673 }
674
675 /**
676 * Drupal _block hook.
677 */
678 function ad_block($op = 'list', $delta = 0, $edit = array()) {
679 switch ($op) {
680 case 'list':
681 $blocks = array();
682 $result = db_query('SELECT gid,name FROM {ad_groups}');
683 while ($ad = db_fetch_object($result)) {
684 $blocks[$ad->gid]['info'] = t('ad group: <em>%name</em>', array('%name' => $ad->name));
685 }
686 return $blocks;
687 case 'view':
688 $group = db_result(db_query('SELECT name FROM {ad_groups} WHERE gid = %d', $delta));
689 $block['content'] = ad($group);
690 return $block;
691 }
692 }
693
694 /****/
695
696 /**
697 * Present a list of ad types to choose from.
698 */
699 function ad_add() {
700 global $user;
701
702 $edit = isset($_POST['edit']) ? $_POST['edit'] : '';
703 $adtypes = module_invoke_all('adapi', 'type');
704 if (arg(3) && is_array($adtypes) && in_array(arg(3), array_keys($adtypes))) {
705 $adtype = arg(3);
706
707 $node = array(
708 'uid' => $user->uid,
709 'name' => $user->name,
710 'type' => 'ad',
711 'adtype' => $adtype,
712 );
713
714 foreach (array('title', 'teaser', 'body') as $field) {
715 if ($_GET['edit'][$field]) {
716 $node[$field] = $_GET['edit'][$field];
717 }
718 }
719 $output = node_form($node);
720 drupal_set_title(t('Submit %name', array('%name' => $adtype)));
721 }
722 else {
723 $output = t('Choose from the following available advertisement types:');
724 $output .= '<dl>';
725 if (sizeof($adtypes) == 1) {
726 drupal_goto('node/add/ad/'. $adtypes[0]);
727 }
728 else if (sizeof($adtypes)) {
729 foreach ($adtypes as $type) {
730 $output .= '<dt>'. l(t('%type advertisement', array('%type' => $type)), "node/add/ad/$type") .'</dt>';
731 $output .= '<dd>'. implode("\n", module_invoke_all('help', 'node/add/ad#'. $type)) .'</dd>';
732 }
733 }
734 else {
735 $output .= '<dt>'. t('There are no advertisement types available.') .'</dt>';
736 }
737 $output .= '</dl>';
738 }
739
740 return $output;
741 }
742
743 /**
744 *
745 */
746 function ad_owners_overview($node) {
747 // Be sure the node owner is listed as an ad owner
748 if (!db_result(db_query('SELECT oid FROM {ad_owners} WHERE uid = %d AND aid = %d', $node->uid, $node->nid))) {
749 ad_owners_add($node->nid, $node->uid);
750 }
751
752 $header = array(
753 array('data' => t('Username'), 'field' => 'uid'),
754 array('data' => t('Options')),
755 );
756
757 $sql = "SELECT uid FROM {ad_owners} WHERE aid = $node->nid";
758 $sql .= tablesort_sql($header);
759 $result = pager_query($sql, 25);
760
761 $rows = array();
762 while ($ad = db_fetch_object($result)) {
763 $row = array();
764 $user = user_load(array('uid' => $ad->uid));
765 $row[] = $user->name;
766 $row[] = l(t('permissions'), "node/$node->nid/ad/$user->uid/permissions") .' | '. l(t('notifications'), "node/$node->nid/ad/$user->uid/notifications");
767 $rows[] = $row;
768 }
769
770 $output = theme('table', $header, $rows);
771 $output .= theme('pager', NULL, 25, 0);
772
773 $output .= '<p></p>';
774 $output .= t('Ad owners are not implemented yet.');
775
776 return $output;
777 }
778
779 /**
780 * Add an owner to an ad.
781 */
782 function ad_owners_add($aid, $uid) {
783 db_query('LOCK TABLES {ad_owners} WRITE');
784 if (!db_result(db_query('SELECT oid FROM {ad_owners} WHERE uid = %d AND aid = %d', $node->uid, $aid))) {
785 db_query('INSERT INTO {ad_owners} (aid, uid) VALUES(%d, %d)', $aid, $uid);
786 }
787 db_query('UNLOCK TABLES');
788 }
789
790 function ad_owner_permissions($aid, $uid) {
791 $output = t('Ad permissions are not implemented yet.');
792 return $output;
793 }
794
795 function ad_owner_notifications($aid, $uid) {
796 $output = t('Ad notifications are not implemented yet.');
797 return $output;
798 }
799
800 /**
801 * Create a unique host id for each ad owner, used when displaying ads remotely.
802 */
803 function ad_host_id_create($uid) {
804 if (!db_result(db_query('SELECT hostid FROM {ad_hosts} WHERE uid = %d', $uid))) {
805 db_query("INSERT INTO {ad_hosts} (uid, hostid) VALUES (%d, '%s')", $uid, md5($uid . time()));
806 }
807 }
808
809 /**
810 * Display a list of all ads.
811 * TODO: Cleanup output.
812 * TODO: Provide filters to help with managing large quantities of ads.
813 */
814 function ad_admin_list() {
815 _ad_check_install();
816
817 $header = array(
818 array('data' => t('Title'), 'field' => 'n.title'),
819 array('data' => t('Type'), 'field' => 'a.adtype'),
820 array('data' => t('Group'), 'field' => 'a.gid'),
821 array('data' => t('Status'), 'field' => 'a.adstatus'),
822 array('data' => t('Options')),
823 );
824
825 $sql = 'SELECT a.aid, a.gid, a.adstatus, a.adtype, n.title FROM {ads} a LEFT JOIN {node} n ON a.aid = n.nid';
826 $sql .= tablesort_sql($header);
827 $result = pager_query($sql, 50);
828
829 while ($ad = db_fetch_object($result)) {
830 $row = array();
831 $row[] = $ad->title;
832 $row[] = $ad->adtype;
833 $row[] = ad_group_name($ad->gid);
834 $row[] = $ad->adstatus;
835 $row[] = l(t('edit'), "node/$ad->aid/edit");
836 $rows[] = $row;
837 }
838
839 $output = theme('table', $header, $rows);
840 $output .= theme('pager', NULL, 50, 0);
841 return $output;
842 }
843
844 /**
845 * Display a form for the ad module settings.
846 */
847 function ad_admin_configure_settings($edit = array()) {
848 _ad_check_install();
849
850 $adserve = variable_get('adserve', '');
851 $form['configuration'] = array(
852 '#type' => 'fieldset',
853 '#title' => t('Status'),
854 );
855 $form['configuration']['adserve'] = array(
856 '#type' => 'markup',
857 '#value' => t('Using detected adserve script</em>: <em>%adserve</em>', array('%adserve' => ($adserve ? $adserve : t('not found')))),
858 );
859
860 $form['cache'] = array(
861 '#type' => 'fieldset',
862 '#title' => t('Cache settings')
863 );
864 $form['cache']['ad_cache'] = array(
865 '#type' => 'radios',
866 '#title' => t('Type'),
867 '#default_value' => variable_get('ad_cache', 'database'),
868 '#options' => array('database' => t('Database'), 'file' => t('File')),
869 '#description' => t('The cache is used to efficiently track how many times advertisements are displayed and clicked. File based caching will usually offer better performance, however, it can be difficult to configure and will not offer valid statistics if you are using multiple load balanced web servers.')
870 );
871
872 // Only enable the 'files' option if the file cache is enabled.
873 if (variable_get('ad_cache', 'database') == 'file') {
874 $attributes = array('enabled' => 'enabled');
875 $description = t('Please select the number of cache files the ad module should use. Select a smaller value for better accuracy when performaing automatic actions on advertisements at specified thresholds. Select a larger value for better performance.');
876 }
877 else {
878 $attributes = array('disabled' => 'disabled');
879 $description = t('This configuration setting is only relevant if the file cache is enabled.');
880 }
881
882 $form['cache']['ad_files'] = array(
883 '#type' => 'select',
884 '#title' => t('Number of cache files'),
885 '#default_value' => variable_get('ad_files', '1'),
886 '#options' => drupal_map_assoc(array(1, 3, 5, 10, 15)),
887 '#description' => $description,
888 '#attributes' => $attributes,
889 );
890
891 $form['save'] = array(
892 '#type' => 'submit',
893 '#value' => t('Save'),
894 );
895
896 return drupal_get_form('ad_settings_form', $form);
897 }
898
899 /**
900 * Prevent people from enabling the file cache until it is implemented.
901 */
902 function ad_settings_form_validate($form_id, $form_values) {
903 if ($form_values['ad_cache'] == 'file') {
904 form_set_error('ad_cache', t('Sorry, the file cache is not support by this version of the ad module. At this time you must use the database cache.'));
905 }
906 }
907
908 /**
909 * Save updated values from settings form.
910 */
911 function ad_settings_form_submit($form_id, $form_values) {
912 variable_set('ad_cache', $form_values['ad_cache']);
913 variable_set('ad_files', $form_values['ad_files']);
914 }
915
916 /**
917 * Return an array of all groups.
918 */
919 function ad_groups_list() {
920 static $groups = NULL;
921 if (is_array($groups)) {
922 return $groups;
923 }
924 $result = db_query('SELECT gid,name,description FROM {ad_groups}');
925 while ($group = db_fetch_object($result)) {
926 $groups[$group->gid] = "$group->name<br />$group->description";
927 }
928 return $groups;
929 }
930
931 function ad_group_name($gid) {
932 static $groups = NULL;
933 if (isset($groups[$gid])) {
934 return $groups[$gid];
935 }
936 $groups[$gid] = db_result(db_query('SELECT name FROM {ad_groups} WHERE gid = %d', $gid));
937 return $groups[$gid];
938 }
939
940 function ad_admin_groups_configure($gid = 0, $op = NULL) {
941 _ad_check_install();
942
943 $header = array(
944 array('data' => t('Name'), 'field' => 'name'),
945 array('data' => t('Description'), 'field' => 'description'),
946 array('data' => t('Options')),
947 );
948
949 $sql = 'SELECT gid, name, description FROM {ad_groups}';
950 $sql .= tablesort_sql($header);
951 $result = pager_query($sql, 15);
952
953 if (db_num_rows($result)) {
954 while ($group = db_fetch_object($result)) {
955 $row = array();
956 $row[] = $group->name;
957 $row[] = $group->description;
958 if ($group->gid == 1) {
959 $row[] = '';
960 }
961 else {
962 $row[] = l(t('edit'), "admin/ad/groups/$group->gid/edit");
963 }
964 $rows[] = $row;
965 }
966 }
967 else {
968 $rows[] = array(array('data' => t('No groups available.'), 'colspan' => 3));
969 }
970
971 $output = theme('table', $header, $rows);
972 $output .= theme('pager', NULL, 15, 0);
973
974 if ($gid && $op == 'edit') {
975 $edit = TRUE;
976 $group = db_fetch_object(db_query('SELECT gid, name, description FROM {ad_groups} WHERE gid = %d', $gid));
977 }
978 else {
979 $edit = FALSE;
980 }
981
982 $form['group'] = array(
983 '#type' => 'fieldset',
984 '#title' => $edit ? t('Save group') : t('Create new group'),
985 );
986 $form['group']['name'] = array(
987 '#type' => 'textfield',
988 '#title' => t('Group name'),
989 '#maxlength' => 255,
990 '#default_value' => $group->name,
991 '#required' => TRUE,
992 '#description' => t('Specify a name for your new ad group. The name can only contain numbers and letters. It can not contain spaces or punctuation.'),
993 );
994 $form['group']['description'] = array(
995 '#type' => 'textarea',
996 '#title' => t('Group desciption'),
997 '#default_value' => $group->description,
998 '#required' => TRUE,
999 '#description' => t('Enter a description of your new ad group. This description is displayed when new advertisements are created.'),
1000 );
1001 $form['group']['gid'] = array(
1002 '#type' => 'value',
1003 '#value' => $gid,
1004 );
1005 $form['group']['actions'] = array(
1006 '#prefix' => '<div class="container-inline">',
1007 '#suffix' => '</div>',
1008 );
1009 $form['group']['actions']['submit'] = array(
1010 '#type' => 'submit',
1011 '#value' => $edit ? t('Save group') : t('Create group'),
1012 );
1013 if ($edit) {
1014 $form['group']['actions']['delete'] = array(
1015 '#type' => 'submit',
1016 '#value' => t('Delete group'),
1017 );
1018 $form['group']['actions']['cancel'] = array(
1019 '#type' => 'markup',
1020 '#value' => l(t('Cancel'), 'admin/ad/groups'),
1021 );
1022 }
1023 $output .= drupal_get_form('ad_admin_group_create', $form);
1024
1025 return $output;
1026 }
1027
1028 function ad_admin_group_create_validate($form_id, $form_values) {
1029 </