annotate ad.module @ 7:6aeff3329e01 ad

maj module ad 2.1rc1
author piotre
date Mon, 20 Jul 2009 13:54:40 +0000
parents 416ea999ed76
children 32c1a7d9e1fa
rev   line source
pierre@0 1 <?php
piotre@7 2 // $Id: ad.module,v 1.2.2.29.2.83.2.16.2.23 2009/07/06 21:47:49 jeremy Exp $
pierre@0 3
pierre@0 4 /**
pierre@0 5 * @file
pierre@0 6 * An advertising system for Drupal powered websites.
pierre@0 7 *
pierre@0 8 * Copyright (c) 2005-2009.
pierre@0 9 * Jeremy Andrews <jeremy@tag1consulting.com>.
pierre@0 10 */
pierre@0 11
pierre@1 12 require_once('ad_token.inc');
pierre@0 13
pierre@0 14 /**
pierre@0 15 * Implementation of hook_theme().
pierre@0 16 */
pierre@0 17 function ad_theme() {
pierre@0 18 return array(
pierre@0 19 'ad_display' => array(
pierre@0 20 'file' => 'ad.module',
pierre@0 21 'arguments' => array(
pierre@0 22 'group' => NULL,
pierre@0 23 'display' => NULL,
pierre@0 24 'method' => 'javascript',
pierre@0 25 ),
pierre@0 26 ),
pierre@0 27 'ad_status_display' => array(
pierre@0 28 'file' => 'ad.module',
pierre@0 29 'arguments' => array(
pierre@0 30 'node' => NULL,
pierre@0 31 ),
pierre@0 32 ),
pierre@0 33 'ad_statistics_display' => array(
pierre@0 34 'file' => 'ad.pages.inc',
pierre@0 35 'arguments' => array(
pierre@0 36 'statistics' => NULL,
pierre@0 37 ),
pierre@0 38 ),
pierre@0 39 'node_ad' => array(
pierre@0 40 'file' => 'ad.pages.inc',
pierre@0 41 'arguments' => array(
pierre@0 42 'node' => NULL,
pierre@0 43 'yield_form' => TRUE,
pierre@0 44 ),
pierre@0 45 ),
pierre@0 46 'ad_admin_ads' => array(
pierre@0 47 'file' => 'ad.admin.inc',
pierre@0 48 'arguments' => array(
pierre@0 49 'form' => NULL,
pierre@0 50 ),
pierre@0 51 ),
pierre@0 52 'ad_filters' => array(
pierre@0 53 'file' => 'ad.admin.inc',
pierre@0 54 'arguments' => array(
pierre@0 55 'form' => NULL,
pierre@0 56 ),
pierre@0 57 ),
pierre@0 58 'ad_filter_form' => array(
pierre@0 59 'file' => 'ad.admin.inc',
pierre@0 60 'arguments' => array(
pierre@0 61 'form' => NULL,
pierre@0 62 ),
pierre@0 63 ),
pierre@0 64 );
pierre@0 65 };
pierre@0 66
pierre@0 67 /**
pierre@0 68 * Use this function to display ads from a specified group.
pierre@0 69 *
pierre@0 70 * @param $group
pierre@0 71 * The ad group tid to display ads from.
pierre@0 72 * @param $quantity
pierre@0 73 * Optionally specify the number of unique ads to display.
pierre@0 74 * @param $options
pierre@0 75 * Any number of options from this list: hostid, nids.
pierre@0 76 */
pierre@0 77 function ad($group = FALSE, $quantity = 1, $options = array()) {
pierre@0 78 global $base_url;
pierre@0 79
pierre@0 80 $adserve = variable_get('adserve', '');
pierre@0 81 $adserveinc = variable_get('adserveinc', '');
pierre@0 82 if (empty($adserve) || empty($adserveinc)) {
pierre@0 83 // This is probably the first time ad() has been called.
pierre@0 84 _ad_check_installation();
pierre@0 85 $adserve = variable_get('adserve', '');
pierre@0 86 $adserveinc = variable_get('adserveinc', '');
pierre@0 87 }
pierre@0 88 if (!file_exists($adserve) || !file_exists($adserveinc)) {
pierre@0 89 drupal_set_message(t('Ads cannot be displayed. The ad module is <a href="@misconfigured">misconfigured</a>, failed to locate the required <em>serve.php</em> ond/or <em>adserve.inc</em> file.', array('@misconfigured' => url('admin/content/ad/configure'))), 'error');
pierre@0 90 _ad_check_installation();
pierre@0 91 return (t('The ad module is <a href="@misconfigured">misconfigured</a>.', array('@misconfigured' => url('admin/content/ad/configure'))));
pierre@0 92 }
pierre@0 93
pierre@0 94 // Be sure a display method has been chosen.
pierre@0 95 if (!isset($options['ad_display'])) {
pierre@0 96 $options['ad_display'] = variable_get('ad_display', 'javascript');
pierre@0 97 }
pierre@0 98 $options['quantity'] = isset($quantity) ? $quantity : 1;
pierre@0 99 if (!isset($options['tids'])) {
pierre@0 100 $options['tids'] = $group;
pierre@0 101 }
pierre@0 102 $options['cache'] = variable_get('ad_cache', 'none');
pierre@0 103
pierre@0 104 switch ($options['ad_display']) {
pierre@0 105 case 'raw':
pierre@0 106 require_once(drupal_get_path('module', 'ad') .'/adserve.inc');
pierre@1 107 require_once(drupal_get_path('module', 'ad') .'/adcache.inc');
pierre@0 108 $output = adserve_ad($options);
pierre@0 109 break;
pierre@0 110 case 'iframe':
pierre@0 111 case 'jquery':
pierre@0 112 $query['m'] = $options['ad_display'];
pierre@0 113 // Fall through...
pierre@0 114 case 'javascript':
pierre@0 115 default:
pierre@0 116 $query['q'] = $quantity;
pierre@0 117 if (isset($options['hostid'])) {
pierre@0 118 $query['k'] = $options['hostid'];
pierre@0 119 }
pierre@0 120 // Allow external cache files to define additional display variables.
pierre@0 121 if ($options['cache'] != 'none') {
pierre@0 122 $query['c'] = $options['cache'];
pierre@0 123 $cache_variables = module_invoke('ad_cache_'. $options['cache'], 'adcacheapi', 'display_variables', array());
pierre@0 124 if (is_array($cache_variables)) {
pierre@0 125 foreach ($cache_variables as $key => $value) {
pierre@0 126 $query[$key] = $value;
pierre@0 127 }
pierre@0 128 }
pierre@0 129 }
pierre@0 130 // Allow ad_type modules to define additional display variables.
pierre@0 131 $type_variables = module_invoke_all('adapi', 'display_variables', array());
pierre@0 132 if (is_array($type_variables)) {
pierre@0 133 foreach ($type_variables as $key => $value) {
pierre@0 134 $query[$key] = $value;
pierre@0 135 }
pierre@0 136 }
pierre@0 137 if (isset($options['nids'])) {
pierre@0 138 // Choose ads from the provided list of node Id's.
pierre@0 139 $nids = $options['nids'];
pierre@0 140 $query['n'] = $nids;
pierre@0 141 $group = "nids-$nids";
pierre@0 142 }
pierre@0 143 else if (isset($options['tids'])) {
pierre@0 144 // Choose ads from the provided list of taxonomy terms.
pierre@0 145 $tids = $options['tids'];
pierre@0 146 $query['t'] = $tids;
pierre@0 147 $group = "tids-$tids";
pierre@0 148 }
pierre@0 149 else {
pierre@0 150 // Choose ads from the specified group.
pierre@0 151 $query['t'] = $group;
pierre@0 152 $options['tids'] = $group;
pierre@0 153 }
pierre@1 154 if (isset($options['url'])) {
pierre@1 155 $query['u'] = $options['url'];
pierre@1 156 }
pierre@1 157 else {
pierre@1 158 $query['u'] = $_GET['q'];
pierre@1 159 }
piotre@7 160 $src = url($base_url .'/'. $adserve, array('query' => $query));
pierre@0 161 if ($options['ad_display'] == 'iframe') {
pierre@0 162 // TODO: We need to know the IFrame size before it is displayed. This
pierre@0 163 // limits the flexibility of what can be displayed in these frames.
pierre@0 164 // For now we'll have a global value, later we'll add per-group
pierre@0 165 // over-rides.
pierre@0 166 $append = 'frameborder="'. variable_get('ad_iframe_frameborder', 0) .'" ';
pierre@0 167 $append .= 'scrolling="'. variable_get('ad_iframe_scroll', 'auto') .'" ';
pierre@0 168 $append .= 'name="'. $group .'" ';
pierre@0 169 if ($height = variable_get('ad_iframe_height', '')) {
pierre@0 170 $append .= 'height="'. $height .'" ';
pierre@0 171 }
pierre@0 172 if ($width = variable_get('ad_iframe_width', '')) {
pierre@0 173 $append .= 'width="'. $width .'" ';
pierre@0 174 }
piotre@7 175 $output = '<iframe src="'. htmlentities($src) ."\" $append></iframe>";
pierre@0 176 }
pierre@0 177 else if ($options['ad_display'] == 'jquery') {
pierre@0 178 // The theme function uses this to generate a CSS id for jQuery to use.
pierre@0 179 $output = $src;
pierre@0 180 }
pierre@0 181 else {
piotre@7 182 $output = "<script type='text/javascript' src='". htmlentities($src) ."'></script>";
pierre@0 183 }
pierre@0 184 break;
pierre@0 185 }
pierre@0 186
pierre@0 187 if (user_access('show advertisements')) {
pierre@1 188 if (isset($options['div']) && $options['div'] !== FALSE) {
pierre@1 189 return theme('ad_display', $group, $output, $options['ad_display']);
pierre@1 190 }
pierre@1 191 else {
pierre@1 192 return theme('ad_display', $group, $output, 'raw');
pierre@1 193 }
pierre@0 194 }
pierre@0 195 else {
pierre@0 196 return theme('ad_display', 'none', "<!-- Enable 'show advertisements' permission if you wish to display ads here. -->");
pierre@0 197 }
pierre@0 198 }
pierre@0 199
pierre@0 200 /**
pierre@0 201 * Function to display the actual advertisement to the screen. Wrap it in a
pierre@0 202 * theme function to make it possible to customize in your own theme.
pierre@0 203 */
pierre@0 204 function theme_ad_display($group, $display, $method = 'javascript') {
pierre@1 205 static $id = -1;
pierre@1 206
pierre@1 207 // Increment counter for displaying multiple advertisements on the page.
pierre@1 208 $id++;
pierre@0 209
pierre@0 210 // The naming convention for the id attribute doesn't allow commas.
pierre@0 211 $group = preg_replace('/[,]/', '+', $group);
pierre@0 212
pierre@0 213 if ($method == 'jquery') {
pierre@1 214 return "\n<div class=\"advertisement group-$group\" id=\"group-id-$id\">\n <script type=\"text/javascript\">\n//<![CDATA[\n $(document).ready(function(){ jQuery(\"div#group-id-$id\").load(\"$display\"); });\n //]]>\n </script>\n</div>\n";
pierre@1 215 }
pierre@1 216 else if ($method == 'raw') {
pierre@1 217 return $display;
pierre@0 218 }
pierre@0 219 else {
pierre@0 220 return "\n<div class=\"advertisement group-$group\" id=\"group-id-$group\">$display</div>\n";
pierre@0 221 }
pierre@0 222 }
pierre@0 223
pierre@0 224 /**
pierre@0 225 * Update click counter then redirect host to ad's target URL.
pierre@0 226 */
pierre@1 227 function ad_redirect($aid, $group = NULL) {
pierre@0 228 global $user;
pierre@1 229 $hostid = isset($_GET['hostid']) ? $_GET['hostid'] : '';
pierre@1 230 $extra = isset($_GET['extra']) ? $_GET['extra'] : '';
pierre@0 231 if (function_exists('click_filter_status')) {
pierre@0 232 $status = click_filter_status($aid, $hostid);
pierre@0 233 if ($status == CLICK_VALID) {
pierre@0 234 ad_statistics_increment($aid, 'click', $group, $hostid);
pierre@0 235 }
pierre@0 236 }
pierre@0 237 else {
pierre@0 238 // We're not filtering clicks, so all clicks are valid.
pierre@0 239 ad_statistics_increment($aid, 'click', $group, $hostid);
pierre@0 240 $status = 0;
pierre@0 241 }
pierre@0 242 // Allow source url to be passed in.
pierre@0 243 $url = isset($_GET['u']) ? $_GET['u'] : '';
pierre@0 244 if (!isset($url) || !valid_url($url)) {
pierre@0 245 $url = referer_uri();
pierre@0 246 }
pierre@1 247 db_query("INSERT INTO {ad_clicks} (aid, uid, status, hostname, user_agent, adgroup, extra, hostid, url, timestamp) VALUES (%d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', %d)", $aid, $user->uid, $status, ip_address(), $_SERVER['HTTP_USER_AGENT'], $group, $extra, $hostid, $url, time());
pierre@0 248
pierre@0 249 // Determine where we're supposed to redirect the user.
pierre@0 250 $adtype = db_result(db_query('SELECT adtype FROM {ads} WHERE aid = %d', $aid));
pierre@0 251
pierre@0 252 $node->nid = $node->aid = $aid;
pierre@0 253 $node->hostid = $hostid;
pierre@0 254 $url = module_invoke('ad_'. $adtype, 'adapi', 'redirect', $node);
pierre@0 255 if (isset($url)) {
pierre@0 256 watchdog('ad', 'Clicked %type ad aid %aid hostid %hostid.', array('%type' => $adtype, '%aid' => $aid, '%hostid' => $hostid));
pierre@0 257 header('Location: '. $url);
pierre@0 258 }
pierre@0 259 else {
pierre@0 260 watchdog('ad', 'Ad redirection failed for aid %aid hostid %hostid, failed to load destination URL. ', array('%aid' => $aid, '%hostid' => $hostid));
pierre@0 261 drupal_goto('');
pierre@0 262 }
pierre@0 263 }
pierre@0 264
pierre@0 265 /**
pierre@0 266 * Ad API Helper Function:
pierre@0 267 * Append all necessary attributes to <a> tags.
pierre@0 268 */
pierre@0 269 function ad_link_attributes() {
pierre@0 270 return array_merge(ad_link_target(TRUE), ad_link_nofollow(TRUE));
pierre@0 271 }
pierre@0 272
pierre@0 273 /**
pierre@0 274 * Ad API Helper Function:
pierre@0 275 * Provide XHTML-strict-compatible target window onclick-handlers based on
pierre@0 276 * global configuration.
pierre@0 277 */
pierre@0 278 function ad_link_target() {
pierre@0 279 switch (variable_get('ad_link_target', '_self')) {
pierre@0 280 case '_blank':
pierre@0 281 $target = array('onclick' => 'window.open(this.href); return false;');
pierre@0 282 break;
pierre@0 283 case '_parent':
pierre@0 284 $target = array('onclick' => 'window.parent.location = this.href; return false;');
pierre@0 285 break;
pierre@0 286 case '_top':
pierre@0 287 $target = array('onclick' => 'window.top.location = this.href; return false;');
pierre@0 288 break;
pierre@0 289 default:
pierre@0 290 $target = array();
pierre@0 291 break;
pierre@0 292 }
pierre@0 293 return $target;
pierre@0 294 }
pierre@0 295
pierre@0 296 /**
pierre@1 297 * Force the cache to be flushed.
pierre@1 298 */
pierre@1 299 function ad_rebuild_cache($verbose = FALSE) {
pierre@1 300 $cache = variable_get('ad_cache', 'none');
pierre@1 301 $build = "ad_cache_{$cache}_build";
pierre@1 302 if (function_exists($build)) {
pierre@1 303 if ($verbose) {
pierre@1 304 drupal_set_message('Rebuilding ad cache.');
pierre@1 305 }
pierre@1 306 $build();
pierre@1 307 }
pierre@1 308 }
pierre@1 309
pierre@1 310 /**
pierre@0 311 * Ad API Helper Function:
pierre@0 312 * Append rel="nofollow" if globally enabled.
pierre@0 313 */
pierre@0 314 function ad_link_nofollow() {
pierre@0 315 if (variable_get('ad_link_nofollow', 0)) {
pierre@0 316 $nofollow = array('rel' => 'nofollow');
pierre@0 317 }
pierre@0 318 else {
pierre@0 319 $nofollow = array();
pierre@0 320 }
pierre@0 321 return $nofollow;
pierre@0 322 }
pierre@0 323
pierre@0 324 /**
pierre@0 325 * Increment action counter.
pierre@0 326 */
pierre@0 327 function ad_statistics_increment($aid, $action, $group = NULL, $hostid = NULL) {
pierre@0 328 // Update action statistics.
pierre@0 329 db_query("UPDATE {ad_statistics} SET count = count + 1 WHERE date = %d AND aid = %d AND action = '%s' AND adgroup = '%s' AND hostid = '%s'", date('YmdH'), $aid, $action, $group, $hostid);
pierre@0 330 // If column doesn't already exist, we need to add it.
pierre@0 331 if (!db_affected_rows()) {
pierre@0 332 db_query("INSERT INTO {ad_statistics} (aid, adgroup, hostid, date, action, count) VALUES(%d, '%s', '%s', %d, '%s', 1)", $aid, $group, $hostid, date('YmdH'), $action);
pierre@0 333 // If another process already added this row our INSERT will fail, if so we
pierre@0 334 // still need to increment it so we don't loose an action.
pierre@0 335 if (!db_affected_rows()) {
pierre@0 336 db_query("UPDATE {ad_statistics} SET count = count + 1 WHERE date = %d AND aid = %d AND action = '%s' AND adgroup = '%s' AND hostid = '%s'", date('YmdH'), $aid, $action, $group, $hostid);
pierre@0 337 }
pierre@0 338 }
pierre@0 339
pierre@0 340 $event = array('aid' => $aid, 'action' => $action, 'hostid' => $hostid);
pierre@0 341 module_invoke_all('adapi', 'statistics_increment', $event);
pierre@0 342 }
pierre@0 343
pierre@1 344 /**
pierre@1 345 * Return an array with all status values user has permission to set.
pierre@1 346 * A user with 'administer advertisements' permission can update any status.
pierre@1 347 */
pierre@1 348 function ad_status_array($aid = 0, $status = NULL) {
pierre@1 349 $permissions = array();
pierre@1 350 // mark status as pending
pierre@1 351 if (user_access('administer advertisements') ||
pierre@1 352 $status == 'pending' || $status == NULL ||
pierre@1 353 ad_permission($aid, 'set status as pending')) {
pierre@1 354 $permissions['pending'] = t('This advertisement is currently waiting for administrative approval.');
pierre@0 355 }
pierre@1 356 // mark status from pending to approved
pierre@1 357 if (user_access('administer advertisements') ||
pierre@1 358 $status == 'approved' ||
pierre@1 359 ($status == 'pending' &&
pierre@1 360 ad_permission($aid, 'set status from pending to approved'))) {
pierre@1 361 $permissions['approved'] = t('This advertisement has been approved and is currently waiting to be activated.');
pierre@0 362 }
pierre@1 363 // mark status as active (from pending, approved, or offline)
pierre@1 364 if (user_access('administer advertisements') ||
pierre@1 365 $status == 'active' ||
pierre@1 366 ($status == 'approved' &&
pierre@1 367 ad_permission($aid, 'set status from approved to active')) ||
pierre@1 368 ($status == 'offline' &&
pierre@1 369 ad_permission($aid, 'set status from offline to active'))) {
pierre@1 370 $permissions['active'] = t('This advertisement is actively being displayed.');
pierre@1 371 }
pierre@1 372 // mark status as offline (from pending, approved, or active)
pierre@1 373 if (user_access('administer advertisements') ||
pierre@1 374 $status == 'offline' ||
pierre@1 375 ($status == 'approved' &&
pierre@1 376 ad_permission($aid, 'set status from approved to offline')) ||
pierre@1 377 ($status == 'active' &&
pierre@1 378 ad_permission($aid, 'set status from active to offline'))) {
pierre@1 379 $permissions['offline'] = t('This advertisement has been temporarily disabled by its owner and is not currently being displayed.');
pierre@1 380 }
pierre@1 381 // mark status as expired (from active or offline)
pierre@1 382 if (user_access('administer advertisements') ||
pierre@1 383 $status == 'expired' ||
pierre@1 384 ($status == 'active' &&
pierre@1 385 ad_permission($aid, 'set status from active to expired')) ||
pierre@1 386 ($status == 'offline' &&
pierre@1 387 ad_permission($aid, 'set status from offline to expired'))) {
pierre@1 388 $permissions['expired'] = t('This advertisement has expired and is no longer being displayed.');
pierre@1 389 }
pierre@1 390 // mark status as denied (from pending or any)
pierre@1 391 if (user_access('administer advertisements') ||
pierre@1 392 $status == 'denied' ||
pierre@1 393 ($status == 'pending' &&
pierre@1 394 ad_permission($aid, 'set status from pending to denied')) ||
pierre@1 395 ad_permission($aid, 'set status as denied')) {
pierre@1 396 $permissions['denied'] = t('This advertisement was refused by the site administrator and will not be displayed.');
pierre@1 397 }
pierre@1 398 return $permissions;
pierre@0 399 }
pierre@0 400
pierre@0 401 /**
pierre@0 402 * Display the status of the currently viewed ad.
pierre@0 403 */
pierre@0 404 function theme_ad_status_display($node) {
pierre@1 405 if (isset($node->adstatus)) {
pierre@1 406 $status_array = ad_status_array($node->nid, $node->adstatus);
pierre@1 407 $output = '<div class="adstatus">';
pierre@1 408 $output .= '<p>'. t($status_array[$node->adstatus]) .'</p>';
pierre@1 409 switch ($node->adstatus) {
pierre@1 410 case 'approved':
pierre@1 411 if ($node->autoactivate) {
pierre@1 412 $output .= '<p>'. t('This advertisement will be automatically activated on %timestamp, in %time.', array('%timestamp' => format_date($node->autoactivate, 'large'), '%time' => format_interval($node->autoactivate - time()))) .'</p>';
pierre@1 413 }
pierre@1 414 break;
pierre@1 415 case 'active':
pierre@1 416 $activated = db_result(db_query("SELECT activated FROM {ads} WHERE aid = %d", $node->nid));
pierre@1 417 if ($activated) {
pierre@1 418 $output .= '<p>'. t('This advertisement has been active since %date.', array('%date' => format_date($activated, 'large'))) .'</p>';
pierre@1 419 }
pierre@1 420 if ($node->autoexpire) {
pierre@1 421 $output .= '<p>'. t('This advertisement will expire on %timestamp, in %time.', array('%timestamp' => format_date($node->autoexpire, 'large'), '%time' => format_interval($node->autoexpire - time()))) .'</p>';
pierre@1 422 }
pierre@1 423 if ($node->maxviews) {
pierre@1 424 $views = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'view' AND date >= %d", $node->nid, date('YmdH', $node->activated)));
pierre@1 425 $output .= '<p>'. t('This advertisement will expire after %left more impressions.', array('%left' => $node->maxviews - $views)) .'</p>';
pierre@1 426 }
pierre@1 427 if ($node->maxclicks) {
pierre@1 428 $clicks = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'click' AND date >= %d", $node->nid, date('YmdH', $node->activated)));
pierre@1 429 $output .= '<p>'. t('This advertisement will expire after %left more clicks.', array('%left' => $node->maxclicks - $clicks)) .'</p>';
pierre@1 430 }
pierre@1 431 break;
pierre@1 432 case 'expired':
pierre@1 433 $expired = db_result(db_query("SELECT expired FROM {ads} WHERE aid = %d", $node->nid));
pierre@1 434 if ($expired) {
pierre@1 435 $output .= '<p>'. t('This advertisement expired %date.', array('%date' => format_date($expired, 'large'))) .'</p>';
pierre@1 436 }
pierre@1 437 break;
pierre@1 438 }
pierre@1 439 $output .= '</div>';
pierre@1 440 return theme('box', t('Status'), $output);
pierre@0 441 }
pierre@0 442 }
pierre@0 443
pierre@0 444 /**
piotre@7 445 * Implementation of hook_init.
piotre@7 446 */
piotre@7 447 function ad_init() {
piotre@7 448 // We do this in the init hook so that it doesn't get skipped when block
piotre@7 449 // caching is enabled.
piotre@7 450 $method = variable_get('ad_display', 'javascript');
piotre@7 451 if ($method == 'jquery') {
piotre@7 452 drupal_add_js('misc/jquery.js', 'core');
piotre@7 453 }
piotre@7 454 }
piotre@7 455
piotre@7 456 /**
pierre@0 457 * Implementation of hook_help().
pierre@0 458 */
pierre@0 459 function ad_help($path, $arg) {
pierre@0 460 $output = '';
pierre@0 461 switch ($path) {
pierre@0 462 case 'admin/help#ad':
pierre@0 463 $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>';
pierre@0 464 break;
pierre@0 465 case 'node/add/ad':
pierre@0 466 $output = '<p>'. t('Advertisements can be randomly displayed to visitors of your website.') .'</p>';
pierre@0 467 break;
pierre@0 468 }
pierre@0 469 return $output;
pierre@0 470 }
pierre@0 471
pierre@0 472 /**
pierre@0 473 * Implementation of hook_cron().
pierre@0 474 */
pierre@0 475 function ad_cron() {
pierre@0 476 if (time() - variable_get('ad_cron_timestamp', 0) >= 60) {
pierre@0 477 // Locate ads that need to be activated or expired.
pierre@0 478 $result = db_query('SELECT aid, adstatus, adtype, autoactivate, autoactivated, autoexpire, autoexpired FROM {ads} WHERE autoactivate <> 0 OR autoexpire <> 0');
pierre@0 479 while ($ad = db_fetch_object($result)) {
pierre@0 480 switch ($ad->adstatus) {
pierre@0 481 case 'approved': {
pierre@0 482 // See if this ad is ready to be activated.
pierre@0 483 if ($ad->autoactivate && $ad->autoactivate <= time()) {
pierre@0 484 $node = node_load($ad->aid);
pierre@0 485
pierre@0 486 // Activate the ad.
pierre@0 487 db_query("UPDATE {ads} SET adstatus = 'active', autoactivate = 0, autoactivated = %d, activated = %d WHERE aid = %d", time(), time(), $ad->aid);
pierre@0 488 ad_statistics_increment($ad->aid, 'autoactivated');
pierre@0 489 ad_statistics_increment($ad->aid, 'active');
pierre@0 490
pierre@0 491 watchdog('ad', 'Automatically activated ad %title with nid %nid.', array('%title' => $node->title, '%nid' => $node->nid));
pierre@0 492
pierre@0 493 // Allow modules to do special processing to automatically
pierre@0 494 // activated advertisements.
pierre@0 495 module_invoke('ad_'. $ad->adtype, 'adapi', 'autoactivate', $node);
pierre@0 496 }
pierre@0 497 else if (!$ad->autoactivate) {
pierre@0 498 // Once daily warn that there's an ad stuck in approved state.
pierre@0 499 if (time() - variable_get("ad_autoactivate_warning_$ad->aid", 0) >= 8600) {
pierre@0 500 watchdog('ad', 'Warning: ad %title with nid %nid in approved state has no autoactivate date set.', array('%title' => $node->title, '%nid' => $node->nid));
pierre@0 501 variable_set("ad_autoactivate_warning_$ad->aid", time());
pierre@0 502 }
pierre@0 503 }
pierre@0 504 break;
pierre@0 505 }
pierre@0 506 case 'active': {
pierre@0 507 // See if this ad is ready to be activated.
pierre@0 508 if ($ad->autoexpire && $ad->autoexpire <= time()) {
pierre@0 509 $node = node_load($ad->aid);
pierre@0 510
pierre@0 511 // Expire the ad.
pierre@0 512 db_query("UPDATE {ads} SET adstatus = 'expired', autoexpire = 0, autoexpired = %d, expired = %d WHERE aid = %d", time(), time(), $ad->aid);
pierre@0 513 ad_statistics_increment($ad->aid, 'autoexpired');
pierre@0 514 ad_statistics_increment($ad->aid, 'expired');
pierre@0 515
pierre@0 516 watchdog('ad', 'Automatically expired ad %title with nid %nid.', array('%title' => $node->title, '%nid' => $node->nid));
pierre@0 517
pierre@0 518 // Allow modules to do special processing to automatically
pierre@0 519 // activated advertisements.
pierre@0 520 module_invoke('ad_'. $ad->adtype, 'adapi', 'autoexpire', $node);
pierre@0 521 }
pierre@0 522 else if (!$ad->autoexpire) {
pierre@0 523 // Ad is already activated, but has autoactivate timestamp set.
pierre@0 524 db_query("UPDATE {ads} SET autoactivate = 0 WHERE aid = %d", $ad->aid);
pierre@0 525 }
pierre@0 526 break;
pierre@0 527 }
pierre@0 528 default:
pierre@0 529 $node = node_load($ad->aid);
pierre@0 530 db_query('UPDATE {ads} SET autoactivate = 0, autoexpire = 0 WHERE aid = %d', $ad->aid);
pierre@0 531 watchdog('ad', 'Warning: reset %type timestamp on advertisement %title with nid %nid because it is in %state state.', array('%title' => $node->title, '%nid' => $node->nid, '%type' => $ad->autoactivate ? 'autoactivate' : 'autoexpire', '%state' => $ad->adstatus));
pierre@0 532 }
pierre@0 533 }
pierre@0 534 variable_set('ad_cron_timestamp', time());
pierre@0 535 }
pierre@0 536 }
pierre@0 537
pierre@0 538 /**
pierre@0 539 * Implementation of hook_perm().
pierre@0 540 */
pierre@0 541 function ad_perm() {
pierre@0 542 return array('administer advertisements',
pierre@0 543 'create advertisements',
pierre@0 544 'edit own advertisements',
pierre@1 545 'edit any advertisement',
pierre@1 546 'delete own advertisements',
pierre@1 547 'delete any advertisement',
pierre@0 548 'show advertisements');
pierre@0 549 }
pierre@0 550
pierre@0 551 /**
pierre@0 552 * Implementation of hook_node_info().
pierre@0 553 */
pierre@0 554 function ad_node_info() {
pierre@0 555 $items['ad'] = array(
pierre@0 556 'name' => t('Advertisement'),
pierre@0 557 'module' => 'ad',
pierre@0 558 'description' => t('Advertisements can be randomly displayed to visitors of your website.'),
pierre@0 559 );
pierre@0 560 return $items;
pierre@0 561 }
pierre@0 562
pierre@0 563 /**
pierre@0 564 * Implementation of hook_access().
pierre@0 565 */
pierre@0 566 function ad_access($op, $node, $account) {
pierre@1 567 switch ($op) {
pierre@1 568 case 'create':
pierre@1 569 return (user_access('create advertisements', $account) || user_access('administer advertisements'));
pierre@1 570 case 'update':
pierre@1 571 return (user_access('edit any advertisement', $account) || (user_access('edit own advertisements', $account) && is_ad_owner($node->nid)) || user_access('administer advertisements', $account));
pierre@1 572 case 'delete':
pierre@1 573 return (user_access('delete any advertisement', $account) || (user_access('delete own advertisements', $account) && is_ad_owner($node->nid)) || user_access('administer advertisements', $account));
pierre@1 574 case 'view':
pierre@1 575 return (user_access('show advertisements', $account) || user_access('administer advertisements', $account));
pierre@0 576 }
pierre@0 577 }
pierre@0 578
pierre@0 579 /**
pierre@0 580 * Implementation of hook_form().
pierre@0 581 */
pierre@1 582 function ad_form(&$node, &$form_state) {
pierre@0 583 $form = array();
pierre@0 584
pierre@1 585 // When form_state is not empty, we should rather use it's values
pierre@1 586 // to not loose data if validation fails.
pierre@1 587 if (!empty($form_state['values'])) {
pierre@1 588 $node = (object)$form_state['values'];
pierre@0 589 }
pierre@0 590
pierre@0 591 $form['aid'] = array(
pierre@0 592 '#type' => 'value',
pierre@0 593 '#value' => isset($node->nid) ? $node->nid : 0,
pierre@0 594 );
pierre@0 595
pierre@0 596 $form['title'] = array(
pierre@0 597 '#type' => 'textfield',
pierre@0 598 '#title' => t('Title'),
pierre@0 599 '#required' => TRUE,
pierre@0 600 '#default_value' => isset($node->title) ? $node->title : '',
pierre@0 601 );
pierre@0 602 $form['body_filter']['body'] = array(
pierre@0 603 '#type' => 'textarea',
pierre@0 604 '#title' => t('Description'),
pierre@0 605 '#default_value' => isset($node->body) ? $node->body : '',
pierre@0 606 '#rows' => 3
pierre@0 607 );
pierre@0 608 $form['body_filter']['format'] = filter_form($node->format);
pierre@0 609
pierre@0 610 // determine the current ad type
pierre@0 611 if (!isset($adtype)) {
pierre@0 612 $adtypes = ad_get_types();
pierre@0 613 if (sizeof($adtypes) == 1) {
pierre@0 614 $adtype = key($adtypes);
pierre@0 615 }
pierre@0 616 else if (!sizeof($adtypes)) {
pierre@0 617 drupal_set_message(t('At least one ad type module must be enabled before you can create advertisements. For example, try <a href="!url">enabling</a> the ad_text or ad_image module.', array('!url' => url('admin/build/modules'))), 'error');
pierre@0 618 }
pierre@0 619 }
pierre@0 620
pierre@1 621 // display ad type switch
pierre@1 622 if (!isset($node->adtype) || isset($node->adtype_select)) {
pierre@1 623 $adtypes = array(0 => '---');
pierre@1 624 $adtypes += ad_get_types('name');
pierre@1 625 $form['select'] = array(
pierre@1 626 '#type' => 'fieldset',
pierre@1 627 '#title' => t('Select Ad type'),
pierre@1 628 '#prefix' => '<div class="container-inline">',
pierre@1 629 '#suffix' => '</div>',
pierre@1 630 '#weight' => 3,
pierre@1 631 );
pierre@1 632 $form['select']['adtype_select'] = array(
pierre@1 633 '#type' => 'select',
pierre@1 634 '#required' => TRUE,
pierre@1 635 '#options' => $adtypes,
pierre@1 636 '#default_value' => isset($node->adtype_select) ? $node->adtype_select : '',
pierre@1 637 );
pierre@1 638 $form['select']['adtype_submit'] = array(
pierre@1 639 '#type' => 'submit',
pierre@1 640 '#value' => t('Select'),
pierre@1 641 '#validate' => array('ad_select_adtype'),
pierre@1 642 '#ahah' => array(
pierre@1 643 'path' => 'node/add/ad/ahah',
pierre@1 644 'wrapper' => 'adtype-ahah-wrapper',
pierre@1 645 ),
pierre@1 646 );
pierre@1 647 }
pierre@0 648 // display type-specific options
pierre@1 649 if (isset($node->adtype) && $node->adtype) {
pierre@1 650 if (isset($node->adtype_select) && $node->adtype_select && ($node->adtype_select != $node->adtype)) {
pierre@1 651 $node->adtype = $node->adtype_select;
pierre@0 652 }
pierre@1 653 ad_form_add_adtype_elements($form, $node->adtype, $node);
pierre@1 654 // add ahah wrapper
pierre@1 655 $form['adtype_elements']['#prefix'] = '<div id="adtype-ahah-wrapper">';
pierre@1 656 $form['adtype_elements']['#suffix'] = '</div>';
pierre@1 657 }
pierre@1 658 if (!isset($form['adtype_elements'])) {
pierre@1 659 $form['adtype_elements'] = array(
pierre@1 660 '#value' => '<div id="adtype-ahah-wrapper"></div>',
pierre@1 661 '#weight' => 3.1,
pierre@0 662 );
pierre@0 663 }
pierre@0 664
pierre@1 665 // fieldset for updating ad status
pierre@1 666 $form['adstatus'] = array(
pierre@1 667 '#type' => 'fieldset',
pierre@1 668 '#title' => t('Status'),
pierre@1 669 '#collapsible' => TRUE,
pierre@1 670 '#weight' => 4,
pierre@1 671 );
pierre@1 672 $nid = isset($node->nid) ? $node->nid : 0;
pierre@1 673 $adstatus = isset($node->adstatus) ? $node->adstatus : '';
pierre@1 674 // display all available status options
pierre@1 675 foreach (ad_status_array($nid, $adstatus) as $status => $description) {
pierre@1 676 $form['adstatus']["ad$status"] = array(
pierre@1 677 '#type' => 'radio',
pierre@1 678 '#title' => t("$status"),
pierre@1 679 '#return_value' => $status,
pierre@1 680 '#default_value' => isset($node->adstatus) ? $node->adstatus : 'pending',
pierre@1 681 '#description' => "$description",
pierre@1 682 '#parents' => array("adstatus")
pierre@0 683 );
pierre@0 684 }
pierre@0 685
pierre@0 686 // display scheduling options
pierre@0 687 $form['schedule'] = array(
pierre@0 688 '#type' => 'fieldset',
pierre@0 689 '#title' => t('Scheduling'),
pierre@0 690 '#collapsible' => TRUE,
pierre@0 691 // Collapse if there isn't any scheduling data set.
pierre@0 692 '#collapsed' => (
pierre@0 693 isset($node->autoactivate) ||
pierre@0 694 isset($form_state['values']['autoactivate']) ||
pierre@0 695 isset($node->autoexpire) ||
pierre@0 696 isset($form_state['values']['autoexpire']) ||
pierre@0 697 isset($node->maxviews) ||
pierre@0 698 isset($form_state['values']['maxviews']) ||
pierre@0 699 isset($node->maxclicks) ||
pierre@0 700 isset($form_state['values']['maxclicks']))
pierre@0 701 ? FALSE : TRUE,
pierre@0 702 );
pierre@0 703
pierre@1 704 if ((isset($node->nid) && ad_permission($node->nid, 'manage status')) ||
pierre@1 705 user_access('administer advertisements')) {
pierre@0 706 $form['schedule']['current'] = array(
pierre@0 707 '#type' => 'markup',
pierre@0 708 '#prefix' => '<div>',
pierre@0 709 '#suffix' => '</div>',
pierre@0 710 '#value' => t('The current date and time is "%date".', array('%date' => format_date(time(), 'custom', 'F j, Y H:i')))
pierre@0 711 );
pierre@0 712 $form['schedule']['autoactivate'] = array(
pierre@0 713 '#type' => 'textfield',
pierre@0 714 '#title' => t('Automatically activate ad'),
pierre@0 715 '#required' => FALSE,
pierre@0 716 '#default_value' => isset($node->autoactivate) && $node->autoactivate > 0 ? format_date((int)$node->autoactivate, 'custom', 'F j, Y H:i') : 0,
pierre@0 717 '#description' => t('You can specify a date and time for this advertisement to be automatically activated. The advertisement needs to be in an <em>approved</em> state before it can be automatically activated. If you prefer to activate the advertisement immediately, leave this field empty.')
pierre@0 718 );
pierre@0 719 }
pierre@0 720
pierre@0 721 if (user_access('administer advertisements')) {
pierre@0 722 // admins can expire advertisements
pierre@0 723 $form['schedule']['autoexpire'] = array(
pierre@0 724 '#type' => 'textfield',
pierre@0 725 '#title' => t('Automatically expire ad'),
pierre@0 726 '#required' => FALSE,
pierre@0 727 '#default_value' => isset($node->autoexpire) && $node->autoexpire > 0 ? format_date((int)$node->autoexpire, 'custom', 'F j, Y H:i') : 0,
pierre@0 728 '#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.')
pierre@0 729 );
pierre@0 730 $form['schedule']['maxviews'] = array(
pierre@0 731 '#type' => 'textfield',
pierre@0 732 '#title' => t('Maximum impressions'),
pierre@0 733 '#required' => FALSE,
pierre@0 734 '#size' => 10,
pierre@0 735 '#maxlength' => 11,
pierre@0 736 '#default_value' => isset($node->maxviews) ? $node->maxviews : 0,
pierre@0 737 '#description' => t('You can specify the maximum number of times this advertisement should be displayed, after which it will be automatically expired. If you don\'t want this advertisement to expire after a certain number of impressions, leave this field set to %zero.', array('%zero' => '0')),
pierre@0 738 );
pierre@0 739 $form['schedule']['maxclicks'] = array(
pierre@0 740 '#type' => 'textfield',
pierre@0 741 '#title' => t('Maximum clicks'),
pierre@0 742 '#required' => FALSE,
pierre@0 743 '#size' => 10,
pierre@0 744 '#maxlength' => 11,
pierre@0 745 '#default_value' => isset($node->maxclicks) ? $node->maxclicks : 0,
pierre@0 746 '#description' => t('You can specify the maximum number of times this advertisement should be clicked, after which it will be automatically expired. If you don\'t want this advertisement to expire after a certain number of clicks leave this field set to %zero.', array('%zero' => '0')),
pierre@0 747 );
pierre@0 748 }
pierre@0 749 else {
pierre@0 750 // display expiration time
pierre@0 751 $form['schedule']['autoexpire_display'] = array(
pierre@0 752 '#type' => 'markup',
pierre@0 753 '#prefix' => '<div>',
pierre@0 754 '#suffix' => '</div>',
pierre@0 755 '#value' => theme('ad_status_display', $node),
pierre@0 756 );
pierre@0 757 $form['schedule']['autoexpire'] = array(
pierre@0 758 '#type' => 'hidden',
pierre@0 759 '#value' => isset($node->autoexpire) ? $node->autoexpire : 0,
pierre@0 760 );
pierre@0 761 }
pierre@0 762
pierre@1 763 $form['#validate'][] = 'ad_select_adtype';
pierre@0 764 return $form;
pierre@0 765 }
pierre@0 766
pierre@0 767 /**
pierre@1 768 * Ad type switch submit handler.
pierre@1 769 */
pierre@1 770 function ad_select_adtype(&$form, &$form_state) {
pierre@1 771 if (!$form_state['values']['adtype'] && !$form_state['values']['adtype_select']) {
pierre@1 772 form_set_error('adtype_select', t('Please, select an Ad type.'));
pierre@1 773 }
pierre@1 774 if (!isset($form_state['values']['adtype']) || isset($form_state['values']['adtype_select']) && $form_state['values']['adtype'] != $form_state['values']['adtype_select']) {
pierre@1 775 $form_state['values']['adtype'] = $form_state['values']['adtype_select'];
pierre@1 776 $form_state['rebuild'] = TRUE;
pierre@1 777 }
pierre@1 778 }
pierre@1 779
pierre@1 780 /**
pierre@1 781 * Ad type switch AHAH menu handler.
pierre@1 782 */
pierre@1 783 function ad_form_ahah() {
pierre@1 784 $form_state = array('storage' => NULL, 'submitted' => FALSE);
pierre@1 785 $form_build_id = $_POST['form_build_id'];
pierre@1 786 $form = form_get_cache($form_build_id, $form_state);
pierre@1 787 ad_form_add_adtype_elements($form, $_POST['adtype_select']);
pierre@1 788 form_set_cache($form_build_id, $form, $form_state);
pierre@1 789 $form += array(
pierre@1 790 '#post' => $_POST,
pierre@1 791 '#programmed' => FALSE,
pierre@1 792 );
pierre@1 793 // Rebuild the form.
pierre@1 794 $form = form_builder($_POST['form_id'], $form, $form_state);
pierre@1 795 $output = drupal_render($form['adtype_elements']);
pierre@1 796 drupal_json(array(
pierre@1 797 'status' => TRUE,
pierre@1 798 'data' => $output,
pierre@1 799 ));
pierre@1 800 }
pierre@1 801
pierre@1 802 /**
pierre@1 803 * Loads Ad type elements into form.
pierre@1 804 */
pierre@1 805 function ad_form_add_adtype_elements(&$form, $adtype, $node = NULL) {
pierre@1 806 unset($form['adtype_elements']);
pierre@1 807 $form['adtype_elements'] = module_invoke('ad_'. $adtype, 'adapi', 'form', $node);
pierre@1 808 $form['adtype'] = array(
pierre@1 809 '#type' => 'hidden',
pierre@1 810 '#value' => $adtype,
pierre@1 811 );
pierre@1 812 $form['adtype_elements']['#weight'] = 3.1;
pierre@1 813 }
pierre@1 814
pierre@1 815 /**
pierre@0 816 * Implementation of hook_form_alter().
pierre@0 817 */
pierre@0 818 function ad_form_alter(&$form, &$form_state, $form_id) {
pierre@0 819 if ($form_id == 'taxonomy_form_vocabulary') {
pierre@0 820 // Remove taxonomy form options not applicable for ad groups.
pierre@0 821 if ($form['vid']['#value'] == _ad_get_vid()) {
pierre@0 822 $form['help_ad_vocab'] = array(
pierre@0 823 '#value' => t('This vocabulary was automatically created for use by the ad module. Only applicable options are available.'),
pierre@0 824 '#weight' => -1
pierre@0 825 );
pierre@0 826 $form['nodes']['ad'] = array(
pierre@0 827 '#type' => 'checkbox',
pierre@0 828 '#title' => t('ad group'),
pierre@0 829 '#value' => 1,
pierre@0 830 '#attributes' => array('disabled' => ''),
pierre@0 831 '#description' => t('Type %type is required to use this vocabulary.', array('%type' => t('ad group')))
pierre@0 832 );
pierre@0 833 $form['tags']['#description'] = t('If enabled, ads are categorized by typing ad group names instead of choosing them from a list.');
pierre@0 834 $form['multiple']['#description'] = t('If enabled, allows ads to have more than one ad group (always true for free tagging).');
pierre@0 835 $form['required']['#description'] = t('If enabled, every ad <strong>must</strong> be assigned to at least one ad group.');
pierre@0 836 $form['hierarchy'] = array(
pierre@0 837 '#type' => 'value',
pierre@0 838 '#value' => 0
pierre@0 839 );
pierre@0 840 unset($form['relations']);
pierre@0 841 }
pierre@0 842 else {
pierre@0 843 unset($form['nodes']['ad']);
pierre@0 844 }
pierre@0 845 }
pierre@0 846 else if ($form_id == 'taxonomy_form_term') {
pierre@0 847 if ($form['vid']['#value'] == _ad_get_vid()) {
pierre@0 848 $form['name']['#title'] = t('Ad group name');
pierre@0 849 $form['name']['#description'] = t('The name for this ad group. Example: "Linux".');
pierre@0 850 $form['description']['#description'] = t('A description of the ad group.');
pierre@0 851 $form['description']['#required'] = TRUE;
pierre@0 852 $form['weight']['#description'] = t('In listings, the heavier ad groups will sink and the lighter ad groups will be positioned nearer the top.');
pierre@0 853 unset($form['synonyms']);
pierre@0 854 }
pierre@0 855 }
sly@3 856 else if ($form_id == 'search_form' && variable_get('ad_no_search', 1) && !user_access('administer advertisements') && !user_access('administer any advertisement')) {
sly@3 857 $vid = _ad_get_vid();
sly@3 858 $vocabulary = db_result(db_query('SELECT name FROM {vocabulary} WHERE vid = %d', $vid));
sly@3 859 unset($form['advanced']['category']['#options'][$vocabulary]);
sly@3 860 if (empty($form['advanced']['category']['#options'])) {
sly@3 861 unset($form['advanced']['category']);
sly@3 862 }
sly@3 863 unset($form['advanced']['type']['#options']['ad']);
sly@3 864 }
sly@3 865 }
sly@3 866
sly@3 867 /**
sly@3 868 * Implementation of hook_db_rewrite_sql().
sly@3 869 */
sly@3 870 function ad_db_rewrite_sql($query, $primary_table, $primary_field, $args) {
sly@3 871 if (variable_get('ad_no_search', 1) && !user_access('administer advertisements') && !user_access('edit any advertisement') && $query == '' && $primary_table == 'n' && $primary_field = 'nid' && empty($args)) {
sly@3 872 return array('where' => " n.type != 'ad'");
sly@3 873 }
pierre@0 874 }
pierre@0 875
pierre@0 876 /**
pierre@0 877 * Implementation of hook_nodeapi().
pierre@0 878 */
pierre@0 879 function ad_nodeapi(&$node, $op, $teaser, $page) {
pierre@0 880 global $user;
pierre@0 881
pierre@0 882 switch ($op) {
pierre@0 883
pierre@0 884 case 'load':
pierre@0 885 $ad = db_fetch_array(db_query('SELECT * FROM {ads} WHERE aid = %d', $node->nid));
pierre@0 886 $merge = array_merge((array)$node, (array)$ad);
pierre@0 887 $adtype = module_invoke('ad_'. $ad['adtype'], 'adapi', 'load', $merge);
pierre@0 888 if (is_array($adtype)) {
pierre@0 889 return array_merge($ad, $adtype);
pierre@0 890 }
pierre@0 891 else {
pierre@0 892 return $ad;
pierre@0 893 }
pierre@0 894 break;
pierre@0 895
pierre@0 896 case 'insert':
pierre@0 897 if (isset($node->adtype)) {
pierre@0 898 if ($node->status != 1 && $node->adstatus == 'active') {
pierre@1 899 $node->adstatus = 'expired';
pierre@0 900 }
pierre@0 901 $activated = $node->adstatus == 'active' ? time() : 0;
pierre@1 902 if (!isset($node->autoactivate)) {
pierre@0 903 $node->autoactivate = 0;
pierre@0 904 }
pierre@0 905 if (!isset($node->maxviews)) {
pierre@0 906 $node->maxviews = 0;
pierre@0 907 }
pierre@0 908 if (!isset($node->maxclicks)) {
pierre@0 909 $node->maxclicks = 0;
pierre@0 910 }
pierre@0 911 db_query("INSERT INTO {ads} (aid, uid, adstatus, adtype, redirect, autoactivate, autoexpire, activated, maxviews, maxclicks) VALUES(%d, %d, '%s', '%s', '%s', %d, %d, %d, %d, %d)", $node->nid, $node->uid, $node->adstatus, $node->adtype, url('ad/redirect/'. $node->nid, array('absolute' => TRUE)), $node->autoactivate ? strtotime($node->autoactivate) : '', $node->autoexpire ? strtotime($node->autoexpire) : '', $activated, $node->maxviews, $node->maxclicks);
pierre@0 912 ad_statistics_increment($node->nid, 'create');
pierre@0 913 }
pierre@0 914 break;
pierre@0 915
pierre@0 916 case 'update':
pierre@0 917 if (isset($node->adtype)) {
pierre@0 918 $ad = db_fetch_object(db_query('SELECT * FROM {ads} WHERE aid = %d', $node->nid));
pierre@0 919 // Ad must be in approved state to be able to autoactivate it.
pierre@1 920 if ($node->adstatus != 'approved' && isset($node->autoactive) && $node->autoactivate) {
pierre@0 921 if ($node->adstatus == 'active') {
pierre@0 922 // This ad is already active, no need to autoactivate it.
pierre@0 923 $node->autoactivate = 0;
pierre@0 924 }
pierre@0 925 else {
pierre@0 926 drupal_set_message(t('This ad will not be automatically activated at the scheduled time because it is not in the <em>approved</em> state.'), 'error');
pierre@0 927 }
pierre@0 928 }
pierre@0 929 // If this node has been upublished, the ad should no longer be active.
pierre@0 930 if ($node->status != 1 && $node->adstatus == 'active') {
pierre@1 931 $node->adstatus = 'expired';
pierre@0 932 }
pierre@0 933 // Check if ad is being manually activated.
pierre@0 934 if ($ad->adstatus != 'active' && $node->adstatus == 'active') {
pierre@0 935 $activated = time();
pierre@0 936 }
pierre@0 937 // Check if ad is being manually expired.
pierre@0 938 else if ($ad->adstatus != 'expired' && $node->adstatus == 'expired') {
pierre@0 939 // Ad has been manually expired.
pierre@1 940 $activated = $ad->activated;
pierre@0 941 $expired = time();
pierre@0 942 }
pierre@0 943 // Ad has not been manually activated or expired, preserve timestamps.
pierre@0 944 else {
pierre@0 945 $activated = $ad->activated;
pierre@0 946 $expired = $ad->expired;
pierre@0 947 }
pierre@0 948 // Ad status has changed, record the event.
pierre@0 949 if ($ad->adstatus != $node->adstatus) {
pierre@0 950 ad_statistics_increment($node->nid, $node->adstatus);
pierre@0 951 }
pierre@0 952 // Update ads table with new information.
pierre@1 953 db_query("UPDATE {ads} SET uid = %d, adstatus = '%s', adtype = '%s', redirect = '%s', autoactivate = %d, autoexpire = %d, activated = %d, maxviews = %d, maxclicks = %d, expired = %d WHERE aid = %d", $node->uid, $node->adstatus, $node->adtype, url('ad/redirect/'. $node->nid, array('absolute' => TRUE)), isset($node->autoactivate) && strlen($node->autoactivate) > 1 ? strtotime($node->autoactivate) : '', isset($node->autoexpire) && strlen($node->autoexpire) > 1 ? strtotime($node->autoexpire) : '', isset($activated) ? $activated : 0, isset($node->maxviews) ? (int)$node->maxviews : 0, isset($node->maxclicks) ? (int)$node->maxclicks : 0, isset($expired) ? $expired : 0, $node->nid);
pierre@0 954 ad_statistics_increment($node->nid, 'update');
pierre@0 955 }
pierre@0 956 break;
pierre@0 957
pierre@0 958 case 'delete':
pierre@0 959 db_query("DELETE FROM {ads} WHERE aid = %d", $node->nid);
pierre@0 960 db_query("DELETE FROM {ad_statistics} WHERE aid = %d", $node->nid);
pierre@0 961 // All that's left of the ad is a single timestamp as to when it was
pierre@0 962 // deleted.
pierre@0 963 ad_statistics_increment($node->nid, 'delete');
pierre@0 964 break;
pierre@0 965
pierre@0 966 case 'view':
pierre@0 967 if (isset($node->adtype)) {
sly@3 968 if (variable_get('ad_meta_noindex', 1)) {
sly@3 969 ad_noindex_meta();
sly@3 970 }
pierre@0 971 $node = node_prepare($node, $teaser);
pierre@0 972 $node->content['body'] = array(
pierre@0 973 '#value' => $teaser ? $node->teaser : theme('node_ad', $node, $page),
pierre@0 974 '#weight' => 1,
pierre@0 975 );
pierre@0 976 }
pierre@0 977 break;
pierre@0 978 }
pierre@0 979 // Allow ad type module to act on nodeapi events. The adapi hook provides
pierre@0 980 // access to additional variables not available in the nodeapi hook.
pierre@0 981 if (isset($node->adtype)) {
pierre@0 982 // Don't use module_invoke, as in pre-PHP5 the changes to $node won't be
pierre@0 983 // passed back.
pierre@0 984 $function = "ad_$node->adtype" .'_adapi';
pierre@0 985 if (function_exists($function)) {
pierre@0 986 $function($op, $node);
pierre@0 987 }
pierre@0 988 }
pierre@0 989 // Allow ad cache module to act on nodeapi events.
pierre@0 990 $cache = variable_get('ad_cache', 'none');
pierre@0 991 if ($cache != 'none') {
pierre@0 992 $function = "ad_cache_$cache" .'_adcacheapi';
pierre@0 993 if (function_exists($function)) {
pierre@0 994 $function($op, $node);
pierre@0 995 }
pierre@0 996 }
pierre@1 997
pierre@1 998 // Rebuild the cache after all hooks are invoked.
pierre@1 999 switch ($op) {
pierre@1 1000 case 'insert':
pierre@1 1001 case 'update':
pierre@1 1002 case 'delete':
pierre@1 1003 if (variable_get('ad_cache_file_rebuild_realtime', 0) &&
pierre@1 1004 isset($node->adtype)) {
pierre@1 1005 ad_rebuild_cache();
pierre@1 1006 }
pierre@1 1007 }
pierre@0 1008 }
pierre@0 1009
sly@3 1010 /**
sly@3 1011 * Add the noindex meta tag.
sly@3 1012 */
sly@3 1013 function ad_noindex_meta() {
sly@3 1014 static $added = FALSE;
sly@3 1015 if (!$added) {
sly@3 1016 drupal_set_html_head('<meta name="robots" content="noindex" />');
sly@3 1017 $added = TRUE;
sly@3 1018 }
sly@3 1019 }
sly@3 1020
pierre@0 1021 function ad_adapi($op, $node = NULL) {
pierre@0 1022 switch ($op) {
pierre@0 1023 case 'permissions':
pierre@1 1024 return array(
pierre@1 1025 'access statistics' => TRUE,
pierre@1 1026 'access click history' => TRUE,
pierre@1 1027 'set status as pending' => FALSE,
pierre@1 1028 'set status as denied' => FALSE,
pierre@1 1029 'set status from pending to approved' => FALSE,
pierre@1 1030 'set status from pending to denied' => FALSE,
pierre@1 1031 'set status from approved to active' => TRUE,
pierre@1 1032 'set status from approved to offline' => TRUE,
pierre@1 1033 'set status from active to offline' => TRUE,
pierre@1 1034 'set status from active to expired' => FALSE,
pierre@1 1035 'set status from offline to active' => TRUE,
pierre@1 1036 'set status from offline to expired' => FALSE,
pierre@1 1037 );
pierre@0 1038 break;
pierre@0 1039 }
pierre@0 1040 }
pierre@0 1041
pierre@0 1042 /**
pierre@0 1043 * Implementation of hook_menu().
pierre@0 1044 */
pierre@0 1045 function ad_menu() {
pierre@0 1046 $items = array();
pierre@0 1047
pierre@0 1048 $items['admin/content/ad'] = array(
pierre@0 1049 'title' => 'Ads',
pierre@0 1050 'page callback' => 'ad_admin_list',
pierre@0 1051 'access arguments' => array('administer advertisements'),
pierre@0 1052 'description' => 'Configure and manage your advertising system.',
pierre@0 1053 'file' => 'ad.admin.inc',
pierre@0 1054 );
pierre@0 1055 $items['admin/content/ad/list'] = array(
pierre@0 1056 'title' => 'List',
pierre@0 1057 'page callback' => 'ad_admin_list',
pierre@0 1058 'access arguments' => array('administer advertisements'),
pierre@0 1059 'type' => MENU_DEFAULT_LOCAL_TASK,
pierre@0 1060 'file' => 'ad.admin.inc',
pierre@0 1061 );
pierre@0 1062 $items['admin/content/ad/configure'] = array(
pierre@0 1063 'title' => 'Settings',
pierre@0 1064 'page callback' => 'drupal_get_form',
pierre@0 1065 'page arguments' => array('ad_admin_configure_settings'),
pierre@0 1066 'access arguments' => array('administer advertisements'),
pierre@0 1067 'type' => MENU_LOCAL_TASK,
pierre@0 1068 'weight' => 3,
pierre@0 1069 'file' => 'ad.admin.inc',
pierre@0 1070 );
pierre@1 1071 $items['node/add/ad/ahah'] = array(
pierre@1 1072 'access arguments' => array('create advertisements'),
pierre@1 1073 'page callback' => 'ad_form_ahah',
pierre@1 1074 'type' => MENU_CALLBACK,
pierre@1 1075 );
pierre@0 1076
pierre@0 1077 ad_menu_add_global_settings($items);
pierre@0 1078
pierre@0 1079 $items['admin/content/ad/groups'] = array(
pierre@0 1080 'title' => 'Ad groups',
pierre@0 1081 'page callback' => 'ad_admin_groups_list',
pierre@0 1082 'access arguments' => array('administer advertisements'),
pierre@0 1083 'type' => MENU_LOCAL_TASK,
pierre@0 1084 'weight' => 5,
pierre@0 1085 'file' => 'ad.admin.inc',
pierre@0 1086 );
pierre@0 1087 $items['admin/content/ad/groups/list'] = array(
pierre@0 1088 'title' => 'List',
pierre@0 1089 'page callback' => 'ad_admin_groups_list',
pierre@0 1090 'access arguments' => array('administer advertisements'),
pierre@0 1091 'type' => MENU_DEFAULT_LOCAL_TASK,
pierre@0 1092 'weight' => 0,
pierre@0 1093 'file' => 'ad.admin.inc',
pierre@0 1094 );
pierre@0 1095 $items['admin/content/ad/groups/add'] = array(
pierre@0 1096 'title' => 'Create group',
pierre@0 1097 'page callback' => 'drupal_get_form',
pierre@0 1098 'page arguments' => array('ad_admin_group_form'),
pierre@0 1099 'access arguments' => array('administer advertisements'),
pierre@0 1100 'type' => MENU_LOCAL_TASK,
pierre@0 1101 'weight' => 3,
pierre@0 1102 'file' => 'ad.admin.inc',
pierre@0 1103 );
pierre@0 1104 $items["admin/content/ad/groups/%ad_group/edit"] = array(
pierre@0 1105 'title' => 'Edit group',
pierre@0 1106 'page callback' => 'drupal_get_form',
pierre@0 1107 'page arguments' => array('ad_admin_group_form', 4),
pierre@0 1108 'access arguments' => array('administer advertisements'),
pierre@0 1109 'weight' => 1,
pierre@0 1110 'file' => 'ad.admin.inc',
pierre@0 1111 );
pierre@0 1112 $items["admin/content/ad/groups/%ad_group/delete"] = array(
pierre@0 1113 'title' => 'Delete group',
pierre@0 1114 'page callback' => 'drupal_get_form',
pierre@0 1115 'page arguments' => array('ad_confirm_group_delete', 4),
pierre@0 1116 'access arguments' => array('administer advertisements'),
pierre@0 1117 'weight' => 2,
pierre@0 1118 'file' => 'ad.admin.inc',
pierre@0 1119 );
pierre@0 1120 $items['admin/content/ad/configure/global'] = array(
pierre@0 1121 'title' => 'Global settings',
pierre@0 1122 'page callback' => 'drupal_get_form',
pierre@0 1123 'page arguments' => array('ad_admin_configure_settings'),
pierre@0 1124 'access arguments' => array('administer advertisements'),
pierre@0 1125 'type' => MENU_DEFAULT_LOCAL_TASK,
pierre@0 1126 'weight' => 0,
pierre@0 1127 'file' => 'ad.admin.inc',
pierre@0 1128 );
pierre@0 1129 $items["node/%node/details/%"] = array(
pierre@0 1130 'title' => 'Click details',
pierre@0 1131 'page callback' => 'ad_click_details',
pierre@0 1132 'page arguments' => array(1, 3),
pierre@0 1133 'access arguments' => array(1, 'access click history'),
pierre@1 1134 'access callback' => 'ad_permission',
pierre@0 1135 'type' => MENU_CALLBACK,
pierre@0 1136 'file' => 'ad.pages.inc',
pierre@0 1137 );
pierre@1 1138 $items["ad/redirect/%"] = array(
pierre@0 1139 'access arguments' => array('show advertisements'),
pierre@0 1140 'type' => MENU_CALLBACK,
pierre@0 1141 'page callback' => 'ad_redirect',
pierre@1 1142 'page arguments' => (array(2)),
pierre@0 1143 );
pierre@0 1144
pierre@0 1145 return $items;
pierre@0 1146 }
pierre@0 1147
pierre@0 1148 /**
pierre@0 1149 * Load settings for all ad modules. Those modules, who don't
pierre@0 1150 * have their settings form, will get a standard one.
pierre@0 1151 */
pierre@0 1152 function ad_menu_add_global_settings(&$menu_items) {
pierre@0 1153 $adtypes = ad_get_types();
pierre@0 1154 foreach ($adtypes as $type => $name) {
pierre@0 1155 // Ad type global settings.
pierre@0 1156 $settings = 'ad_'. $type .'_global_settings';
pierre@1 1157 $file = 'ad_image.module';
pierre@0 1158 if (!function_exists($settings)) {
pierre@0 1159 $settings = 'ad_no_global_settings';
pierre@1 1160 $file = 'ad.admin.inc';
pierre@0 1161 }
pierre@0 1162 $menu_items['admin/content/ad/configure/'. $type] = array(
pierre@0 1163 'title' => $name,
pierre@0 1164 'page callback' => 'drupal_get_form',
pierre@0 1165 'page arguments' => array($settings),
pierre@0 1166 'access arguments' => array('administer advertisements'),
pierre@0 1167 'type' => MENU_LOCAL_TASK,
pierre@0 1168 'weight' => 2,
pierre@0 1169 'file' => 'ad.admin.inc',
pierre@0 1170 );
pierre@0 1171 }
pierre@0 1172 }
pierre@0 1173
pierre@0 1174 /**
pierre@0 1175 * Drupal menu wildcard Ad group loader
pierre@0 1176 */
pierre@0 1177 function ad_group_load($tid) {
pierre@0 1178 if (!is_numeric($tid)) {
pierre@0 1179 return FALSE;
pierre@0 1180 }
pierre@0 1181 $group = ad_groups_list(TRUE, $tid);
pierre@0 1182 if (!isset($group)) {
pierre@0 1183 return FALSE;
pierre@0 1184 }
pierre@0 1185 return $group;
pierre@0 1186 }
pierre@0 1187
pierre@0 1188 /**
pierre@0 1189 * Implementation of hook_block().
pierre@0 1190 */
pierre@0 1191 function ad_block($op = 'list', $delta = 0, $edit = array()) {
pierre@0 1192 switch ($op) {
pierre@0 1193 case 'list':
pierre@0 1194 $blocks = array();
pierre@0 1195 $groups = ad_groups_list();
pierre@0 1196 foreach ($groups as $tid => $name) {
pierre@0 1197 $blocks[$tid]['info'] = t('ad group: @name', array('@name' => $name));
pierre@0 1198 }
pierre@0 1199 return $blocks;
pierre@0 1200 case 'configure':
pierre@0 1201 $form['ad_block_quantity_'. $delta] = array(
pierre@0 1202 '#type' => 'select',
pierre@0 1203 '#title' => t('Number of ads'),
pierre@0 1204 '#default_value' => variable_get('ad_block_quantity_'. $delta, 1),
pierre@0 1205 '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)),
pierre@0 1206 '#description' => t('Select the maximum number of unique ads that should be displayed together in this block. If you specify a number larger than the maximum number of ads in this ad group, all ads will be displayed once.'),
pierre@0 1207 );
pierre@0 1208 return $form;
pierre@0 1209 case 'save':
pierre@0 1210 variable_set('ad_block_quantity_'. $delta, $edit['ad_block_quantity_'. $delta]);
pierre@0 1211 break;
pierre@0 1212 case 'view':
pierre@0 1213 $block['content'] = ad($delta, variable_get('ad_block_quantity_'. $delta, 1));
pierre@0 1214 return $block;
pierre@0 1215 }
pierre@0 1216 }
pierre@0 1217
pierre@0 1218 /**
pierre@0 1219 * Determine whether the user has a given privilege.
pierre@0 1220 *
pierre@1 1221 * @param $aid
pierre@1 1222 * ID of advertisement.
pierre@0 1223 * @param $permission
pierre@1 1224 * Permission string which should be checked (such as 'access click history')
pierre@0 1225 * @param $account
pierre@0 1226 * User object, which are accessing the ad or current user by default.
pierre@0 1227 */
pierre@1 1228 function ad_permission($aid, $string, $account = NULL) {
pierre@0 1229 global $user;
pierre@1 1230 $access = FALSE;
pierre@0 1231
pierre@1 1232 // by default, check permission for current user
pierre@0 1233 if (!isset($account)) {
pierre@0 1234 $account = $user;
pierre@0 1235 }
pierre@0 1236
pierre@1 1237 // user #1 has all privileges
pierre@0 1238 if ($account->uid == 1) {
pierre@0 1239 return TRUE;
pierre@0 1240 }
pierre@0 1241
pierre@1 1242 // if you have administer permissions, you have all permissions
pierre@0 1243 if (user_access('administer advertisements', $account)) {
pierre@0 1244 return TRUE;
pierre@0 1245 }
pierre@0 1246
pierre@1 1247 // when used in the Drupal menu, $aid may be the full ad object.
pierre@1 1248 if (is_object($aid) && isset($aid->aid)) {
pierre@1 1249 $aid = $aid->aid;
pierre@1 1250 }
sly@3 1251 else if (is_object($aid) && isset($aid->nid)) {
sly@3 1252 $aid = $aid->nid;
sly@3 1253 }
pierre@1 1254 else if (is_object($aid)) {
pierre@1 1255 watchdog('ad', 'Invalid aid object passed into ad_permission, no aid->aid set.');
pierre@1 1256 $aid = 0;
pierre@0 1257 }
pierre@0 1258
pierre@1 1259 // invoke ad_owners module to determine user's access
pierre@1 1260 if (module_exists('ad_owners') &&
pierre@1 1261 function_exists('ad_owners_permission')) {
pierre@1 1262 $access = ad_owners_permission($aid, $string, $account);
pierre@1 1263 }
pierre@1 1264 // no ad_owners module, allow acces to statistics and click history
pierre@1 1265 else if (in_array($string, array('access statistics', 'access click history'))) {
pierre@1 1266 $access = TRUE;
pierre@1 1267 }
pierre@1 1268 // with no ad_owners module, all other permissions are denied unless user
pierre@1 1269 // has 'administer advertisements' permission
pierre@1 1270 return $access;
pierre@0 1271 }
pierre@0 1272
pierre@0 1273 /**
pierre@0 1274 * Returns ad types data.
pierre@0 1275 *
pierre@0 1276 * @param $op
pierre@0 1277 * If set to 'name', will only return array of type names ($type => $name).
pierre@0 1278 * If set to 'data', will return all of the type's data
pierre@0 1279 * @param $type
pierre@0 1280 * If not specified, will return array of all available types, else will
pierre@0 1281 * return specific type's name or data.
pierre@0 1282 */
pierre@0 1283 function ad_get_types($op = 'name', $type = NULL) {
pierre@0 1284 $adtypes = module_invoke_all('adapi', 'type', array());
pierre@0 1285 switch ($op) {
pierre@0 1286 case 'name':
pierre@0 1287 if (isset($type)) {
pierre@0 1288 return $adtypes[$type]['name'];
pierre@0 1289 }
pierre@0 1290 else {
pierre@0 1291 foreach ($adtypes as $type => $data) {
pierre@0 1292 $adtypes[$type] = $data['name'];
pierre@0 1293 }
pierre@0 1294 return $adtypes;
pierre@0 1295 }
pierre@0 1296 case 'data':
pierre@0 1297 if (isset($type)) {
pierre@0 1298 return $adtypes[$type];
pierre@0 1299 }
pierre@0 1300 else {
pierre@0 1301 return $adtypes;
pierre@0 1302 }
pierre@0 1303 }
pierre@0 1304 }
pierre@0 1305
pierre@0 1306 /**
pierre@0 1307 * Return an array of all groups, or a specific group.
pierre@0 1308 *
pierre@0 1309 * @param $object
pierre@0 1310 * If FALSE, will return only name of group(s). If TRUE, will return full
pierre@0 1311 * group object including ->name, ->description, and ->tid.
pierre@0 1312 * @param $tid
pierre@0 1313 * If set to an integer >0, will only return group info about that specific
pierre@0 1314 * group.
pierre@0 1315 */
pierre@0 1316 function ad_groups_list($object = FALSE, $tid = NULL) {
pierre@0 1317 static $groups = array();
pierre@0 1318 static $names = array();
pierre@0 1319
pierre@0 1320 // Return the full group object(s).
pierre@0 1321 if ($object) {
pierre@0 1322 if (empty($groups)) {
pierre@0 1323 $tids = taxonomy_get_tree(_ad_get_vid());
pierre@0 1324 if (is_array($tids)) {
pierre@0 1325 foreach ($tids as $group) {
pierre@0 1326 $groups[$group->tid]->name = $group->name;
pierre@0 1327 $groups[$group->tid]->description = $group->description;
pierre@0 1328 $groups[$group->tid]->tid = $group->tid;
pierre@0 1329 $groups[$group->tid]->weight = $group->weight;
pierre@0 1330 }
pierre@0 1331 }
pierre@0 1332 // Hard coded "default" group with tid of 0.
pierre@0 1333 $groups[0]->name = t('default');
pierre@0 1334 $groups[0]->description = t('The default ad group is comprised of all ads not assigned to any other ad group.');
pierre@0 1335 $groups[0]->tid = 0;
pierre@0 1336 $groups[0]->weight = 0;
pierre@0 1337 }
pierre@0 1338 // Return a specific group object.
pierre@0 1339 if ((int)$tid) {
pierre@0 1340 return $groups[$tid];
pierre@0 1341 }
pierre@0 1342 // Return an array of all group objects.
pierre@0 1343 else {
pierre@0 1344 return $groups;
pierre@0 1345 }
pierre@0 1346 }
pierre@0 1347 // Return only the group name(s).
pierre@0 1348 else {
pierre@0 1349 if (empty($names)) {
pierre@0 1350 $tids = taxonomy_get_tree(_ad_get_vid());
pierre@0 1351 if (is_array($tids)) {
pierre@0 1352 foreach ($tids as $group) {
pierre@0 1353 $names[$group->tid] = $group->name;
pierre@0 1354 }
pierre@0 1355 }
pierre@0 1356 // Hard coded "default" group with tid of 0.
pierre@0 1357 $names[0] = t('default');
pierre@0 1358 }
pierre@0 1359 // Return a specific group name.
pierre@0 1360 if ((int)$tid) {
pierre@0 1361 return $names[$tid];
pierre@0 1362 }
pierre@0 1363 // Return an array of all group names.
pierre@0 1364 else {
pierre@0 1365 return $names;
pierre@0 1366 }
pierre@0 1367 }
pierre@0 1368 }
pierre@0 1369
pierre@0 1370 /**
pierre@0 1371 * Implement ad notify api _hook.
pierre@0 1372 */
pierre@0 1373 function ad_adnotifyapi($op, $arg1 = NULL, $arg2 = NULL) {
pierre@0 1374 switch ($op) {
pierre@0 1375 // Make the following events available for notification.
pierre@0 1376 case 'register':
pierre@0 1377 return array(
pierre@0 1378 '-expired' => t('Email @when before the advertisement will expire.'),
pierre@0 1379 'expired' => t('Email @when after the advertisement is expired.'),
pierre@0 1380 '-active' => t('Email @when before the advertisement will be activated (if scheduled).'),
pierre@0 1381 'active' => t('Email @when after the advertisement is activated.'),
sly@2 1382 'offline' => t('Email @when after the advertisement is taken offline.'),
pierre@0 1383 'click' => t('Email @when after the advertisement is clicked.'),
pierre@0 1384 'approved' => t('Email @when after the advertisement is approved.'),
pierre@0 1385 'denied' => t('Email @when after the advertisement is denied.'),
sly@2 1386 'update' => t('Email @when after the advertisement is updated.'),
pierre@0 1387 );
pierre@0 1388 break;
pierre@0 1389 case '-expired':
pierre@0 1390 $node = node_load($arg1->aid);
pierre@0 1391 if (isset($node->autoexpire) && $node->autoexpire) {
pierre@0 1392 if ((time() + $arg1->delay >= $node->autoexpire) &&
pierre@0 1393 ($arg1->sent + $arg1->delay < $node->autoexpire)) {
pierre@0 1394 return array('-expired' => 1);
pierre@0 1395 }
pierre@0 1396 }
pierre@0 1397 break;
pierre@0 1398 case '-active':
pierre@0 1399 $node = node_load($arg1->aid);
pierre@0 1400 if (isset($node->autoactivate) && $node->autoactivate) {
pierre@0 1401 if ((time() + $arg1->delay >= $node->autoactivate) &&
pierre@0 1402 ($arg1->sent + $arg1->delay < $node->autoactivate)) {
pierre@0 1403 return array('-active' => 1);
pierre@0 1404 }
pierre@0 1405 }
pierre@0 1406 break;
pierre@0 1407 case 'mail_text':
pierre@0 1408 switch ($arg1) {
pierre@0 1409 case 'expired':
pierre@0 1410 return array(
pierre@1 1411 'subject' => t('[%site-name ad] %event notification'),
pierre@1 1412 'body' => t("Hello %owner_name,\n\n This is an automatically generated notification to inform you that your advertisement \"%title\" that was being displayed on the %site-name website has expired.\n\n Your advertisement was viewed %global_impressions times and clicked %global_clicks times since it was activated on %activated_large.\n\n You can view additional statistics about this advertisement or update this notification at the following url:\n %url\n\nRegards,\n The %site-name Team\n\n-\n%site-url"),
pierre@0 1413 );
pierre@0 1414 case '-expired':
pierre@0 1415 return array(
pierre@1 1416 'subject' => t('[%site-name ad] expiration notification'),
pierre@1 1417 'body' => t("Hello %owner_name,\n\n This is an automatically generated notification to inform you that your advertisement \"%title\" that is being displayed on the %site-name website will expire on %autoexpire_large.\n\n Your advertisement has been viewed %today_impressions times and clicked %today_clicks times today. It was viewed %yesterday_impressions times and clicked %yesterday_clicks times yesterday. It has been viewed %global_impressions times and clicked %global_clicks times since it was activated on %activated_large.\n\n You can view additional statistics about this advertisement or update this notification at the following url:\n %url\n\nRegards,\n The %site-name Team\n\n-\n%site-url"),
pierre@0 1418 );
pierre@0 1419 case 'active':
pierre@0 1420 return array(
pierre@1 1421 'subject' => t('[%site-name ad] %event notification'),
pierre@1 1422 'body' => t("Hello %owner_name,\n\n This is an automatically generated notification to inform you that your advertisement \"%title\" is now actively being displayed on the %site-name website.\n\n Your advertisement has been viewed %global_impressions times and clicked %global_clicks times since it was activated on %activated_large.\n\n You can view additional statistics about this advertisement or update this notification at the following url:\n %url\n\nRegards,\n The %site-name Team\n\n-\n%site-url"),
pierre@0 1423 );
pierre@0 1424 case '-active':
pierre@0 1425 return array(
pierre@1 1426 'subject' => t('[%site-name ad] activation notification'),
pierre@1 1427 'body' => t("Hello %owner_name,\n\n This is an automatically generated notification to inform you that your advertisement \"%title\" will be actively displayed on the %site-name website on %autoactivate_large.\n\n You can view statistics about this advertisement or update this notification at the following url:\n %url\n\nRegards,\n The %site-name Team\n\n-\n%site-url"),
pierre@0 1428 );
pierre@0 1429 case 'click':
pierre@0 1430 return array(
pierre@1 1431 'subject' => t('[%site-name ad] %event notification'),
pierre@1 1432 'body' => t("Hello %owner_name,\n\n This is an automatically generated notification to inform you that your advertisement \"%title\" on the %site-name website has been clicked.\n\n Your advertisement has been viewed %today_impressions times and clicked %today_clicks times today. It was viewed %yesterday_impressions times and clicked %yesterday_clicks times yesterday. It has been viewed %global_impressions times and clicked %global_clicks times since it was activated on %activated_large.\n\n You will receive this %frequency You can view additional statistics about this advertisement or update this notification at the following url:\n %url\n\nRegards,\n The %site-name Team\n\n-\n%site-url"),
pierre@0 1433 );
pierre@0 1434 case 'approved':
pierre@0 1435 return array(
pierre@1 1436 'subject' => t('[%site-name ad] %event notification'),
pierre@1 1437 'body' => t("Hello %owner_name,\n\n This is an automatically generated notification to inform you that your advertisement \"%title\" on the %site-name website has been approved.\n\n You can view statistics about this advertisement at the following url:\n %url\n\nRegards,\n The %site-name Team\n\n-\n%site-url"),
pierre@0 1438 );
pierre@0 1439 case 'denied':
pierre@0 1440 return array(
pierre@1 1441 'subject' => t('[%site-name ad] %event notification'),
pierre@1 1442 'body' => t("Hello %owner_name,\n\n This is an automatically generated notification to inform you that your advertisement \"%title\" on the %site-name website has been denied and will not be displayed.\n\n You can view statistics about this advertisement at the following url:\n %url\n\nRegards,\n The %site-name Team\n\n-\n%site-url"),
pierre@0 1443 );
pierre@0 1444 }
pierre@0 1445 break;
pierre@0 1446 }
pierre@0 1447 }
pierre@0 1448
pierre@0 1449 function _ad_check_installation() {
pierre@0 1450 // Verify serve.php exists and is readable.
pierre@0 1451 $adserve = variable_get('adserve', '');
pierre@0 1452 $adserveinc = variable_get('adserveinc', '');
pierre@0 1453 if (!file_exists($adserve)) {
pierre@0 1454 // The serve.php file should be in the same directory as the ad.module.
pierre@0 1455 $adserve = drupal_get_path('module', 'ad') .'/serve.php';
pierre@0 1456 variable_set('adserve', $adserve);
pierre@0 1457 }
pierre@0 1458 if (!is_readable($adserve)) {
pierre@0 1459 variable_set('adserve', '');
pierre@0 1460 drupal_set_message(t('Failed to read the required file %filename. Please make the file readable by the webserver process. No ads can be displayed until this problem is resolved.', array('%filename' => $adserve)), 'error');
pierre@0 1461 }
pierre@0 1462 if (!file_exists($adserveinc)) {
pierre@0 1463 // The adserve.inc file should be in the same directory as the ad.module.
pierre@0 1464 $adserveinc = drupal_get_path('module', 'ad') .'/adserve.inc';
pierre@0 1465 variable_set('adserveinc', $adserveinc);
pierre@0 1466 }
pierre@0 1467 if (!is_readable($adserveinc)) {
pierre@0 1468 variable_set('adserveinc', '');
pierre@0 1469 drupal_set_message(t('Failed to read the required file %filename. Please make the file readable by the webserver process. No ads can be displayed until this problem is resolved.', array('%filename' => $adserveinc)), 'error');
pierre@0 1470 }
pierre@0 1471
pierre@0 1472 // Validate vid in vocabulary table.
pierre@0 1473 $vid = db_result(db_query("SELECT vid FROM {vocabulary} WHERE module = 'ad'"));
pierre@0 1474 if ($vid != variable_get('ad_group_vid', '')) {
pierre@0 1475 drupal_set_message(t('Invalid vocabulary defined for advertisements, attempting to auto-fix.'), 'error');
pierre@0 1476 if ($vid) {
pierre@0 1477 db_query("DELETE FROM {vocabulary_node_types} WHERE vid = %d OR type = 'ad'", variable_get('ad_group_vid', ''));
pierre@0 1478 variable_set('ad_group_vid_restore', variable_get('ad_group_vid', ''));
pierre@0 1479 }
pierre@0 1480 variable_del('ad_group_vid');
pierre@0 1481 }
pierre@0 1482 else {
pierre@0 1483 // Validate vid in vocabulary_node_types table.
pierre@0 1484 $result = db_query("SELECT vid FROM {vocabulary_node_types} WHERE type = 'ad'");
pierre@0 1485 $found = FALSE;
pierre@0 1486 while ($vocab = db_fetch_object($result)) {
pierre@0 1487 if ($vocab->vid == variable_get('ad_group_vid', '')) {
pierre@0 1488 $found = TRUE;
pierre@0 1489 }
pierre@0 1490 }
pierre@0 1491 if (!$found) {
pierre@0 1492 drupal_set_message(t('Missing vocabulary node type for advertisements, attempting to auto-fix.'), 'error');
pierre@0 1493 db_query("DELETE FROM {vocabulary_node_types} WHERE vid = %d OR type = 'ad'", variable_get('ad_group_vid', ''));
pierre@0 1494 db_query("DELETE FROM {vocabulary} WHERE vid = %d", variable_get('ad_group_vid', ''));
pierre@0 1495 variable_set('ad_group_vid_restore', variable_get('ad_group_vid', ''));
pierre@0 1496 variable_del('ad_group_vid');
pierre@0 1497 }
pierre@0 1498 }
pierre@0 1499
pierre@0 1500 _ad_get_vid();
pierre@0 1501 // Preserve old ad groups, if any.
pierre@0 1502 if (($old = variable_get('ad_group_vid_restore', '')) &&
pierre@0 1503 $vid = variable_get('ad_group_vid', '')) {
pierre@0 1504 drupal_set_message(t('Restoring orphaned ad group configuration.'));
pierre@0 1505 db_query('UPDATE {term_data} SET vid = %d WHERE vid = %d', $vid, $old);
pierre@0 1506 variable_set('ad_group_vid_restore', '');
pierre@0 1507 }
pierre@0 1508
pierre@0 1509 $rid = db_result(db_query_range("SELECT rid FROM {permission} WHERE perm LIKE '%%show advertisements%%'", 1));
pierre@0 1510 if (!$rid) {
pierre@0 1511 drupal_set_message(t('Be sure to enable "!show" permissions for all roles that you wish to see advertisements.', array('!show' => l(t('show advertisements'), 'admin/user/permissions'))));
pierre@0 1512 }
pierre@0 1513
pierre@0 1514 // Allow modules to define an action to take each time an ad is served.
pierre@0 1515 // When modules define 'adserve_select' or 'adserve_filter', they must set
pierre@0 1516 // the 'function' and 'path' parameters. The 'weight' parameter can
pierre@0 1517 // optionally be set.
pierre@0 1518 // function: the function to call when serving an add
pierre@0 1519 // path: the path to the include file where $function is defined
pierre@0 1520 // Modules can define actions that happen when advertisements are served.
pierre@0 1521 // Currently support actions are:
pierre@0 1522 // - init_text (display content before displaying ads) // TODO
pierre@0 1523 // - select (alter which ads are selected to be displayed) // TODO
pierre@0 1524 // - filter (filter selected ads before they are displayed) // TODO
pierre@0 1525 // - exit_text (display content after displaying ads) // TODO
pierre@0 1526 $hooks = array('init_text', 'select', 'filter', 'exit_text');
pierre@0 1527 foreach ($hooks as $hook) {
pierre@0 1528 $adserve_actions = module_invoke_all('adapi', "adserve_$hook", array());
pierre@0 1529 $actions = array();
pierre@0 1530 foreach ($adserve_actions as $name => $action) {
pierre@0 1531 if (is_numeric($action['weight'])) {
pierre@0 1532 $weight = $action['weight'];
pierre@0 1533 }
pierre@0 1534 else {
pierre@0 1535 // weight is an optional, defaults to 0
pierre@0 1536 $weight = $action['weight'] = 0;
pierre@0 1537 }
pierre@0 1538 $actions[$weight .'.'. $name] = $action;
pierre@0 1539 $actions[$weight .'.'. $name]['name'] = $name;
pierre@0 1540 }
pierre@0 1541 // order actions by weight (multiple same-weight actions sorted by alpha)
pierre@0 1542 ksort($actions);
pierre@0 1543 variable_set("adserve_$hook", serialize($actions));
pierre@0 1544 }
pierre@0 1545
pierre@0 1546 module_invoke_all('adapi', 'check_install', array());
pierre@0 1547 }
pierre@0 1548
pierre@0 1549 /**
pierre@0 1550 * Creates a vocabulary for use by ad groups if not already created.
pierre@0 1551 */
pierre@0 1552 function _ad_get_vid() {
pierre@0 1553 $vid = variable_get('ad_group_vid', '');
pierre@0 1554 if (empty($vid)) {
pierre@0 1555 // No vid stored in the variables table, check if one even exists.
pierre@0 1556 $vid = db_result(db_query("SELECT vid FROM {vocabulary} WHERE module = '%s'", 'ad'));
pierre@0 1557 if (!$vid) {
pierre@0 1558 // No vid, so we create one.
pierre@0 1559 $edit = array(
pierre@0 1560 'name' => t('Ad groups'),
pierre@0 1561 'multiple' => 1,
pierre@0 1562 'required' => 0,
pierre@0 1563 'hierarchy' => 0,
pierre@0 1564 'relations' => 0,
pierre@0 1565 'module' => 'ad',
pierre@0 1566 'nodes' => array('ad' => 1)
pierre@0 1567 );
pierre@0 1568
pierre@0 1569 taxonomy_save_vocabulary($edit);
pierre@0 1570 $vid = $edit['vid'];
pierre@0 1571 }
pierre@0 1572 // Save the vid for next time.
pierre@0 1573 variable_set('ad_group_vid', $vid);
pierre@0 1574 }
pierre@0 1575 return $vid;
pierre@0 1576 }
pierre@0 1577
pierre@0 1578 /**
pierre@0 1579 * Builds the necessary HTML to display an image-based impressions counter.
pierre@0 1580 */
sly@2 1581 function ad_display_image($aid, $css = TRUE) {
pierre@0 1582 global $base_url;
pierre@0 1583 $adserve = variable_get('adserve', '');
pierre@0 1584 $cache = variable_get('ad_cache', 'none');
pierre@0 1585 $variables = "?o=image";
sly@2 1586 $variables .= "&a=$aid";
sly@2 1587 if ($cache != 'none') {
sly@2 1588 $variables .= '&c='. $cache . module_invoke('ad_cache_'. $cache, 'adcacheapi', 'display_variables', array());
pierre@0 1589 }
sly@2 1590 $output = '<img src="'. htmlentities(url($base_url .'/'. $adserve . $variables)) .'" height="0" width="0" alt="view counter" />';
pierre@0 1591 if ($css) {
pierre@0 1592 return '<div class="ad-image-counter">'. $output .'</div>';
pierre@0 1593 }
pierre@0 1594 else {
pierre@0 1595 return $output;
pierre@0 1596 }
pierre@0 1597 }
pierre@0 1598
pierre@0 1599 /**
pierre@0 1600 * Retrieve the group name from the nid.
pierre@0 1601 */
pierre@0 1602 function _ad_get_group($nid) {
pierre@0 1603 static $groups = array();
pierre@0 1604
pierre@0 1605 if (!isset($groups[$nid])) {
pierre@0 1606 $result = db_query('SELECT d.name FROM {term_data} d LEFT JOIN {term_node} n ON d.tid = n.tid WHERE n.nid = %d AND d.vid = %d', $nid, _ad_get_vid());
pierre@0 1607 while ($term = db_fetch_object($result)) {
pierre@0 1608 $terms[] = $term->name;
pierre@0 1609 }
pierre@0 1610 if (!empty($terms)) {
pierre@0 1611 $groups[$nid] = implode(', ', $terms);
pierre@0 1612 }
pierre@0 1613 else {
pierre@0 1614 $groups[$nid] = t('default');
pierre@0 1615 }
pierre@0 1616 }
pierre@0 1617
pierre@0 1618 return $groups[$nid];
pierre@0 1619 }
pierre@0 1620