/** * A method to determine the day/hour that a placement first became active, * based on the first record of its children ads delivering. * * @param integer $placementId The placement ID. * @return mixed PEAR:Error on database error, null on no result, or a * PEAR::Date object representing the time the placement started * delivery, or, if not yet active, the current date/time. */ function getPlacementFirstStatsDate($placementId) { // Test the input values if (!is_numeric($placementId)) { return null; } // Get the required data $conf = $GLOBALS['_MAX']['CONF']; $adTable = $this->oDbh->quoteIdentifier($conf['table']['prefix'] . $conf['table']['banners'], true); $dsahTable = $this->oDbh->quoteIdentifier($conf['table']['prefix'] . $conf['table']['data_summary_ad_hourly'], true); $query = "\n SELECT\n DATE_FORMAT(dsah.date_time, '%Y-%m-%d') AS day,\n HOUR(dsah.date_time) AS hour\n FROM\n {$adTable} AS a,\n {$dsahTable} AS dsah\n WHERE\n a.campaignid = " . $this->oDbh->quote($placementId, 'integer') . "\n AND\n a.bannerid = dsah.ad_id\n ORDER BY\n day ASC, hour ASC\n LIMIT 1"; $message = "Finding start date of placement ID {$placementId} based on delivery statistics."; OA::debug($message, PEAR_LOG_DEBUG); $rc = $this->oDbh->query($query); if (PEAR::isError($rc)) { return $rc; } // Was a result found? if ($rc->numRows() == 0) { // Return the current time $oDate = new Date(); } else { // Store the results $aRow = $rc->fetchRow(); $oDate = new Date($aRow['day'] . ' ' . $aRow['hour'] . ':00:00'); } return $oDate; }
function run() { // Make sure that the output is sent to the browser before // loading libraries and connecting to the db flush(); $aConf = $GLOBALS['_MAX']['CONF']; // Set longer time out, and ignore user abort if (!ini_get('safe_mode')) { @set_time_limit($aConf['maintenance']['timeLimitScripts']); @ignore_user_abort(true); } if (!defined('OA_VERSION')) { // If the code is executed inside delivery, the constants // need to be initialized require_once MAX_PATH . '/constants.php'; setupConstants(); } $oLock =& OA_DB_AdvisoryLock::factory(); if ($oLock->get(OA_DB_ADVISORYLOCK_MAINTENANCE)) { OA::debug('Running Automatic Maintenance Task', PEAR_LOG_INFO); OA_Preferences::loadAdminAccountPreferences(); require_once LIB_PATH . '/Maintenance.php'; $oMaint = new OX_Maintenance(); $oMaint->run(); $oLock->release(); OA::debug('Automatic Maintenance Task Completed', PEAR_LOG_INFO); } else { OA::debug('Automatic Maintenance Task not run: could not acquire lock', PEAR_LOG_INFO); } }
function defaultData() { $oManager = new OX_Plugin_ComponentGroupManager(); if (!array_key_exists('testPlugin', $GLOBALS['_MAX']['CONF']['pluginGroupComponents'])) { $oManager->disableComponentGroup('testPlugin'); } $this->oManager->enableComponentGroup('testPlugin'); $oTestPluginTable = OA_Dal::factoryDO('testplugin_table'); if (!$oTestPluginTable) { OA::debug('Failed to instantiate DataObject for testplugin_table'); return false; } $oTestPluginTable->myplugin_desc = 'Hello World'; $aSettings[0]['data'] = $oTestPluginTable->insert(); $aSettings[0]['section'] = 'myPlugin'; $aSettings[0]['key'] = 'english'; $oTestPluginTable->myplugin_desc = 'Hola Mundo'; $aSettings[1]['data'] = $oTestPluginTable->insert(); $aSettings[1]['section'] = 'myPlugin'; $aSettings[1]['key'] = 'spanish'; $oTestPluginTable->myplugin_desc = 'Look Simon, you\'re just making it up now'; $aSettings[2]['data'] = $oTestPluginTable->insert(); $aSettings[2]['section'] = 'myPlugin'; $aSettings[2]['key'] = 'russian'; $oManager->_registerSettings($aSettings); $oManager->disableComponentGroup('testPlugin'); return true; }
/** * A private method for summarising data into the final tables when * at least one hour is complete. * * @access private * @param PEAR::Date $oStartDate The start date of the complete hour(s). * @param PEAR::Date $oEndDate The end date of the complete hour(s). */ function _saveSummary($oStartDate, $oEndDate) { $message = '- Updating the data_summary_ad_hourly table for data after ' . $oStartDate->format('%Y-%m-%d %H:%M:%S') . ' ' . $oStartDate->tz->getShortName(); $this->oController->report .= $message . ".\n"; OA::debug($message, PEAR_LOG_DEBUG); $oServiceLocator =& OA_ServiceLocator::instance(); $oDal =& $oServiceLocator->get('OX_Dal_Maintenance_Statistics'); $aTypes = array('types' => array(0 => 'request', 1 => 'impression', 2 => 'click'), 'connections' => array(1 => MAX_CONNECTION_AD_IMPRESSION, 2 => MAX_CONNECTION_AD_CLICK)); $oDal->saveSummary($oStartDate, $oEndDate, $aTypes, 'data_intermediate_ad', 'data_summary_ad_hourly'); }
/** * A method to run the run() method of each task in the collection, * in the registered order. * * @todo We should really make OA_Task::run return a boolean we can check. */ function runTasks() { // Remove tasks from the queue and unset them when done to prevent // useless memory consumption while ($oTask = array_shift($this->aTasks)) { OA::debug('Task begin: ' . get_class($oTask), PEAR_LOG_INFO); $oTask->run(); OA::debug('Task complete: ' . get_class($oTask), PEAR_LOG_INFO); unset($oTask); } }
/** * The implementation of the OA_Task::run() method that performs * the required task of activating/deactivating campaigns. */ function run() { if ($this->oController->updateIntermediate) { $oServiceLocator =& OA_ServiceLocator::instance(); $oDate =& $oServiceLocator->get('now'); $oDal =& $oServiceLocator->get('OX_Dal_Maintenance_Statistics'); $message = '- Managing (activating/deactivating) campaigns'; $this->oController->report .= "{$message}.\n"; OA::debug($message); $this->report .= $oDal->manageCampaigns($oDate); } }
/** * A method to prune a bucket of all records up to and * including the timestamp given. * * @param Date $oEnd Prune until this interval_start (inclusive). * @param Date $oStart Only prune before this interval_start date (inclusive) * as well. Optional. * @return mixed Either the number of rows pruned, or an MDB2_Error objet. */ public function pruneBucket($oBucket, $oEnd, $oStart = null) { $sTableName = $oBucket->getBucketTableName(); if (!is_null($oStart)) { OA::debug(' - Pruning the ' . $sTableName . ' table for data with operation interval start between ' . $oStart->format('%Y-%m-%d %H:%M:%S') . ' ' . $oStart->tz->getShortName() . ' and ' . $oEnd->format('%Y-%m-%d %H:%M:%S') . ' ' . $oEnd->tz->getShortName(), PEAR_LOG_DEBUG); } else { OA::debug(' - Pruning the ' . $sTableName . ' table for all data with operation interval start equal to or before ' . $oEnd->format('%Y-%m-%d %H:%M:%S') . ' ' . $oEnd->tz->getShortName(), PEAR_LOG_DEBUG); } $query = "\n DELETE FROM\n {$sTableName}\n WHERE\n interval_start <= " . DBC::makeLiteral($oEnd->format('%Y-%m-%d %H:%M:%S')); if (!is_null($oStart)) { $query .= "\n AND\n interval_start >= " . DBC::makeLiteral($oStart->format('%Y-%m-%d %H:%M:%S')); } $oDbh = OA_DB::singleton(); return $oDbh->exec($query); }
/** * A method to invoke errors. * * @static * @param mixed $message A string error message, or a {@link PEAR_Error} object. * @param integer $type A custom message code - see the {@link setupConstants()} function. * @param integer $behaviour Optional behaviour (i.e. PEAR_ERROR_DIE to halt on this error). * @return PEAR_Error $error A (@link PEAR_Error} object. */ function raiseError($message, $type = null, $behaviour = null) { // If fatal if ($behaviour == PEAR_ERROR_DIE) { // Log fatal message here as execution will stop $errorType = MAX::errorConstantToString($type); if (!is_string($message)) { $message = print_r($message, true); } OA::debug($type . ' :: ' . $message, PEAR_LOG_EMERG); exit; } $error = PEAR::raiseError($message, $type, $behaviour); return $error; }
/** * A method to derive the class name to instantiate. * * @return string The name of the class object to create. */ function deriveClassName() { $aConf = $GLOBALS['_MAX']['CONF']; $filename = ucfirst(strtolower($aConf['database']['type'])); $classname = 'OX_Dal_Maintenance_Statistics_' . $filename; $includeFile = LIB_PATH . "/Dal/Maintenance/Statistics/{$filename}.php"; require_once $includeFile; if (!class_exists($classname)) { // Unable to include the specified class file - raise error and halt OA::debug('Unable to find the "' . $classname . '" class in the "' . $includeFile . '" file.', PEAR_LOG_ERR); OA::debug('Aborting script execution', PEAR_LOG_ERR); exit; } return $classname; }
/** * A method to run distributed maintenance. */ function run() { if (empty($GLOBALS['_MAX']['CONF']['lb']['enabled'])) { OA::debug('Distributed stats disabled, not running Maintenance Distributed Engine', PEAR_LOG_INFO); return; } if (!empty($GLOBALS['_MAX']['CONF']['rawDatabase'])) { $GLOBALS['_MAX']['CONF']['database'] = $GLOBALS['_MAX']['CONF']['rawDatabase'] + $GLOBALS['_MAX']['CONF']['database']; OA::debug('rawDatabase functionality is being used, switching settings', PEAR_LOG_INFO); } $oLock =& OA_DB_AdvisoryLock::factory(); if (!$oLock->get(OA_DB_ADVISORYLOCK_DISTRIBUTED)) { OA::debug('Maintenance Distributed Engine Already Running', PEAR_LOG_INFO); return; } OA::debug('Running Maintenance Distributed Engine', PEAR_LOG_INFO); // Attempt to increase PHP memory OX_increaseMemoryLimit(OX_getMinimumRequiredMemory('maintenance')); // Ensure the current time is registered with the OA_ServiceLocator $oServiceLocator =& OA_ServiceLocator::instance(); $oNow =& $oServiceLocator->get('now'); if (!$oNow) { // Record the current time, and register with the OA_ServiceLocator $oNow = new Date(); $oServiceLocator->register('now', $oNow); } OA::debug(' - Current time is ' . $oNow->format('%Y-%m-%d %H:%M:%S') . ' ' . $oNow->tz->getShortName(), PEAR_LOG_DEBUG); // Get the components of the deliveryLog extension $aBuckets = OX_Component::getComponents('deliveryLog'); // Copy buckets' records with "interval_start" up to and including previous OI start, // and then prune the data processed $aPreviousOperationIntervalDates = OX_OperationInterval::convertDateToPreviousOperationIntervalStartAndEndDates($oNow); OA::debug(' - Will process data for all operation intervals before and up to start', PEAR_LOG_DEBUG); OA::debug(' time of ' . $aPreviousOperationIntervalDates['start']->format('%Y-%m-%d %H:%M:%S') . ' ' . $aPreviousOperationIntervalDates['start']->tz->getShortName(), PEAR_LOG_DEBUG); foreach ($aBuckets as $sBucketName => $oBucketClass) { if ($oBucketClass->testStatisticsMigration($oBucketClass->getStatisticsMigration())) { $oBucketClass->processBucket($aPreviousOperationIntervalDates['start']); $oBucketClass->pruneBucket($aPreviousOperationIntervalDates['start']); } else { OA::debug(' - Skipping ' . $sBucketName, PEAR_LOG_DEBUG); } } $oLock->release(); OA::debug('Maintenance Distributed Engine Completed', PEAR_LOG_INFO); }
/** * Returns authentication plugin * * @static * @param string $authType * @return Plugins_Authentication */ static function staticGetAuthPlugin() { static $authPlugin; static $authPluginType; if (!isset($authPlugin) || $authPluginType != $authType) { $aConf = $GLOBALS['_MAX']['CONF']; if (!empty($aConf['authentication']['type'])) { $authType = $aConf['authentication']['type']; $authPlugin = OX_Component::factoryByComponentIdentifier($authType); } if (!$authPlugin) { // Fall back to internal $authType = 'none'; $authPlugin = new Plugins_Authentication(); } if (!$authPlugin) { OA::debug('Error while including authentication plugin and unable to fallback', PEAR_LOG_ERR); } $authPluginType = $authType; } return $authPlugin; }
/** * A method to read and parse the configuration file for a plugin. * * @static * @param string $module The plugin module name (i.e. /plugins/module directory). * @param string $package An optional plugin package name (i.e. /plugins/module/package * directory). If not given, the module level configuration file * will be read. * @param string $name An optional plugin name (i.e. /plugins/module/package/plugin.plugin.php). * If not given, the package level configuration file will be read, or * the module level configuration file will be read, depending on the * $package parameter. * @param boolean $processSections Process section names in the configuration file, and return * as a multidimenstional array. * {@see http://uk.php.net/manual/en/function.parse-ini-file.php}. * @param boolean $copyDefaultIfNotExists If true, copy the default configuration file for the * plugin to the real configuration file location, if the * real configuration file does not (yet) exist. * @return mixed An array containing the parsed configuration file, or false on error. * */ function getConfig($module, $package = null, $name = null, $processSections = false, $copyDefaultIfNotExists = true) { // First lets see if the config is saved in our global config file $conf = isset($GLOBALS['_MAX']['CONF'][$module]) ? $GLOBALS['_MAX']['CONF'][$module] : false; if (!empty($package)) { $conf = isset($conf[$package]) ? $conf[$package] : false; } // Try plugin config if ($conf === false) { $configFileName = MAX_Plugin::getConfigFileName($module, $package, $name); $conf = MAX_Plugin::getConfigByFileName($configFileName, $processSections, false); } if ($conf !== false) { return $conf; } if ($copyDefaultIfNotExists) { MAX_Plugin::copyDefaultConfig($module, $package, $name); $defaultConfigFileName = MAX_Plugin::getConfigFileName($module, $package, $name, true); return MAX_Plugin::getConfigByFileName($defaultConfigFileName, $processSections, false); } OA::debug("Config for {$package}/{$module}/{$name} does not exist.", MAX_ERROR_NOFILE); return false; }
/** * A method to reject conversions which variables which are required to be * non-empty, but which are in reality, empty, between the supplied operation * interval start and end dates. * * @param PEAR::Date $oStart The start date/time of the operation interval. * @param PEAR::Date $oEnd The end date/time of the operation interval. */ function rejectEmptyVarConversions($oStart, $oEnd) { $aConf = $GLOBALS['_MAX']['CONF']; $query = "\n UPDATE\n {$aConf['table']['prefix']}{$aConf['table']['data_intermediate_ad_connection']} AS diac\n JOIN\n {$aConf['table']['prefix']}{$aConf['table']['variables']} AS v\n ON\n (\n diac.tracker_id = v.trackerid\n )\n LEFT JOIN\n {$aConf['table']['prefix']}{$aConf['table']['data_intermediate_ad_variable_value']} AS diavv\n ON\n (\n diac.data_intermediate_ad_connection_id = diavv.data_intermediate_ad_connection_id\n AND\n v.variableid = diavv.tracker_variable_id\n )\n SET\n diac.connection_status = " . MAX_CONNECTION_STATUS_DISAPPROVED . ",\n diac.updated = '" . OA::getNow() . "',\n diac.comments = CONCAT('Rejected because ', COALESCE(NULLIF(v.description, ''), v.name), ' is empty')\n WHERE\n diac.tracker_date_time >= " . $this->oDbh->quote($oStart->format('%Y-%m-%d %H:%M:%S'), 'timestamp') . "\n AND\n diac.tracker_date_time <= " . $this->oDbh->quote($oEnd->format('%Y-%m-%d %H:%M:%S'), 'timestamp') . "\n AND\n diac.inside_window = 1\n AND\n v.reject_if_empty = 1\n AND\n (diavv.value IS NULL OR diavv.value = '')\n "; $message = '- Rejecting conversions with empty required variables between ' . $oStart->format('%Y-%m-%d %H:%M:%S') . ' ' . $oStart->tz->getShortName() . ' and ' . $oEnd->format('%Y-%m-%d %H:%M:%S') . ' ' . $oEnd->tz->getShortName(); OA::debug($message, PEAR_LOG_DEBUG); $rows = $this->oDbh->exec($query); if (PEAR::isError($rows)) { return MAX::raiseError($rows, MAX_ERROR_DBFAILURE, PEAR_ERROR_DIE); } }
/** * A method to activate/deactivate campaigns, based on the date and/or the inventory * requirements (impressions, clicks and/or conversions). Also sends email reports * for any campaigns that are activated/deactivated, as well as sending email reports * for any campaigns that are likely to expire in the near future. * * @param Date $oDate The current date/time. * @return string Report on the campaigns activated/deactivated. */ function manageCampaigns($oDate) { $aConf = $GLOBALS['_MAX']['CONF']; $oServiceLocator =& OA_ServiceLocator::instance(); $oEmail =& $oServiceLocator->get('OA_Email'); if ($oEmail === false) { $oEmail = new OA_Email(); $oServiceLocator->register('OA_Email', $oEmail); } $report = "\n"; // Select all campaigns in the system, where: // The campaign is ACTIVE and: // - The end date stored for the campaign is not null; or // - The campaign has a lifetime impression, click or conversion // target set. // // That is: // - It is possible for the active campaign to be automatically // stopped, as it has a valid end date. (No limitations are // applied to those campaigns tested, as the ME may not have // run for a while, and if so, even campaigns with an end date // of many, many weeks ago should be tested to ensure they are // [belatedly] halted.) // - It is possible for the active campaign to be automatically // stopped, as it has at leaast one lifetime target that could // have been reached. // // The campaign is INACTIVE and: // - The start date stored for the campaign is not null; and // - The weight is greater than zero; and // - The end date stored for the campaign is either null, or is // greater than "today" less one day. // // That is: // - It is possible for the inactive campaign to be automatically // started, as it has a valid start date. (No limitations are // applied to those campaigns tested, as the ME may not have run // for a while, and if so, even campaigns with an activation date // of many, many weeks ago should be tested to ensure they are // [belatedy] enabled.) // - The campaign is not in a permanently inactive state, as a // result of the weight being less then one, which means that // it cannot be activated. // - The test to start the campaign is unlikely to fail on account // of the end date. (Inactive campaigns with start dates may have // passed the start date, but they may also have passed the end // date - unfortunately, because the dates are not stored in UTC, // it's not possible to know exactly which campaigns have passed // the end date or not, until the values are converted to UTC based // on the Advertiser Account timezone preference - so it's necessary // to get some campaigns that might be passed the end date, and do // the converstion to UTC and test to check.) $prefix = $this->getTablePrefix(); $oYesterdayDate = new Date(); $oYesterdayDate->copy($oDate); $oYesterdayDate->subtractSeconds(SECONDS_PER_DAY); $query = "\n SELECT\n cl.clientid AS advertiser_id,\n cl.account_id AS advertiser_account_id,\n cl.agencyid AS agency_id,\n cl.contact AS contact,\n cl.email AS email,\n cl.reportdeactivate AS send_activate_deactivate_email,\n ca.campaignid AS campaign_id,\n ca.campaignname AS campaign_name,\n ca.views AS targetimpressions,\n ca.clicks AS targetclicks,\n ca.conversions AS targetconversions,\n ca.status AS status,\n ca.activate AS start,\n ca.expire AS end\n FROM\n {$prefix}campaigns AS ca,\n {$prefix}clients AS cl\n WHERE\n ca.clientid = cl.clientid\n AND\n ca.status = " . $this->oDbh->quote(OA_ENTITY_STATUS_RUNNING, 'integer') . "\n AND\n (\n ca.expire " . OA_Dal::notEqualNoDateString() . "\n OR\n (\n ca.views > 0\n OR\n ca.clicks > 0\n OR\n ca.conversions > 0\n )\n )\n UNION ALL\n SELECT\n cl.clientid AS advertiser_id,\n cl.account_id AS advertiser_account_id,\n cl.agencyid AS agency_id,\n cl.contact AS contact,\n cl.email AS email,\n cl.reportdeactivate AS send_activate_deactivate_email,\n ca.campaignid AS campaign_id,\n ca.campaignname AS campaign_name,\n ca.views AS targetimpressions,\n ca.clicks AS targetclicks,\n ca.conversions AS targetconversions,\n ca.status AS status,\n ca.activate AS start,\n ca.expire AS end\n FROM\n {$prefix}campaigns AS ca,\n {$prefix}clients AS cl\n WHERE\n ca.clientid = cl.clientid\n AND\n ca.status != " . $this->oDbh->quote(OA_ENTITY_STATUS_RUNNING, 'integer') . "\n AND\n ca.activate " . OA_Dal::notEqualNoDateString() . "\n AND\n (\n ca.weight > 0\n OR\n ca.priority > 0\n )\n AND\n (\n ca.expire >= " . $this->oDbh->quote($oYesterdayDate->format('%Y-%m-%d'), 'timestamp') . "\n OR\n ca.expire " . OA_Dal::equalNoDateString() . "\n )\n ORDER BY\n advertiser_id"; $rsResult = $this->oDbh->query($query); if (PEAR::isError($rsResult)) { return MAX::raiseError($rsResult, MAX_ERROR_DBFAILURE, PEAR_ERROR_DIE); } OA::debug('- Found ' . $rsResult->numRows() . ' campaigns to test for activation/deactivation', PEAR_LOG_DEBUG); while ($aCampaign = $rsResult->fetchRow()) { if ($aCampaign['status'] == OA_ENTITY_STATUS_RUNNING) { // The campaign is currently running, look at the campaign $disableReason = 0; $canExpireSoon = false; if ($aCampaign['targetimpressions'] > 0 || $aCampaign['targetclicks'] > 0 || $aCampaign['targetconversions'] > 0) { // The campaign has an impression, click and/or conversion target, // so get the sum total statistics for the campaign $query = "\n SELECT\n SUM(dia.impressions) AS impressions,\n SUM(dia.clicks) AS clicks,\n SUM(dia.conversions) AS conversions\n FROM\n " . $this->oDbh->quoteIdentifier($aConf['table']['prefix'] . $aConf['table']['data_intermediate_ad'], true) . " AS dia,\n " . $this->oDbh->quoteIdentifier($aConf['table']['prefix'] . $aConf['table']['banners'], true) . " AS b\n WHERE\n dia.ad_id = b.bannerid\n AND b.campaignid = {$aCampaign['campaign_id']}"; $rsResultInner = $this->oDbh->query($query); $valuesRow = $rsResultInner->fetchRow(); if (!is_null($valuesRow['impressions']) || !is_null($valuesRow['clicks']) || !is_null($valuesRow['conversions'])) { // There were impressions, clicks and/or conversions for this // campaign, so find out if campaign targets have been passed if (is_null($valuesRow['impressions'])) { // No impressions $valuesRow['impressions'] = 0; } if (is_null($valuesRow['clicks'])) { // No clicks $valuesRow['clicks'] = 0; } if (is_null($valuesRow['conversions'])) { // No conversions $valuesRow['conversions'] = 0; } if ($aCampaign['targetimpressions'] > 0) { if ($aCampaign['targetimpressions'] <= $valuesRow['impressions']) { // The campaign has an impressions target, and this has been // passed, so update and disable the campaign $disableReason |= OX_CAMPAIGN_DISABLED_IMPRESSIONS; } } if ($aCampaign['targetclicks'] > 0) { if ($aCampaign['targetclicks'] <= $valuesRow['clicks']) { // The campaign has a click target, and this has been // passed, so update and disable the campaign $disableReason |= OX_CAMPAIGN_DISABLED_CLICKS; } } if ($aCampaign['targetconversions'] > 0) { if ($aCampaign['targetconversions'] <= $valuesRow['conversions']) { // The campaign has a target limitation, and this has been // passed, so update and disable the campaign $disableReason |= OX_CAMPAIGN_DISABLED_CONVERSIONS; } } if ($disableReason) { // One of the campaign targets was exceeded, so disable $message = '- Exceeded a campaign quota: Deactivating campaign ID ' . "{$aCampaign['campaign_id']}: {$aCampaign['campaign_name']}"; OA::debug($message, PEAR_LOG_INFO); $report .= $message . "\n"; $doCampaigns = OA_Dal::factoryDO('campaigns'); $doCampaigns->campaignid = $aCampaign['campaign_id']; $doCampaigns->find(); $doCampaigns->fetch(); $doCampaigns->status = OA_ENTITY_STATUS_EXPIRED; $result = $doCampaigns->update(); if ($result == false) { return MAX::raiseError($rows, MAX_ERROR_DBFAILURE, PEAR_ERROR_DIE); } phpAds_userlogSetUser(phpAds_userMaintenance); phpAds_userlogAdd(phpAds_actionDeactiveCampaign, $aCampaign['campaign_id']); } else { // The campaign didn't have a diable reason, // it *might* possibly be diabled "soon"... $canExpireSoon = true; } } } // Does the campaign need to be disabled due to the date? if ($aCampaign['end'] != OA_Dal::noDateValue()) { // The campaign has a valid end date, stored in the timezone of the advertiser; // create an end date in the advertiser's timezone, set the time, and then // convert to UTC so that it can be compared with the MSE run time, which is // in UTC $aAdvertiserPrefs = OA_Preferences::loadAccountPreferences($aCampaign['advertiser_account_id'], true); $oTimezone = new Date_Timezone($aAdvertiserPrefs['timezone']); $oEndDate = new Date(); $oEndDate->convertTZ($oTimezone); $oEndDate->setDate($aCampaign['end'] . ' 23:59:59'); // Campaigns end at the end of the day $oEndDate->toUTC(); if ($oDate->after($oEndDate)) { // The end date has been passed; disable the campaign $disableReason |= OX_CAMPAIGN_DISABLED_DATE; $message = "- Passed campaign end time of '{$aCampaign['end']} 23:59:59 {$aAdvertiserPrefs['timezone']} (" . $oEndDate->format('%Y-%m-%d %H:%M:%S') . ' ' . $oEndDate->tz->getShortName() . ")': Deactivating campaign ID {$aCampaign['campaign_id']}: {$aCampaign['campaign_name']}"; OA::debug($message, PEAR_LOG_INFO); $report .= $message . "\n"; $doCampaigns = OA_Dal::factoryDO('campaigns'); $doCampaigns->campaignid = $aCampaign['campaign_id']; $doCampaigns->find(); $doCampaigns->fetch(); $doCampaigns->status = OA_ENTITY_STATUS_EXPIRED; $result = $doCampaigns->update(); if ($result == false) { return MAX::raiseError($rows, MAX_ERROR_DBFAILURE, PEAR_ERROR_DIE); } phpAds_userlogSetUser(phpAds_userMaintenance); phpAds_userlogAdd(phpAds_actionDeactiveCampaign, $aCampaign['campaign_id']); } else { // The campaign wasn't disabled based on the end // date, to it *might* possibly be disabled "soon"... $canExpireSoon = true; } } if ($disableReason) { // The campaign was disabled, so send the appropriate // message to the campaign's contact $query = "\n SELECT\n bannerid AS advertisement_id,\n description AS description,\n alt AS alt,\n url AS url\n FROM\n " . $this->oDbh->quoteIdentifier($aConf['table']['prefix'] . $aConf['table']['banners'], true) . "\n WHERE\n campaignid = {$aCampaign['campaign_id']}"; OA::debug("- Getting the advertisements for campaign ID {$aCampaign['campaign_id']}", PEAR_LOG_DEBUG); $rsResultAdvertisement = $this->oDbh->query($query); if (PEAR::isError($rsResultAdvertisement)) { return MAX::raiseError($rsResultAdvertisement, MAX_ERROR_DBFAILURE, PEAR_ERROR_DIE); } while ($advertisementRow = $rsResultAdvertisement->fetchRow()) { $advertisements[$advertisementRow['advertisement_id']] = array($advertisementRow['description'], $advertisementRow['alt'], $advertisementRow['url']); } if ($aCampaign['send_activate_deactivate_email'] == 't') { $oEmail->sendCampaignActivatedDeactivatedEmail($aCampaign['campaign_id'], $disableReason); } } else { if ($canExpireSoon) { // The campaign has NOT been deactivated - test to see if it will // be deactivated "soon", and send email(s) warning of this as required $oEmail->sendCampaignImpendingExpiryEmail($oDate, $aCampaign['campaign_id']); } } } else { // The campaign is not active - does it need to be enabled, // based on the campaign starting date? if ($aCampaign['start'] != OA_Dal::noDateValue()) { // The campaign has a valid start date, stored in the timezone of the advertiser; // create an end date in the advertiser's timezone, set the time, and then // convert to UTC so that it can be compared with the MSE run time, which is // in UTC $aAdvertiserPrefs = OA_Preferences::loadAccountPreferences($aCampaign['advertiser_account_id'], true); $oTimezone = new Date_Timezone($aAdvertiserPrefs['timezone']); $oStartDate = new Date(); $oStartDate->convertTZ($oTimezone); $oStartDate->setDate($aCampaign['start'] . ' 00:00:00'); // Campaigns start at the start of the day $oStartDate->toUTC(); if ($aCampaign['end'] != OA_Dal::noDateValue()) { // The campaign has a valid end date, stored in the timezone of the advertiser; // create an end date in the advertiser's timezone, set the time, and then // convert to UTC so that it can be compared with the MSE run time, which is // in UTC $oEndDate = new Date(); $oEndDate->convertTZ($oTimezone); $oEndDate->setDate($aCampaign['end'] . ' 23:59:59'); // Campaign end at the end of the day $oEndDate->toUTC(); } else { $oEndDate = null; } if ($oDate->after($oStartDate)) { // The start date has been passed; find out if there are any impression, click // or conversion targets for the campaign (i.e. if the target values are > 0) $remainingImpressions = 0; $remainingClicks = 0; $remainingConversions = 0; if ($aCampaign['targetimpressions'] > 0 || $aCampaign['targetclicks'] > 0 || $aCampaign['targetconversions'] > 0) { // The campaign has an impression, click and/or conversion target, // so get the sum total statistics for the campaign so far $query = "\n SELECT\n SUM(dia.impressions) AS impressions,\n SUM(dia.clicks) AS clicks,\n SUM(dia.conversions) AS conversions\n FROM\n " . $this->oDbh->quoteIdentifier($aConf['table']['prefix'] . $aConf['table']['data_intermediate_ad'], true) . " AS dia,\n " . $this->oDbh->quoteIdentifier($aConf['table']['prefix'] . $aConf['table']['banners'], true) . " AS b\n WHERE\n dia.ad_id = b.bannerid\n AND b.campaignid = {$aCampaign['campaign_id']}"; $rsResultInner = $this->oDbh->query($query); $valuesRow = $rsResultInner->fetchRow(); // Set the remaining impressions, clicks and conversions for the campaign $remainingImpressions = $aCampaign['targetimpressions'] - $valuesRow['impressions']; $remainingClicks = $aCampaign['targetclicks'] - $valuesRow['clicks']; $remainingConversions = $aCampaign['targetconversions'] - $valuesRow['conversions']; } // In order for the campaign to be activated, need to test: // 1) That there is no impression target (<= 0), or, if there is an impression target (> 0), // then there must be remaining impressions to deliver (> 0); and // 2) That there is no click target (<= 0), or, if there is a click target (> 0), // then there must be remaining clicks to deliver (> 0); and // 3) That there is no conversion target (<= 0), or, if there is a conversion target (> 0), // then there must be remaining conversions to deliver (> 0); and // 4) Either there is no end date, or the end date has not been passed if (($aCampaign['targetimpressions'] <= 0 || $aCampaign['targetimpressions'] > 0 && $remainingImpressions > 0) && ($aCampaign['targetclicks'] <= 0 || $aCampaign['targetclicks'] > 0 && $remainingClicks > 0) && ($aCampaign['targetconversions'] <= 0 || $aCampaign['targetconversions'] > 0 && $remainingConversions > 0) && (is_null($oEndDate) || $oEndDate->format('%Y-%m-%d') != OA_Dal::noDateValue() && Date::compare($oDate, $oEndDate) < 0)) { $message = "- Passed campaign start time of '{$aCampaign['start']} 00:00:00 {$aAdvertiserPrefs['timezone']} (" . $oStartDate->format('%Y-%m-%d %H:%M:%S') . ' ' . $oStartDate->tz->getShortName() . ")': Activating campaign ID {$aCampaign['campaign_id']}: {$aCampaign['campaign_name']}"; OA::debug($message, PEAR_LOG_INFO); $report .= $message . "\n"; $doCampaigns = OA_Dal::factoryDO('campaigns'); $doCampaigns->campaignid = $aCampaign['campaign_id']; $doCampaigns->find(); $doCampaigns->fetch(); $doCampaigns->status = OA_ENTITY_STATUS_RUNNING; $result = $doCampaigns->update(); if ($result == false) { return MAX::raiseError($rows, MAX_ERROR_DBFAILURE, PEAR_ERROR_DIE); } phpAds_userlogSetUser(phpAds_userMaintenance); phpAds_userlogAdd(phpAds_actionActiveCampaign, $aCampaign['campaign_id']); // Get the advertisements associated with the campaign $query = "\n SELECT\n bannerid AS advertisement_id,\n description AS description,\n alt AS alt,\n url AS url\n FROM\n " . $this->oDbh->quoteIdentifier($aConf['table']['prefix'] . $aConf['table']['banners'], true) . "\n WHERE\n campaignid = {$aCampaign['campaign_id']}"; OA::debug("- Getting the advertisements for campaign ID {$aCampaign['campaign_id']}", PEAR_LOG_DEBUG); $rsResultAdvertisement = $this->oDbh->query($query); if (PEAR::isError($rsResultAdvertisement)) { return MAX::raiseError($rsResultAdvertisement, MAX_ERROR_DBFAILURE, PEAR_ERROR_DIE); } while ($advertisementRow = $rsResultAdvertisement->fetchRow()) { $advertisements[$advertisementRow['advertisement_id']] = array($advertisementRow['description'], $advertisementRow['alt'], $advertisementRow['url']); } if ($aCampaign['send_activate_deactivate_email'] == 't') { $oEmail->sendCampaignActivatedDeactivatedEmail($aCampaign['campaign_id']); } } } } } } }
/** * Returns a child with a given id. If user have no access to this section * or if the child does not exists null is returned * * @param string $sectionId * @return OA_Admin_Menu_Section */ function &get($sectionId, $checkAccess = true) { if (!isset($this->aSectionsMap[$sectionId])) { $errMsg = "MenuSection::get() Cannot get section. No such section with id '" . $sectionId . "'"; OA::debug($errMsg, PEAR_LOG_WARNING); return null; } $oChildSection =& $this->aSectionsMap[$sectionId]; if ($checkAccess) { if (!$oChildSection->check()) { $oChildSection = null; } } return $oChildSection; }
/** * Add the fields which require calculations * * @param array Row of stats */ function summarizeStats(&$row) { OA::debug('Cannot run abstract method'); exit; }
/** * A method to activate/deactivate campaigns, based on the date and/or the inventory * requirements (impressions, clicks and/or conversions). Also sends email reports * for any campaigns that are activated/deactivated, as well as sending email reports * for any campaigns that are likely to expire in the near future. * * @param Date $oDate The current date/time. * @return string Report on the campaigns activated/deactivated. */ function manageCampaigns($oDate) { $aConf = $GLOBALS['_MAX']['CONF']; $oServiceLocator =& OA_ServiceLocator::instance(); $oEmail =& $oServiceLocator->get('OA_Email'); if ($oEmail === false) { $oEmail = new OA_Email(); $oServiceLocator->register('OA_Email', $oEmail); } $report = "\n"; // Select all campaigns in the system, where: // The campaign is ACTIVE and: // - The end date stored for the campaign is not null; or // - The campaign has a lifetime impression, click or conversion // target set. // // That is: // - It is possible for the active campaign to be automatically // stopped, as it has a valid end date. (No limitations are // applied to those campaigns tested, as the ME may not have // run for a while, and if so, even campaigns with an end date // of many, many weeks ago should be tested to ensure they are // [belatedly] halted.) // - It is possible for the active campaign to be automatically // stopped, as it has at leaast one lifetime target that could // have been reached. // // The campaign is INACTIVE and: // - The start date stored for the campaign is not null; and // - The weight is greater than zero; and // - The end date stored for the campaign is either null, or is // greater than "today" less one day. // // That is: // - It is possible for the inactive campaign to be automatically // started, as it has a valid start date. (No limitations are // applied to those campaigns tested, as the ME may not have run // for a while, and if so, even campaigns with an activation date // of many, many weeks ago should be tested to ensure they are // [belatedy] enabled.) // - The campaign is not in a permanently inactive state, as a // result of the weight being less then one, which means that // it cannot be activated. // - The test to start the campaign is unlikely to fail on account // of the end date. $prefix = $this->getTablePrefix(); $oNowDate = new Date($oDate); $oNowDate->toUTC(); $query = "\n SELECT\n cl.clientid AS advertiser_id,\n cl.account_id AS advertiser_account_id,\n cl.agencyid AS agency_id,\n cl.contact AS contact,\n cl.email AS email,\n cl.reportdeactivate AS send_activate_deactivate_email,\n ca.campaignid AS campaign_id,\n ca.campaignname AS campaign_name,\n ca.views AS targetimpressions,\n ca.clicks AS targetclicks,\n ca.conversions AS targetconversions,\n ca.status AS status,\n ca.activate_time AS start,\n ca.expire_time AS end\n FROM\n {$prefix}campaigns AS ca,\n {$prefix}clients AS cl\n WHERE\n ca.clientid = cl.clientid\n AND\n ((\n ca.status = " . $this->oDbh->quote(OA_ENTITY_STATUS_RUNNING, 'integer') . " AND\n (\n ca.expire_time IS NOT NULL\n OR\n (\n ca.views > 0\n OR\n ca.clicks > 0\n OR\n ca.conversions > 0\n )\n )\n ) OR (\n ca.status = " . $this->oDbh->quote(OA_ENTITY_STATUS_AWAITING, 'integer') . " AND\n (\n ca.activate_time <= " . $this->oDbh->quote($oNowDate->getDate(DATE_FORMAT_ISO), 'timestamp') . "\n AND\n (\n ca.weight > 0\n OR\n ca.priority > 0\n )\n AND\n (\n ca.expire_time >= " . $this->oDbh->quote($oNowDate->getDate(DATE_FORMAT_ISO), 'timestamp') . "\n OR\n ca.expire_time IS NULL\n )\n )\n ))\n ORDER BY\n advertiser_id"; OA::debug('- Requesting campaigns to test for activation/deactivation', PEAR_LOG_DEBUG); $rsResult = $this->oDbh->query($query); if (PEAR::isError($rsResult)) { return MAX::raiseError($rsResult, MAX_ERROR_DBFAILURE, PEAR_ERROR_DIE); } OA::debug('- Found ' . $rsResult->numRows() . ' campaigns to test for activation/deactivation', PEAR_LOG_DEBUG); while ($aCampaign = $rsResult->fetchRow()) { if ($aCampaign['status'] == OA_ENTITY_STATUS_RUNNING) { // The campaign is currently running, look at the campaign $disableReason = 0; $canExpireSoon = false; if ($aCampaign['targetimpressions'] > 0 || $aCampaign['targetclicks'] > 0 || $aCampaign['targetconversions'] > 0) { OA::debug(' - Selecting impressions, clicks and conversions for this running campaign ID = ' . $aCampaign['campaign_id'], PEAR_LOG_DEBUG); // The campaign has an impression, click and/or conversion target, // so get the sum total statistics for the campaign $query = "\n SELECT\n SUM(dia.impressions) AS impressions,\n SUM(dia.clicks) AS clicks,\n SUM(dia.conversions) AS conversions\n FROM\n " . $this->oDbh->quoteIdentifier($aConf['table']['prefix'] . $aConf['table']['data_intermediate_ad'], true) . " AS dia,\n " . $this->oDbh->quoteIdentifier($aConf['table']['prefix'] . $aConf['table']['banners'], true) . " AS b\n WHERE\n dia.ad_id = b.bannerid\n AND b.campaignid = {$aCampaign['campaign_id']}"; $rsResultInner = $this->oDbh->query($query); $valuesRow = $rsResultInner->fetchRow(); if (isset($valuesRow['impressions']) || !is_null($valuesRow['clicks']) || !is_null($valuesRow['conversions'])) { // There were impressions, clicks and/or conversions for this // campaign, so find out if campaign targets have been passed if (!isset($valuesRow['impressions'])) { // No impressions $valuesRow['impressions'] = 0; } if (!isset($valuesRow['clicks'])) { // No clicks $valuesRow['clicks'] = 0; } if (!isset($valuesRow['conversions'])) { // No conversions $valuesRow['conversions'] = 0; } if ($aCampaign['targetimpressions'] > 0) { if ($aCampaign['targetimpressions'] <= $valuesRow['impressions']) { // The campaign has an impressions target, and this has been // passed, so update and disable the campaign $disableReason |= OX_CAMPAIGN_DISABLED_IMPRESSIONS; } } if ($aCampaign['targetclicks'] > 0) { if ($aCampaign['targetclicks'] <= $valuesRow['clicks']) { // The campaign has a click target, and this has been // passed, so update and disable the campaign $disableReason |= OX_CAMPAIGN_DISABLED_CLICKS; } } if ($aCampaign['targetconversions'] > 0) { if ($aCampaign['targetconversions'] <= $valuesRow['conversions']) { // The campaign has a target limitation, and this has been // passed, so update and disable the campaign $disableReason |= OX_CAMPAIGN_DISABLED_CONVERSIONS; } } if ($disableReason) { // One of the campaign targets was exceeded, so disable $message = ' - Exceeded a campaign quota: Deactivating campaign ID ' . "{$aCampaign['campaign_id']}: {$aCampaign['campaign_name']}"; OA::debug($message, PEAR_LOG_INFO); $report .= $message . "\n"; $doCampaigns = OA_Dal::factoryDO('campaigns'); $doCampaigns->campaignid = $aCampaign['campaign_id']; $doCampaigns->find(); $doCampaigns->fetch(); $doCampaigns->status = OA_ENTITY_STATUS_EXPIRED; $result = $doCampaigns->update(); if ($result == false) { return MAX::raiseError($rows, MAX_ERROR_DBFAILURE, PEAR_ERROR_DIE); } phpAds_userlogSetUser(phpAds_userMaintenance); phpAds_userlogAdd(phpAds_actionDeactiveCampaign, $aCampaign['campaign_id']); } else { // The campaign didn't have a diable reason, // it *might* possibly be diabled "soon"... $canExpireSoon = true; } } } // Does the campaign need to be disabled due to the date? if (!empty($aCampaign['end'])) { // The campaign has a valid end date, stored in in UTC $oEndDate = new Date($aCampaign['end']); $oEndDate->setTZByID('UTC'); if ($oDate->after($oEndDate)) { // The end date has been passed; disable the campaign $disableReason |= OX_CAMPAIGN_DISABLED_DATE; $message = " - Passed campaign end time of '" . $oEndDate->getDate() . " UTC" . "': Deactivating campaign ID {$aCampaign['campaign_id']}: {$aCampaign['campaign_name']}"; OA::debug($message, PEAR_LOG_INFO); $report .= $message . "\n"; $doCampaigns = OA_Dal::factoryDO('campaigns'); $doCampaigns->campaignid = $aCampaign['campaign_id']; $doCampaigns->find(); $doCampaigns->fetch(); $doCampaigns->status = OA_ENTITY_STATUS_EXPIRED; $result = $doCampaigns->update(); if ($result == false) { return MAX::raiseError($rows, MAX_ERROR_DBFAILURE, PEAR_ERROR_DIE); } phpAds_userlogSetUser(phpAds_userMaintenance); phpAds_userlogAdd(phpAds_actionDeactiveCampaign, $aCampaign['campaign_id']); } else { // The campaign wasn't disabled based on the end // date, to it *might* possibly be disabled "soon"... $canExpireSoon = true; } } if ($disableReason) { // The campaign was disabled, so send the appropriate // message to the campaign's contact $query = "\n SELECT\n bannerid AS advertisement_id,\n description AS description,\n alt AS alt,\n url AS url\n FROM\n " . $this->oDbh->quoteIdentifier($aConf['table']['prefix'] . $aConf['table']['banners'], true) . "\n WHERE\n campaignid = {$aCampaign['campaign_id']}"; OA::debug(" - Getting the advertisements for campaign ID {$aCampaign['campaign_id']}", PEAR_LOG_DEBUG); $rsResultAdvertisement = $this->oDbh->query($query); if (PEAR::isError($rsResultAdvertisement)) { return MAX::raiseError($rsResultAdvertisement, MAX_ERROR_DBFAILURE, PEAR_ERROR_DIE); } while ($advertisementRow = $rsResultAdvertisement->fetchRow()) { $advertisements[$advertisementRow['advertisement_id']] = array($advertisementRow['description'], $advertisementRow['alt'], $advertisementRow['url']); } if ($aCampaign['send_activate_deactivate_email'] == 't') { OA::debug(" - Sending campaign deactivated email ", PEAR_LOG_DEBUG); $oEmail->sendCampaignActivatedDeactivatedEmail($aCampaign['campaign_id'], $disableReason); // Also send campaignDeliveryEmail for the campaign we just deactivated. $doClients = OA_Dal::staticGetDO('clients', $aCampaign['advertiser_id']); $aAdvertiser = $doClients->toArray(); OA::debug(" - Sending campaign delivery email ", PEAR_LOG_DEBUG); $oStart = new Date($aAdvertiser['reportlastdate']); $oEnd = new Date($oDate); // Set end date to tomorrow so we get stats for today. $oEnd->addSpan(new Date_Span('1-0-0-0')); $oEmail->sendCampaignDeliveryEmail($aAdvertiser, $oStart, $oEnd, $aCampaign['campaign_id']); } } else { if ($canExpireSoon) { // The campaign has NOT been deactivated - test to see if it will // be deactivated "soon", and send email(s) warning of this as required OA::debug(" - Sending campaign 'soon deactivated' email ", PEAR_LOG_DEBUG); $oEmail->sendCampaignImpendingExpiryEmail($oDate, $aCampaign['campaign_id']); } } } elseif (!empty($aCampaign['start'])) { // The campaign is awaiting activation and has a valid start date, stored in UTC $oStartDate = new Date($aCampaign['start']); $oStartDate->setTZByID('UTC'); // Find out if there are any impression, click or conversion targets for // the campaign (i.e. if the target values are > 0) $remainingImpressions = 0; $remainingClicks = 0; $remainingConversions = 0; if ($aCampaign['targetimpressions'] > 0 || $aCampaign['targetclicks'] > 0 || $aCampaign['targetconversions'] > 0) { OA::debug(" - The campaign ID " . $aCampaign['campaign_id'] . " has an impression, click and/or conversion target, requesting impressions so far", PEAR_LOG_DEBUG); $query = "\n SELECT\n SUM(dia.impressions) AS impressions,\n SUM(dia.clicks) AS clicks,\n SUM(dia.conversions) AS conversions\n FROM\n " . $this->oDbh->quoteIdentifier($aConf['table']['prefix'] . $aConf['table']['data_intermediate_ad'], true) . " AS dia,\n " . $this->oDbh->quoteIdentifier($aConf['table']['prefix'] . $aConf['table']['banners'], true) . " AS b\n WHERE\n dia.ad_id = b.bannerid\n AND b.campaignid = {$aCampaign['campaign_id']}"; $rsResultInner = $this->oDbh->query($query); $valuesRow = $rsResultInner->fetchRow(); // Set the remaining impressions, clicks and conversions for the campaign $remainingImpressions = $aCampaign['targetimpressions'] - $valuesRow['impressions']; $remainingClicks = $aCampaign['targetclicks'] - $valuesRow['clicks']; $remainingConversions = $aCampaign['targetconversions'] - $valuesRow['conversions']; } // In order for the campaign to be activated, need to test: // 1) That there is no impression target (<= 0), or, if there is an impression target (> 0), // then there must be remaining impressions to deliver (> 0); and // 2) That there is no click target (<= 0), or, if there is a click target (> 0), // then there must be remaining clicks to deliver (> 0); and // 3) That there is no conversion target (<= 0), or, if there is a conversion target (> 0), // then there must be remaining conversions to deliver (> 0) if (($aCampaign['targetimpressions'] <= 0 || $aCampaign['targetimpressions'] > 0 && $remainingImpressions > 0) && ($aCampaign['targetclicks'] <= 0 || $aCampaign['targetclicks'] > 0 && $remainingClicks > 0) && ($aCampaign['targetconversions'] <= 0 || $aCampaign['targetconversions'] > 0 && $remainingConversions > 0)) { $message = "- Passed campaign start time of '" . $oStartDate->getDate() . " UTC" . "': Activating campaign ID {$aCampaign['campaign_id']}: {$aCampaign['campaign_name']}"; OA::debug($message, PEAR_LOG_INFO); $report .= $message . "\n"; $doCampaigns = OA_Dal::factoryDO('campaigns'); $doCampaigns->campaignid = $aCampaign['campaign_id']; $doCampaigns->find(); $doCampaigns->fetch(); $doCampaigns->status = OA_ENTITY_STATUS_RUNNING; $result = $doCampaigns->update(); if ($result == false) { return MAX::raiseError($rows, MAX_ERROR_DBFAILURE, PEAR_ERROR_DIE); } phpAds_userlogSetUser(phpAds_userMaintenance); phpAds_userlogAdd(phpAds_actionActiveCampaign, $aCampaign['campaign_id']); if ($aCampaign['send_activate_deactivate_email'] == 't') { OA::debug(" - Sending activation email for campaign ID " . $aCampaign['campaign_id'], PEAR_LOG_DEBUG); $oEmail->sendCampaignActivatedDeactivatedEmail($aCampaign['campaign_id']); } } } } }
/** * The underlying private method to getOwningAccountIdsreturn an array of account IDs of the account(s) that * should "own" any audit trail entries for this entity type; these * are NOT related to the account ID of the currently active account * (which is performing some kind of action on the entity), but is * instead related to the type of entity, and where in the account * heirrachy the entity is located. * * Works by locating the "account_id" column in this DB_DataObject, * and converting this into the array of owning account IDs. * * @param string $parentTable The name of another table to look in * for the "account_id" column, if this * DB_DataObject does not have such a column. * @param string $parentKey Name of the key that relates this * DB_DataObject and the parent entity in * $parentTable. * @param boolean $resetCache When true, reset the internal cache and * return null. * * @see DB_DataObjectCommon::getOwningAccountIds() */ protected function _getOwningAccountIds($parentTable = null, $parentKeyName = null, $resetCache = false) { // Use a static cache to store previously calculated owning // account IDs static $aCache = array(); // Reset the cache? if ($resetCache) { $aCache = array(); return; } // Get this DB_DataObject's table name and primary key name $tableName = $this->getTableWithoutPrefix(); $primaryKeyName = $this->getFirstPrimaryKey(); // Is this a call to get the owning account IDs directly from // this DB_DataObject, or do we need to look at a parent? if (is_null($parentTable) && is_null($parentKeyName)) { // Get the directly owning account ID from this DB_DataObject // If the owning account IDs have already been calculated, // return them directly if (!empty($aCache[$tableName][$this->{$primaryKeyName}])) { return $aCache[$tableName][$this->{$primaryKeyName}]; } // Test to ensure that the account ID column exists in this // DB_DataObject $aColumns = $this->table(); if (!isset($aColumns['account_id'])) { $message = "Cannot locate owning account IDs for entity in table '{$tableName}', " . "as the table is not directly linked to an account."; OA::debug($message, PEAR_LOG_ERR); return false; } // Set the directly owning account ID based on the account_id // column value if (empty($this->account_id)) { $doThis = OA_Dal::staticGetDO($tableName, $this->{$primaryKeyName}); if ($doThis != false) { $directOwnerAccountId = $doThis->account_id; } } else { $directOwnerAccountId = $this->account_id; } // Do we have the directly owning account ID? if (empty($directOwnerAccountId)) { $message = "Cannot locate owning account IDs for entity in table '{$tableName}', " . "as the 'account_id' column was empty where column '{$primaryKeyName}' was " . " equal to '{$this->{$primaryKeyName}}'."; OA::debug($message, PEAR_LOG_ERR); return false; } // Convert the directly onwing account ID into an array of owning // account IDs $aResult = $this->_getOwningAccountIdsByAccountId($directOwnerAccountId); // Store the result in the cache array $aCache[$tableName][$this->{$primaryKeyName}] = $aResult; // Return the result return $aCache[$tableName][$this->{$primaryKeyName}]; } else { // Get the directly owning account ID from a parent table // If the owning account IDs have already been calculated, // return them directly if (!empty($aCache[$tableName][$this->{$primaryKeyName}][$parentTable])) { return $aCache[$tableName][$this->{$primaryKeyName}][$parentTable]; } // Test to ensure that the parent key column passed into the // method exists in this DB_DataObject if (empty($this->{$parentKeyName})) { $doThis = OA_Dal::staticGetDO($tableName, $this->{$primaryKeyName}); if ($doThis) { $parentKeyValue = $doThis->{$parentKeyName}; } } else { $parentKeyValue = $this->{$parentKeyName}; } // Do we have the parent table key value? if (empty($parentKeyValue)) { $message = "Cannot locate owning account IDs for entity in table '{$tableName}', " . "as the parent key value could not be located where column " . "'{$primaryKeyName}' was equal to '{$this->{$primaryKeyName}}'."; OA::debug($message, PEAR_LOG_ERR); return false; } // Get the DB_DataObject for the parnet table $doParent = OA_Dal::staticGetDO($parentTable, $parentKeyValue); if ($doParent == false) { $message = "Cannot locate owning account IDs for entity in table '{$tableName}', " . "as the parent data object could not be created where column " . "'{$primaryKeyName}' was equal to '{$this->{$primaryKeyName}}'."; OA::debug($message, PEAR_LOG_ERR); return false; } // Get the result of calling the getOwningAccountIds() method on the // parent DB_DataObject $aResult = $doParent->getOwningAccountIds(); // Store the result in the cache array $aCache[$tableName][$this->{$primaryKeyName}][$parentTable] = $aResult; // Return the result return $aCache[$tableName][$this->{$primaryKeyName}][$parentTable]; } }
/** * A private method to release a previously acquired lock. * * @return bool True if the lock was correctly released. */ function _releaseLock() { OA::debug('Base class cannot be used directly, use the factory method instead', PEAR_LOG_ERR); return false; }
/** * Performs a batch insert using either LOAD DATA INFILE or COPY FROM, eventually * falling back to batchInsertPlain (plain INSERTs) on failure. On MySQL LOAD DATA * INFILE is 20x faster than plain single inserts * * @param string $tableName The unquoted table name * @param array $aFields The array of unquoted field names * @param array $aValues The array of data to be inserted * @param bool $replace Should the data be UPDATEd when the primary key or unique key is already present in the table? * * @return int The number of rows inserted or PEAR_Error on failure */ function batchInsert($tableName, $aFields, $aValues, $replace = false, $primaryKey = array()) { if (!is_array($aFields) || !is_array($aValues)) { return MAX::raiseError('$aFields and $aValues must be arrays', PEAR_ERROR_RETURN); } $oDbh = OA_DB::singleton(); // Quote table name $qTableName = $oDbh->quoteIdentifier($tableName); // Quote fields list $fieldList = '(' . join(',', array_map(array($oDbh, 'quoteIdentifier'), $aFields)) . ')'; // Database custom stuff if ($oDbh->dbsyntax == 'mysql') { $result = self::_batchInsertMySQL($qTableName, $fieldList, $aValues, $replace); } else { $result = self::_batchInsertPgSQL($qTableName, $fieldList, $aValues, $replace, $primaryKey); } if (PEAR::isError($result)) { OA::debug('LOAD DATA INFILE / COPY failed or not supported, falling back to INSERTing data by looping over each record...', PEAR_LOG_INFO); $result = self::batchInsertPlain($tableName, $aFields, $aValues); } return $result; }
/** * Resets a (postgresql) sequence to 1 * similar to OA_DB_Table::resetSequence() * DOESN'T SEEM TO WORK THO * * @param string $sequence the name of the sequence to reset * @return boolean true on success, false otherwise */ function resetSequence($tableName) { $aConf = $GLOBALS['_MAX']['CONF']; $oDbh = OA_DB::singleton(); if ($aConf['database']['type'] == 'pgsql') { OA_DB::setCaseSensitive(); $aSequences = $oDbh->manager->listSequences(); OA_DB::disableCaseSensitive(); if (is_array($aSequences)) { OA::debug('Resetting sequence ' . $sequence, PEAR_LOG_DEBUG); OA::disableErrorHandling(null); $tableName = substr($aConf['table']['prefix'] . $tableName, 0, 29) . '_'; foreach ($aSequences as $k => $sequence) { if (strpos($sequence, $tableName) === 0) { $sequence = $oDbh->quoteIdentifier($sequence . '_seq', true); $result = $oDbh->exec("SELECT setval('{$sequence}', 1, false)"); break; } } OA::enableErrorHandling(); if (PEAR::isError($result)) { OA::debug('Unable to reset sequence on table ' . $tableName, PEAR_LOG_ERR); return false; } } } else { if ($aConf['database']['type'] == 'mysql') { $tableName = $aConf['table']['prefix'] . $tableName; OA::disableErrorHandling(); $result = $oDbh->exec("ALTER TABLE {$tableName} AUTO_INCREMENT = 1"); OA::enableErrorHandling(); if (PEAR::isError($result)) { OA::debug('Unable to reset sequence on table ' . $tableName, PEAR_LOG_ERR); return false; } } } return true; }
/** * A method to set the client charset * * @abstract * * @param string $charset * @return mixed True on success, PEAR_Error otherwise */ function setClientCharset($charset) { OA::debug('Cannot run abstract method'); exit; }
/** * A private metod to obtain a zone's average number of impressions per * operation interval, for a given range of operation interval IDs. * * The average is calculated from the values in the same operation interval * IDs from previous weeks to the operation interval range supplied, * over ZONE_FORECAST_BASELINE_WEEKS weeks. * * If the zone does not have sufficient data to calculate the average over * the required number of past weeks, then no average value will be returned. * * @access private * @param integer $zoneId The zone ID to obtain the averages for. * @param PEAR::Date $oStartDate The start date/time of the operation interval * of the lower range of the operation interval * IDs to calculate the past average impressions * delivered for. * @param PEAR::Date $oEndDate The start date/time of the operation interval * of the upper range of the operation interval * IDs to calculate the past average impressions * delivered for. * @return array An array, indexed by operation interval IDs, containing the * the average numer of impressions that the zone with ID * $zoneId actually delivered in the past * ZONE_FORECAST_BASELINE_WEEKS weeks in the same operatation * interval IDs. */ function _getZoneImpressionAverages($zoneId, $oStartDate, $oEndDate) { // Get average impressions for the zone $aZoneImpressionAverages = $this->oDal->getZonePastImpressionAverageByOI($zoneId, $oStartDate, $oEndDate, ZONE_FORECAST_BASELINE_WEEKS); if (PEAR::isError($aZoneImpressionAverages)) { OA::debug("- Error retrieving zone ID {$zoneId}'s average past impressions, exiting", PEAR_LOG_CRIT); exit; } return $aZoneImpressionAverages; }
/** * logs the overhead of a table * * @param string $table : name of table without prefix */ function _logTableOverhead($table) { $table = $this->_getTablenameUnquoted($table); $aResult = $this->oDbh->manager->getTableStatus($table); if (isset($aResult[0]['data_free']) && is_numeric($aResult[0]['data_free'])) { $overhead = $aResult[0]['data_free']; OA::debug('Table ' . $table . ' overhead (number of allocated but unused bytes) = ' . $overhead); if ($overhead > 0) { OA::debug('To reclaim diskspace, consider optimising this table'); } } else { OA::debug('Table ' . $table . ' overhead (number of allocated but unused bytes) = unkown'); } }
function checkFilePermission($file, $recurse) { if (!file_exists($file) || !$this->isWritable($file)) { return false; } $recurseWritable = true; if ($recurse) { $dh = @opendir($file); if ($dh) { while (false !== ($f = readdir($dh))) { if ($f == '.' || $f == '..' || $f == '.svn') { continue; } $thisFile = $file . '/' . $f; if (!$this->checkFilePermission($thisFile, $recurse)) { OA::debug('Unwritable ' . (is_dir($thisFile) ? 'folder ' : 'file ') . $thisFile); $recurseWritable = false; } } closedir($dh); } } return $recurseWritable; }
/** * The method to run the Maintenance Statistics Engine process. * * @static */ function run() { OA::switchLogIdent('maintenance'); // Get the configuration $aConf = $GLOBALS['_MAX']['CONF']; // Log the start of the process OA::debug('Running Maintenance Statistics Engine', PEAR_LOG_INFO); // Set longer time out, and ignore user abort if (!ini_get('safe_mode')) { @set_time_limit($aConf['maintenance']['timeLimitScripts']); @ignore_user_abort(true); } // Run the following code as the "Maintenance" user OA_Permission::switchToSystemProcessUser('Maintenance'); // Ensure the the current time is registered with the OA_ServiceLocator $oServiceLocator =& OA_ServiceLocator::instance(); $oDate =& $oServiceLocator->get('now'); if (!$oDate) { // Record the current time, and register with the OA_ServiceLocator $oDate = new Date(); $oServiceLocator->register('now', $oDate); } $this->aComponents = OX_Component::getListOfRegisteredComponentsForHook('addMaintenanceStatisticsTask'); // addMaintenanceStatisticsTask hook if (!empty($this->aComponents) && is_array($this->aComponents)) { foreach ($this->aComponents as $componentId) { if ($obj = OX_Component::factoryByComponentIdentifier($componentId)) { $obj->beforeMse(); } } } // Initialise the task runner object, for storing/running the tasks $this->oTaskRunner = new OA_Task_Runner(); // Register this object as the controlling class for the process, // so that tasks run by the task runner can locate this class to // update the report, etc. $oServiceLocator =& OA_ServiceLocator::instance(); $oServiceLocator->register('Maintenance_Statistics_Controller', $this); // Create and register an instance of the OA_Dal_Maintenance_Statistics DAL // class for the following tasks to use if (!$oServiceLocator->get('OX_Dal_Maintenance_Statistics')) { $oFactory = new OX_Dal_Maintenance_Statistics_Factory(); $oDal = $oFactory->factory(); $oServiceLocator->register('OX_Dal_Maintenance_Statistics', $oDal); } // Add the task to set the update requirements $oSetUpdateRequirements = new OX_Maintenance_Statistics_Task_SetUpdateRequirements(); $this->oTaskRunner->addTask($oSetUpdateRequirements); // Add the task to migrate the bucket data into the statistics tables $oSummariseIntermediate = new OX_Maintenance_Statistics_Task_MigrateBucketData(); $this->oTaskRunner->addTask($oSummariseIntermediate); // Add the task to handle the de-duplication and rejection of empty conversions $oDeDuplicateConversions = new OX_Maintenance_Statistics_Task_DeDuplicateConversions(); $this->oTaskRunner->addTask($oDeDuplicateConversions); // Add the task to handle the updating of "intermediate" statistics with // conversion information, as a legacy issue until all code obtains // conversion data from the standard conversion statistics tables $oManageConversions = new OX_Maintenance_Statistics_Task_ManageConversions(); $this->oTaskRunner->addTask($oManageConversions); // Add the task to summarise the intermediate statistics into final form $oSummariseFinal = new OX_Maintenance_Statistics_Task_SummariseFinal(); $this->oTaskRunner->addTask($oSummariseFinal); // Add the task to log the completion of the task $oLogCompletion = new OX_Maintenance_Statistics_Task_LogCompletion(); $this->oTaskRunner->addTask($oLogCompletion); // Add the task to manage (enable/disable) campaigns $oManageCampaigns = new OX_Maintenance_Statistics_Task_ManageCampaigns(); $this->oTaskRunner->addTask($oManageCampaigns); // addMaintenanceStatisticsTask hook if (!empty($this->aComponents) && is_array($this->aComponents)) { foreach ($this->aComponents as $componentId) { if ($obj = OX_Component::factoryByComponentIdentifier($componentId)) { $this->oTaskRunner->addTask($obj->addMaintenanceStatisticsTask(), $obj->getExistingClassName(), $obj->getOrder()); } } } // Run the MSE process tasks $this->oTaskRunner->runTasks(); // addMaintenanceStatisticsTask hook if (!empty($this->aComponents) && is_array($this->aComponents)) { foreach ($this->aComponents as $componentId) { if ($obj = OX_Component::factoryByComponentIdentifier($componentId)) { $obj->afterMse(); } } } // Return to the "normal" user OA_Permission::switchToSystemProcessUser(); // Log the end of the process OA::debug('Maintenance Statistics Engine Completed (Started at ' . $oDate->format('%Y-%m-%d %H:%M:%S') . ' ' . $oDate->tz->getShortName() . ')', PEAR_LOG_INFO); OA::switchLogIdent(); }
/** * This function check if the click logging for an add is blocked * when the block logging is active * * @param integer $adId The add's id of the add that the function checks * if is blocked for click logging * @param array $aBlockLoggingClick And array with the timestamps of the last click logged * for every add that has been clicked * @return boolean Returns true when the click block logging window for * and add hasn't expired yet */ function MAX_Delivery_log_isClickBlocked($adId, $aBlockLoggingClick) { if (isset($GLOBALS['conf']['logging']['blockAdClicksWindow']) && $GLOBALS['conf']['logging']['blockAdClicksWindow'] != 0) { if (isset($aBlockLoggingClick[$adId])) { $endBlock = MAX_commonUnCompressInt($aBlockLoggingClick[$adId]) + $GLOBALS['conf']['logging']['blockAdClicksWindow']; if ($endBlock >= MAX_commonGetTimeNow()) { ###START_STRIP_DELIVERY OA::debug('adID ' . $adId . ' click is still blocked by block logging window '); ###END_STRIP_DELIVERY return true; } } } return false; }
/** * A method to prune a bucket of all records up to and * including the time given. * * @param Date $oEnd Prune until this interval_start (inclusive). * @param Date $oStart Only prune before this interval_start date (inclusive) * as well. Optional. * @return mixed Either the number of rows pruned, or an MDB2_Error objet. */ public function pruneBucket($oBucket, $oEnd, $oStart = null) { $sTableName = $oBucket->getBucketTableName(); if (!is_null($oStart)) { OA::debug(' - Pruning the ' . $sTableName . ' table for data with operation interval start between ' . $oStart->format('%Y-%m-%d %H:%M:%S') . ' ' . $oStart->tz->getShortName() . ' and ' . $oEnd->format('%Y-%m-%d %H:%M:%S') . ' ' . $oEnd->tz->getShortName(), PEAR_LOG_DEBUG); } else { OA::debug(' - Pruning the ' . $sTableName . ' table for all data with operation interval start equal to or before ' . $oEnd->format('%Y-%m-%d %H:%M:%S') . ' ' . $oEnd->tz->getShortName(), PEAR_LOG_DEBUG); } // As this is raw data being processed, data will not be logged based on the operation interval, // but based on the time the raw data was collected. Adjust the $oEnd value accordingly... if (!is_null($oStart)) { $aStartDates = OX_OperationInterval::convertDateToOperationIntervalStartAndEndDates($oStart); } $aEndDates = OX_OperationInterval::convertDateToOperationIntervalStartAndEndDates($oEnd); OA::debug(' - The ' . $sTableName . ' table is a raw data table. Data logged in real-time, not operation intervals.', PEAR_LOG_INFO); if (!is_null($oStart)) { OA::debug(' - Accordingly, pruning of the ' . $sTableName . ' table will be performed based on data that has a logged date between ', PEAR_LOG_INFO); OA::debug(' ' . $aStartDates['start']->format('%Y-%m-%d %H:%M:%S') . ' ' . $aStartDates['start']->tz->getShortName() . ' and ' . $aEndDates['end']->format('%Y-%m-%d %H:%M:%S') . ' ' . $aEndDates['end']->tz->getShortName(), PEAR_LOG_INFO); } else { OA::debug(' - Accordingly, pruning of the ' . $sTableName . ' table will be performed based on data that has a logged date equal to', PEAR_LOG_INFO); OA::debug(' or before ' . $aEndDates['end']->format('%Y-%m-%d %H:%M:%S') . ' ' . $aEndDates['end']->tz->getShortName(), PEAR_LOG_INFO); } $query = "\n DELETE FROM\n {$sTableName}\n WHERE\n date_time <= " . DBC::makeLiteral($aEndDates['end']->format('%Y-%m-%d %H:%M:%S')); if (!is_null($oStart)) { $query .= "\n AND\n date_time >= " . DBC::makeLiteral($aStartDates['start']->format('%Y-%m-%d %H:%M:%S')); } $oDbh = OA_DB::singleton(); return $oDbh->exec($query); }
/** * Connect to OpenX Sync to check for updates * * @param float $already_seen Only check for updates newer than this value. * @return array An array of two items: * * Item 0 is the XML-RPC error code. Meanings: * -2 => The admin user has disabled update checking * -1 => No response from the server * 0 - 799 => XML-RPC library error codes * 0 => No error * 800 => No updates * 801+ => Error codes from the remote XML-RPC server * * Item 1 is either the error message (item 1 != 0), or an array containing update info */ function checkForUpdates($already_seen = 0) { global $XML_RPC_erruser; if (!$this->aConf['sync']['checkForUpdates']) { // Checking for updates has been disabled by the admin user, // so do not communicate with the server that provides the // details of what upgrades are available - just return an // 800 "error" $aReturn = array(-2, 'Check for updates has been disabled by the administrator.'); return $aReturn; } // Create the XML-RPC client object $client = OA_Central::getXmlRpcClient($this->_conf); // Prepare the installation's platform hash $platform_hash = OA_Dal_ApplicationVariables::get('platform_hash'); if (!$platform_hash) { // The installation does not have a platform hash; generate one, // and save it to the database for later use OA::debug("Generating a new platform_hash for the installation", PEAR_LOG_INFO); $platform_hash = OA_Dal_ApplicationVariables::generatePlatformHash(); if (!OA_Dal_ApplicationVariables::set('platform_hash', $platform_hash)) { OA::debug("Could not save the new platform_hash to the database", PEAR_LOG_ERR); unset($platform_hash); OA::debug("Sync process proceeding without a platform_hash", PEAR_LOG_INFO); } } // Prepare the parameters required for the XML-RPC call to // obtain if an update is available for this installation $params = array(new XML_RPC_Value(PRODUCT_NAME, 'string'), new XML_RPC_Value($this->getConfigVersion(OA_Dal_ApplicationVariables::get('oa_version')), 'string'), new XML_RPC_Value($already_seen, 'string'), new XML_RPC_Value($platform_hash, 'string')); // Has the Revive Adserver admin user kindly agreed to share the // technology stack that it is running on, to help the community? $aTechStack = array('data' => false); if ($this->aConf['sync']['shareStack']) { // Thanks, admin user! You're a star! Prepare the technology stack // data and add it to the XML-RPC call if ($this->oDbh->dbsyntax == 'mysql') { $dbms = 'MySQL'; } else { if ($this->oDbh->dbsyntax == 'pgsql') { $dbms = 'PostgreSQL'; } else { $dbms = 'UnknownSQL'; } } $aTechStack = array('os_type' => php_uname('s'), 'os_version' => php_uname('r'), 'webserver_type' => isset($_SERVER['SERVER_SOFTWARE']) ? preg_replace('#^(.*?)/.*$#', '$1', $_SERVER['SERVER_SOFTWARE']) : '', 'webserver_version' => isset($_SERVER['SERVER_SOFTWARE']) ? preg_replace('#^.*?/(.*?)(?: .*)?$#', '$1', $_SERVER['SERVER_SOFTWARE']) : '', 'db_type' => $dbms, 'db_version' => $this->oDbh->queryOne("SELECT VERSION()"), 'php_version' => phpversion(), 'php_sapi' => ucfirst(php_sapi_name()), 'php_extensions' => get_loaded_extensions(), 'php_register_globals' => (bool) ini_get('register_globals'), 'php_magic_quotes_gpc' => (bool) ini_get('magic_quotes_gpc'), 'php_safe_mode' => (bool) ini_get('safe_mode'), 'php_open_basedir' => (bool) strlen(ini_get('open_basedir')), 'php_upload_tmp_readable' => (bool) is_readable(ini_get('upload_tmp_dir') . DIRECTORY_SEPARATOR)); } $params[] = XML_RPC_Encode($aTechStack); // Add the registered email address $params[] = new XML_RPC_Value(OA_Dal_ApplicationVariables::get('sync_registered_email'), 'string'); // Create the XML-RPC request message $msg = new XML_RPC_Message("Revive.Sync", $params); // Send the XML-RPC request message if ($response = $client->send($msg, 10)) { // XML-RPC server found, now checking for errors if (!$response->faultCode()) { // No fault! Woo! Get the response and return it! $aReturn = array(0, XML_RPC_Decode($response->value())); // Prepare cache $cache = $aReturn[1]; // Update last run OA_Dal_ApplicationVariables::set('sync_last_run', date('Y-m-d H:i:s')); // Also write to the debug log OA::debug("Sync: updates found!", PEAR_LOG_INFO); } else { // Boo! An error! (Well, maybe - if it's 800, yay!) $aReturn = array($response->faultCode(), $response->faultString()); // Prepare cache $cache = false; // Update last run if ($response->faultCode() == 800) { // Update last run OA_Dal_ApplicationVariables::set('sync_last_run', date('Y-m-d H:i:s')); // Also write to the debug log OA::debug("Sync: {$aReturn[1]}", PEAR_LOG_INFO); } else { // Write to the debug log OA::debug("Sync: {$aReturn[1]} (code: {$aReturn[0]}", PEAR_LOG_ERR); // Return immediately without writing to cache return $aReturn; } } OA_Dal_ApplicationVariables::set('sync_cache', serialize($cache)); OA_Dal_ApplicationVariables::set('sync_timestamp', time()); return $aReturn; } $aReturn = array(-1, 'No response from the remote XML-RPC server.'); // Also write to the debug log OA::debug("Sync: {$aReturn[1]}", PEAR_LOG_ERR); return $aReturn; }
function _debugIfError($processName, $error) { if (PEAR::isError($error)) { OA::debug("OpenX {$processName} error (" . $error->getCode() . "): " . $error->getMessage(), PEAR_LOG_INFO); } }