| pierre@0 | 1 <?php | 
| pierre@0 | 2 // $Id: ad_weight_percent.module,v 1.1.2.4.2.2 2009/02/16 17:06:50 jeremy Exp $ | 
| pierre@0 | 3 | 
| pierre@0 | 4 /** | 
| pierre@0 | 5  * @file | 
| pierre@0 | 6  * A plug in for the ad.module, providing a percentage based weighting mechanism | 
| pierre@0 | 7  * for the random selection of ads. | 
| pierre@0 | 8  * | 
| pierre@0 | 9  * Copyright (c) 2007-2009. | 
| pierre@0 | 10  *  Jeremy Andrews <jeremy@tag1consulting.com>. | 
| pierre@0 | 11  */ | 
| pierre@0 | 12 | 
| pierre@0 | 13 /** | 
| pierre@0 | 14  * Drupal hook_menu(). | 
| pierre@0 | 15  */ | 
| pierre@0 | 16 function ad_weight_percent_menu() { | 
| pierre@0 | 17   $items = array(); | 
| pierre@0 | 18 | 
| pierre@0 | 19 /* TODO | 
| pierre@0 | 20    Non menu code that was placed in hook_menu under the '!$may_cache' block | 
| pierre@0 | 21    so that it could be run during initialization, should now be moved to hook_init. | 
| pierre@0 | 22    Previously we called hook_init twice, once early in the bootstrap process, second | 
| pierre@0 | 23    just after the bootstrap has finished. The first instance is now called boot | 
| pierre@0 | 24    instead of init. | 
| pierre@0 | 25 | 
| pierre@0 | 26    In Drupal 6, there are now two hooks that can be used by modules to execute code | 
| pierre@0 | 27    at the beginning of a page request. hook_boot() replaces hook_boot() in Drupal 5 | 
| pierre@0 | 28    and runs on each page request, even for cached pages. hook_boot() now only runs | 
| pierre@0 | 29    for non-cached pages and thus can be used for code that was previously placed in | 
| pierre@0 | 30    hook_menu() with $may_cache = FALSE: | 
| pierre@0 | 31 | 
| pierre@0 | 32    Dynamic menu items under a '!$may_cache' block can often be simplified | 
| pierre@0 | 33    to remove references to arg(n) and use of '%<function-name>' to check | 
| pierre@0 | 34    conditions. See http://drupal.org/node/103114. | 
| pierre@0 | 35 | 
| pierre@0 | 36    The title and description arguments should not have strings wrapped in t(), | 
| pierre@0 | 37    because translation of these happen in a later stage in the menu system. | 
| pierre@0 | 38 */ | 
| pierre@0 | 39   if ($may_cache) { | 
| pierre@0 | 40     $items['admin/content/ad/groups/percent'] = array( | 
| pierre@0 | 41       'title' => 'Weight Percent', | 
| pierre@0 | 42       'page callback' => 'drupal_get_form', | 
| pierre@0 | 43       'page arguments' => array('ad_weight_percent_settings'), | 
| pierre@0 | 44       'type' => MENU_LOCAL_TASK, | 
| pierre@0 | 45       'weight' => 5, | 
| pierre@0 | 46     ); | 
| pierre@0 | 47   } | 
| pierre@0 | 48 | 
| pierre@0 | 49   return $items; | 
| pierre@0 | 50 } | 
| pierre@0 | 51 | 
| pierre@0 | 52 /** | 
| pierre@0 | 53  * Configure per-group percentage settings. | 
| pierre@0 | 54  */ | 
| pierre@0 | 55 function ad_weight_percent_settings() { | 
| pierre@0 | 56   $form = array(); | 
| pierre@0 | 57 | 
| pierre@0 | 58   $groups = module_invoke('ad', 'groups_list', TRUE); | 
| pierre@0 | 59   foreach ($groups as $tid => $group) { | 
| pierre@0 | 60     $form["group-$tid"] = array( | 
| pierre@0 | 61       '#type' => 'fieldset', | 
| pierre@0 | 62       '#title' => $group->name, | 
| pierre@0 | 63       '#collapsible' => TRUE, | 
| pierre@0 | 64       '#collapsed' => variable_get("enable-$tid", 0) ? FALSE : TRUE, | 
| pierre@0 | 65     ); | 
| pierre@0 | 66     $form["group-$tid"]["description-$tid"] = array( | 
| pierre@0 | 67       '#type' => 'markup', | 
| pierre@0 | 68       '#prefix' => '<div>', | 
| pierre@0 | 69       '#suffix' => '</div>', | 
| pierre@0 | 70       '#value' => theme_placeholder("$group->description"), | 
| pierre@0 | 71     ); | 
| pierre@0 | 72     $form["group-$tid"]["enable-$tid"] = array( | 
| pierre@0 | 73       '#type' => 'checkbox', | 
| pierre@0 | 74       '#default_value' => variable_get("enable-$tid", 0), | 
| pierre@0 | 75       '#title' => t('Enabled'), | 
| pierre@0 | 76       '#description' => t('If enabled, each ad in this group will be weighted per the percentages defined below.'), | 
| pierre@0 | 77     ); | 
| pierre@0 | 78 | 
| pierre@0 | 79     $result = db_query('SELECT nid FROM {term_node} WHERE tid = %d', $group->tid); | 
| pierre@0 | 80     while ($nid = db_fetch_object($result)) { | 
| pierre@0 | 81       $ad = node_load($nid->nid); | 
| pierre@0 | 82       $percent = db_fetch_object(db_query('SELECT * FROM {ad_weight_percent} WHERE tid = %d AND aid = %d', $tid, $nid->nid)); | 
| pierre@0 | 83       $form["group-$tid"]["ad-$tid"]["$tid-$nid->nid"] = array( | 
| pierre@0 | 84         '#type' => 'fieldset', | 
| pierre@0 | 85         '#title' => $ad->title, | 
| pierre@0 | 86         '#collapsible' => TRUE, | 
| pierre@0 | 87       ); | 
| pierre@0 | 88       $form["group-$tid"]["ad-$tid"]["$tid-$nid->nid"]["ad-$tid-$nid->nid"] = array( | 
| pierre@0 | 89         '#type' => 'markup', | 
| pierre@0 | 90         '#prefix' => '<div>', | 
| pierre@0 | 91         '#suffix' => '</div>', | 
| pierre@0 | 92         '#value' => "$ad->ad<br />$ad->url", | 
| pierre@0 | 93       ); | 
| pierre@0 | 94       $form["group-$tid"]["ad-$tid"]["$tid-$nid->nid"]["percent-$tid-$nid->nid"] = array( | 
| pierre@0 | 95         '#type' => 'textfield', | 
| pierre@0 | 96         '#title' => t('Display percent'), | 
| pierre@0 | 97         '#default_value' => $percent->weight, | 
| pierre@0 | 98         '#size' => 2, | 
| pierre@0 | 99         '#maxlength' => 3, | 
| pierre@0 | 100         '#description' => t("Enter a percentage from 0 to 100.  The total percentages of all ads in this group must add up to 100.  For example, if you have two ads, and want one to be displayed 70% of the time and the other 30% of the time enter '70' in one and '30' in the other."), | 
| pierre@0 | 101       ); | 
| pierre@0 | 102     } | 
| pierre@0 | 103 | 
| pierre@0 | 104   } | 
| pierre@0 | 105 | 
| pierre@0 | 106   $form['submit'] = array( | 
| pierre@0 | 107     '#type' => 'submit', | 
| pierre@0 | 108     '#value' => t('Submit'), | 
| pierre@0 | 109   ); | 
| pierre@0 | 110 | 
| pierre@0 | 111   return $form; | 
| pierre@0 | 112 } | 
| pierre@0 | 113 | 
| pierre@0 | 114 /** | 
| pierre@0 | 115  * Be sure that all enabled groups add up to a total of 100%. | 
| pierre@0 | 116  */ | 
| pierre@0 | 117 function ad_weight_percent_settings_validate($form, &$form_state) { | 
| pierre@0 | 118   $groups = module_invoke('ad', 'groups_list', TRUE); | 
| pierre@0 | 119   foreach ($groups as $tid => $group) { | 
| pierre@0 | 120     if ($form_state['values']["enable-$tid"]) { | 
| pierre@0 | 121       $result = db_query('SELECT nid FROM {term_node} WHERE tid = %d', $group->tid); | 
| pierre@0 | 122       $total = 0; | 
| pierre@0 | 123       // Add up total percentages for all nids in group, confirm equals 100%. | 
| pierre@0 | 124       $first = 0; | 
| pierre@0 | 125       while ($nid = db_fetch_object($result)) { | 
| pierre@0 | 126         if (!$first) $first = $nid->nid; | 
| pierre@0 | 127         $total = $total + (int)$form_state['values']["percent-$tid-$nid->nid"]; | 
| pierre@0 | 128       } | 
| pierre@0 | 129       // Confirmed that total equals 100%. | 
| pierre@0 | 130       if ($total != 100) { | 
| pierre@0 | 131         form_set_error("percent-$tid-$first", t('The total percentage for all ads in the %group group combined must equal 100%.  It currently equals %percent.', array('%group' => $group->name, '%percent' => "$total%"))); | 
| pierre@0 | 132       } | 
| pierre@0 | 133     } | 
| pierre@0 | 134   } | 
| pierre@0 | 135 } | 
| pierre@0 | 136 | 
| pierre@0 | 137 /** | 
| pierre@0 | 138  * Save the weight percent settings in the database. | 
| pierre@0 | 139  */ | 
| pierre@0 | 140 function ad_weight_percent_settings_submit($form, &$form_state) { | 
| pierre@0 | 141   $groups = module_invoke('ad', 'groups_list', TRUE); | 
| pierre@0 | 142   foreach ($groups as $tid => $group) { | 
| pierre@0 | 143     variable_set("enable-$tid", (int)$form_state['values']["enable-$tid"]); | 
| pierre@0 | 144     db_query('DELETE FROM {ad_weight_percent} WHERE tid = %d', $tid); | 
| pierre@0 | 145     $result = db_query('SELECT nid FROM {term_node} WHERE tid = %d', $group->tid); | 
| pierre@0 | 146     while ($nid = db_fetch_object($result)) { | 
| pierre@0 | 147       db_query('INSERT INTO {ad_weight_percent} (tid, aid, weight) VALUES(%d, %d, %d)', $tid, $nid->nid, (int)$form_state['values']["percent-$tid-$nid->nid"]); | 
| pierre@0 | 148     } | 
| pierre@0 | 149   } | 
| pierre@0 | 150 } | 
| pierre@0 | 151 | 
| pierre@0 | 152 /** | 
| pierre@0 | 153  * Returns the greatest common divisor of an array of integers. | 
| pierre@0 | 154  */ | 
| pierre@0 | 155 function ad_weight_percent_gcd($integers) { | 
| pierre@0 | 156   $gcd = array_shift($integers); | 
| pierre@0 | 157 | 
| pierre@0 | 158   while (!empty($integers)) { | 
| pierre@0 | 159     $gcd = _ad_weight_percent_gcd($gcd, array_shift($integers)); | 
| pierre@0 | 160   } | 
| pierre@0 | 161   return $gcd; | 
| pierre@0 | 162 } | 
| pierre@0 | 163 | 
| pierre@0 | 164 /** | 
| pierre@0 | 165  * Helper function to calculate the greatest common divisor using the Euclidean | 
| pierre@0 | 166  * algorithm (http://en.wikipedia.org/wiki/Euclidean_algorithm). | 
| pierre@0 | 167  */ | 
| pierre@0 | 168 function _ad_weight_percent_gcd($a, $b) { | 
| pierre@0 | 169   if ($b == 0) { | 
| pierre@0 | 170     return $a; | 
| pierre@0 | 171   } | 
| pierre@0 | 172   else { | 
| pierre@0 | 173     return _ad_weight_percent_gcd($b, $a % $b); | 
| pierre@0 | 174   } | 
| pierre@0 | 175 } | 
| pierre@0 | 176 | 
| pierre@0 | 177 /** | 
| pierre@0 | 178  * Ad module's adcacheapi _hook(). | 
| pierre@0 | 179  */ | 
| pierre@0 | 180 /* | 
| pierre@0 | 181 function ad_cache_file_adcacheapi($op, &$node) { | 
| pierre@0 | 182   switch ($op) { | 
| pierre@0 | 183     case 'display_variables': | 
| pierre@0 | 184       $files = variable_get('ad_files', 3); | 
| pierre@0 | 185       $path = file_create_path(); | 
| pierre@0 | 186       return "&f=$files&p=$path"; | 
| pierre@0 | 187     case 'method': | 
| pierre@0 | 188       return array('file' => t('File')); | 
| pierre@0 | 189     case 'description': | 
| pierre@0 | 190       return t('File based caching will usually offer better performance, however, some find it difficult to enable and it may not offer valid statistics if you are using multiple load balanced web servers.'); | 
| pierre@0 | 191     case 'settings': | 
| pierre@0 | 192       $form = array(); | 
| pierre@0 | 193       $form['cache']['file'] = array( | 
| pierre@0 | 194         '#type' => 'fieldset', | 
| pierre@0 | 195         '#title' => t('File cache settings'), | 
| pierre@0 | 196         '#collapsible' => TRUE, | 
| pierre@0 | 197         '#collapsed' => (variable_get('ad_cache', 'none') == 'file') ? FALSE : TRUE, | 
| pierre@0 | 198       ); | 
| pierre@0 | 199       $form['cache']['file']['ad_files'] = array( | 
| pierre@0 | 200         '#type' => 'select', | 
| pierre@0 | 201         '#title' => t('Number of cache files'), | 
| pierre@0 | 202         '#default_value' => variable_get('ad_files', 3), | 
| pierre@0 | 203         '#options' => drupal_map_assoc(array(1, 3, 5, 10, 15)), | 
| pierre@0 | 204         '#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.  This configuration option is only relevant if the file cache is enabled.') | 
| pierre@0 | 205       ); | 
| pierre@0 | 206       $period = drupal_map_assoc(array(15,30,60,600,1800,3600,21600,43200,86400), 'format_interval'); | 
| pierre@0 | 207       $form['cache']['file']['ad_cache_file_lifetime'] = array( | 
| pierre@0 | 208         '#type' => 'select', | 
| pierre@0 | 209         '#title' => t('Cache lifetime'), | 
| pierre@0 | 210         '#default_value' => variable_get('ad_cache_file_lifetime', 60), | 
| pierre@0 | 211         '#options' => $period, | 
| pierre@0 | 212         '#description' => t('Specify how long information should be cached before ad statistics are updated in the database.  Increasing the cache lifetime can improve overall performance.  This configuration options is only relevant if the file cache is enabled.'), | 
| pierre@0 | 213       ); | 
| pierre@0 | 214       return $form; | 
| pierre@0 | 215     case 'settings_submit': | 
| pierre@0 | 216       variable_set('ad_cache_file_lifetime', $node['ad_cache_file_lifetime']); | 
| pierre@0 | 217       if ($node['ad_cache'] != 'file') { | 
| pierre@0 | 218         ad_cache_file_build(0, variable_get('ad_files', 3)); | 
| pierre@0 | 219       } | 
| pierre@0 | 220       else { | 
| pierre@0 | 221         ad_cache_file_build($node['ad_files'], variable_get('ad_files', 3)); | 
| pierre@0 | 222       } | 
| pierre@0 | 223       variable_set('ad_files', $node['ad_files']); | 
| pierre@0 | 224       break; | 
| pierre@0 | 225 | 
| pierre@0 | 226     case 'insert': | 
| pierre@0 | 227     case 'update': | 
| pierre@0 | 228     case 'delete': | 
| pierre@0 | 229       if (variable_get('ad_cache', 'none') == 'file') { | 
| pierre@0 | 230         ad_cache_file_build(); | 
| pierre@0 | 231       } | 
| pierre@0 | 232       break; | 
| pierre@0 | 233   } | 
| pierre@0 | 234 } | 
| pierre@0 | 235 */ | 
| pierre@0 | 236 |