Exemple #1
0
}
//
// daily jobs
//
echo 'Checking daily jobs...' . BR;
// get date of last run
$record = Values::get_record('cron.daily', NULL_DATE);
// wait at least 1 day = 86400 seconds between runs
if (isset($record['edit_date'])) {
    $target = SQL::strtotime($record['edit_date']) + 86400;
} else {
    $target = time();
}
// request to be delayed
if ($target > time()) {
    echo 'Wait until ' . gmdate('r', $target) . ' GMT' . BR;
} else {
    Values::set('cron.daily', 'running...');
    // do the job and provide feed-back to user
    $context['text'] = Hooks::include_scripts('daily');
    echo $context['text'];
    // remember tick date and resulting text
    Values::set('cron.daily', $context['text']);
    // log outcome of script execution in debug mode
    if ($context['with_debug'] == 'Y') {
        Logger::remember('cron.php: daily processing', $context['text'], 'debug');
    }
}
// all done
$time = round(get_micro_time() - $context['start_time'], 2);
exit(sprintf('Script terminated in %.2f seconds.', $time) . BR);
Exemple #2
0
    if ($recipients_ok == 0) {
        $context['text'] .= i18n::s('No letter has been transmitted.') . BR . "\n";
    } elseif ($recipients_ok == 1) {
        $context['text'] .= i18n::s('One letter has been transmitted.') . BR . "\n";
    } else {
        $context['text'] .= sprintf(i18n::s('%d letters have been transmitted.'), $recipients_ok) . BR . "\n";
    }
    // transmission errors, if any
    if ($recipients_errors == 1) {
        $context['text'] .= i18n::s('One transmission error has been encountered.') . BR . "\n";
    } elseif ($recipients_errors > 1) {
        $context['text'] .= sprintf(i18n::s('%d transmission errors have been encountered.'), $recipients_errors) . BR . "\n";
    }
    // save digest stamp, if any
    if (isset($_REQUEST['digest_stamp']) && $_REQUEST['digest_stamp'] > NULL_DATE) {
        Values::set('letters.digest.stamp', $_REQUEST['digest_stamp']);
    }
    // display the execution time
    $time = round(get_micro_time() - $context['start_time'], 2);
    $context['text'] .= '<p>' . sprintf(i18n::s('Script terminated in %.2f seconds.'), $time) . '</p>';
    // forward to the index page
    $menu = array('letters/' => i18n::s('Newsletters'));
    $context['text'] .= Skin::build_list($menu, 'menu_bar');
    // make the user select an option
} else {
    // the splash message
    $context['text'] .= '<p>' . i18n::s('This script will help you to prepare and to send a electronic message to community members. Please select below the action you would like to perform. Depending on your choice, the assistant may ask for additional parameters on successive panels.') . '</p>' . "\n";
    // the form
    $context['text'] .= '<form method="get" action="' . $context['script_url'] . '" id="main_form">' . "\n";
    // a digest of most recent articles
    $context['text'] .= '<p><input type="radio" name="action" value="digest" selected="selected" /> ' . i18n::s('Send a digest of articles published recently') . '</p>' . "\n";
Exemple #3
0
 /**
  * process new messages, if any
  *
  * This function checks inbound mailboxes, and process new messages on their arrival.
  *
  * This function is aiming to run silently, therefore errors are logged in a file.
  *
  * @return a string to be displayed in resulting page, if any
  */
 public static function tick_hook()
 {
     global $context;
     // useless if we don't have a valid database connection
     if (!$context['connection']) {
         return;
     }
     // we need some queue definitions
     Safe::load('parameters/agents.include.php');
     if (!isset($context['mail_queues']) || !is_array($context['mail_queues']) || !count($context['mail_queues'])) {
         return 'agents/messages.php: no queue has been defined' . BR;
     }
     // remember start time
     $stamp = get_micro_time();
     // process each inbound queue
     include_once $context['path_to_root'] . 'shared/values.php';
     // messages.tick
     $count = 0;
     foreach ($context['mail_queues'] as $name => $queue) {
         // count messages retrieved
         $messages = Messages::process_queue($queue);
         $count += $messages;
         // remember tick date
         Values::set('messages.tick.' . $name, $messages);
     }
     // rebuild index pages
     if ($count) {
         Cache::clear();
     }
     // compute execution time
     $time = round(get_micro_time() - $stamp, 2);
     // report on work achieved
     if ($count > 1) {
         return 'agents/messages.php: ' . $count . ' messages have been processed (' . $time . ' seconds)' . BR;
     } elseif ($count == 1) {
         return 'agents/messages.php: 1 message has been processed (' . $time . ' seconds)' . BR;
     } else {
         return 'agents/messages.php: nothing to do (' . $time . ' seconds)' . BR;
     }
 }
Exemple #4
0
 /**
  * purge idle space
  *
  * This function OPTIMIZEs tables that may create overheads because of
  * frequent deletions, including: cache, links, members, messages,
  * notifications, values, versions, visits.
  *
  * Last purge is recorded as value 'sql.tick'.
  *
  * @param boolean optional TRUE to not report on any error
  * @return a string to be displayed in resulting page, if any
  */
 public static function purge($silent = FALSE)
 {
     global $context;
     // useless if we don't have a valid database connection
     if (!$context['connection']) {
         return;
     }
     // remember start time
     $stamp = get_micro_time();
     // get date of last tick
     include_once $context['path_to_root'] . 'shared/values.php';
     $record = Values::get_record('sql.tick', NULL_DATE);
     // wait at least 8 hours = 24*3600 seconds between ticks
     if (isset($record['edit_date'])) {
         $target = SQL::strtotime($record['edit_date']) + 8 * 3600;
     } else {
         $target = time();
     }
     // request to be delayed
     if ($target > time()) {
         return 'shared/sql.php: wait until ' . gmdate('r', $target) . ' GMT' . BR;
     }
     // recover unused bytes
     $query = 'OPTIMIZE TABLE ' . SQL::table_name('cache') . ', ' . SQL::table_name('links') . ', ' . SQL::table_name('members') . ', ' . SQL::table_name('messages') . ', ' . SQL::table_name('notifications') . ', ' . SQL::table_name('values') . ', ' . SQL::table_name('versions') . ', ' . SQL::table_name('visits');
     $result = SQL::query($query, $silent);
     // remember tick date and resulting text
     Values::set('sql.tick', 'purge');
     // compute execution time
     $time = round(get_micro_time() - $stamp, 2);
     // report on work achieved
     if ($result) {
         return 'shared/sql.php: unused bytes have been recovered (' . $time . ' seconds)' . BR;
     } else {
         return 'shared/sql.php: nothing to recover (' . $time . ' seconds)' . BR;
     }
 }
Exemple #5
0
 /**
  * process new uploads, if any
  *
  * This function checks the input queue, and process new files on their arrival.
  *
  * This function is aiming to run silently, therefore errors are logged in a file.
  *
  * @return a string to be displayed in resulting page, if any
  *
  */
 public static function tick_hook()
 {
     global $context;
     // useless if we don't have a valid database connection
     if (!$context['connection']) {
         return;
     }
     // remember start time
     $stamp = get_micro_time();
     // process handx weblog entries, if any
     $count = 0;
     if (($files = Uploads::list_files('inbox/entries')) && @count($files) > 0) {
         foreach ($files as $file) {
             // help the webmaster
             Logger::remember('agents/upload.php: processing ' . $file);
             // create articles
             Uploads::process_handx_weblog($file);
             // no more than 10 entries per tick
             $count += 1;
             if ($count >= 10) {
                 break;
             }
         }
         // remember tick date
         include_once $context['path_to_root'] . 'shared/values.php';
         Values::set('uploads.tick.entries', $count);
     }
     // rebuild index pages
     if ($count) {
         Cache::clear();
     }
     // compute execution time
     $time = round(get_micro_time() - $stamp, 2);
     // report on work achieved
     if ($count > 1) {
         return 'agents/uploads.php: ' . $count . ' files have been processed (' . $time . " seconds)" . BR;
     } elseif ($count == 1) {
         return 'agents/uploads.php: 1 file has been processed (' . $time . " seconds)" . BR;
     } else {
         return 'agents/uploads.php: nothing to do (' . $time . " seconds)" . BR;
     }
 }
Exemple #6
0
 /**
  * process deferred messages
  *
  * Most often, the server has to stay below a given rate of messages,
  * for example 50 messages per hour.
  *
  * Of course, any lively community will feature bursts of activity and of
  * messages, therefore the need for a shaping mechanism.
  *
  * YACS implements a leaking bucket algorithm to take care of messages sent
  * previously:
  *
  * 1. Initially, the bucket is empty.
  *
  * 2. New messages are queued in the database, to be processed asynchronously.
  *
  * 3. On background ticks, the bucket is decremented. If the bucket becomes
  * empty, and if some messages have been queued, a couple of them are sent, and
  * the bucket is incremented accordingly.
  *
  * Bucket content is managed as value 'bucket.content' saved in the database.
  *
  * The bucket size is given by parameter $context['mail_hourly_maximum'], set
  * in the configuration panel for system parameters.
  *
  * This parameter has a default value of 50, meaning YACS will not send more
  * than 50 messages per hour.
  *
  * Background processing is either added to regular page generation or delegated
  * to an external sub-system (e.g., cron). In case of a large site, we recommend
  * to use the second solution, even if this adds additional setup steps. Your
  * choice will be recorded in the configuration panel for system parameters.
  *
  * @see control/configure.php
  *
  * The number of messages sent on each tick can go up to the bucket size if
  * background processing is external. Else it is one fourth of bucket size, to
  * minimize impact on watching surfer.
  *
  * @see cron.php
  */
 public static function tick_hook()
 {
     global $context;
     // email services have to be activated
     if (!isset($context['with_email']) || $context['with_email'] != 'Y') {
         return;
     }
     // useless if we don't have a valid database connection
     if (!$context['connection']) {
         return;
     }
     // remember start time
     $start = get_micro_time();
     // get bucket size --force it if set to 0
     if (!isset($context['mail_hourly_maximum']) || $context['mail_hourly_maximum'] < 5) {
         $context['mail_hourly_maximum'] = 50;
     }
     // get record related to last tick
     include_once $context['path_to_root'] . 'shared/values.php';
     $bucket = Values::get_record('mailer.bucket.content', 0);
     $bucket['value'] = intval($bucket['value']);
     // some content to leak
     if ($bucket['value'] > 0) {
         // date of last stamp
         if (isset($bucket['edit_date'])) {
             $stamp = SQL::strtotime($bucket['edit_date']);
         } else {
             $stamp = time() - 3600;
         }
         // leak is maximum after one hour
         $leak = intval($context['mail_hourly_maximum'] * (time() - $stamp) / 3600);
         // preserve previous value until actual leak
         if ($leak < 1) {
             return;
         }
         // actual leak
         $bucket['value'] = max(0, $bucket['value'] - $leak);
     }
     // process some messages only when bucket is empty
     $count = 0;
     if ($bucket['value'] < 1) {
         // reduced speed if on-line processing
         if (isset($_SERVER['REMOTE_ADDR'])) {
             $slice = intval($context['mail_hourly_maximum'] / 4);
         } else {
             $slice = intval($context['mail_hourly_maximum']);
         }
         // get some messages, if any
         $query = "SELECT * FROM " . SQL::table_name('messages') . " ORDER BY edit_date LIMIT 0, " . $slice;
         if ($result = SQL::query($query)) {
             // process every message
             while ($item = SQL::fetch($result)) {
                 Mailer::process($item['recipient'], $item['subject'], $item['message'], $item['headers']);
                 // purge the queue
                 $query = 'DELETE FROM ' . SQL::table_name('messages') . ' WHERE id = ' . $item['id'];
                 SQL::query($query);
                 // fill the bucket
                 $bucket['value'] += 1;
                 $count++;
                 // take care of time
                 if (!($count % 50)) {
                     // ensure enough execution time
                     Safe::set_time_limit(30);
                 }
             }
             // close connection
             Mailer::close();
         }
     }
     // remember new state of the bucket
     Values::set('mailer.bucket.content', $bucket['value']);
     // compute execution time
     $time = round(get_micro_time() - $start, 2);
     // report on work achieved
     if ($count > 1) {
         return 'shared/mailer.php: ' . $count . ' messages have been processed (' . $time . ' seconds)' . BR;
     } elseif ($count == 1) {
         return 'shared/mailer.php: 1 message has been processed (' . $time . ' seconds)' . BR;
     } elseif ($bucket['value']) {
         return 'shared/mailer.php: delaying messages (' . $time . ' seconds)' . BR;
     } else {
         return 'shared/mailer.php: nothing to do (' . $time . ' seconds)' . BR;
     }
 }
Exemple #7
0
 /**
  * get news from remote servers
  *
  * This function queries remote sources and populate the table of links based on fetched news.
  *
  * On tick, the including hook calls [code]Feeds::tick_hook()[/code].
  * See [script]control/scan.php[/script] for a more complete description of hooks.
  *
  * The function browses the database to locate servers acting as feeders, and read the URLs to use.
  *
  * A round-robin algorithm is implemented, meaning that servers are polled in sequence throughout successive ticks.
  * At most 1 feed is parsed on each tick, to limit impact when the "poor-man" cron mechanism is used,
  * which is the default setting.
  *
  * XML feeds are fetched and parsed according to their type.
  * At the moment YACS is able to process RSS and slashdot feeds.
  * Link records are created or updated in the database saving as much of possible of provided data.
  * Item data is reflected in Link, Title, and Description fields.
  * Channel	data is used to populate the Source field.
  * Stamping information is based on feeding date, and channel title.
  * Also, the edit action 'link:feed' marks links that are collected from feeders.
  * The anchor field is set to the category assigned in the server profile.
  *
  * At the end of the feeding process, the database is purged from oldest links according to the limit
  * defined in parameters/feeds.include.php, set through feeds/configure.php.
  * See Links::purge_old_news().
  *
  * @param boolean if set to true, fetch news on each call; else use normal period of time
  * @return a string to be displayed in resulting page, if any
  *
  * @see control/scan.php
  * @see feeds/configure.php
  */
 public static function tick_hook($forced = FALSE)
 {
     global $context;
     // load librairies only once
     include_once $context['path_to_root'] . 'links/links.php';
     include_once $context['path_to_root'] . 'servers/servers.php';
     include_once $context['path_to_root'] . 'shared/values.php';
     // feeds.tick
     // get feeding parameters
     Safe::load('parameters/feeds.include.php');
     // delay between feeds - minimum is 5 minutes
     if (!isset($context['minutes_between_feeds']) || $context['minutes_between_feeds'] < 5) {
         $context['minutes_between_feeds'] = 5;
     }
     // do not wait for the end of a feeding cycle
     if ($forced) {
         $threshold = gmstrftime('%Y-%m-%d %H:%M:%S');
     } else {
         $threshold = gmstrftime('%Y-%m-%d %H:%M:%S', time() - $context['minutes_between_feeds'] * 60);
     }
     // get a batch of feeders
     if (!($feeders = Servers::list_for_feed(0, 1, 'feed'))) {
         return 'feeds/feeds.php: no feed has been defined' . BR;
     }
     // remember start time
     $start_time = get_micro_time();
     // list banned tokens
     $banned_pattern = Servers::get_banned_pattern();
     // browse each feed
     $count = 0;
     foreach ($feeders as $server_id => $attributes) {
         // get specific feed parameters
         list($feed_url, $feed_title, $anchor, $stamp) = $attributes;
         // skip servers processed recently
         if ($stamp > $threshold) {
             continue;
         }
         // flag this record to enable round-robin even on error
         Servers::stamp($server_id);
         // fetch news from the provided link
         if (!($news = Feeds::get_remote_news_from($feed_url)) || !is_array($news)) {
             continue;
         }
         // no anchor has been defined for this feed
         if (!$anchor) {
             // create a default section if necessary
             if (!($anchor = Sections::lookup('external_news'))) {
                 $fields = array();
                 $fields['nick_name'] = 'external_news';
                 $fields['create_date'] = gmstrftime('%Y-%m-%d %H:%M:%S', time());
                 $fields['edit_date'] = gmstrftime('%Y-%m-%d %H:%M:%S', time());
                 $fields['index_map'] = 'N';
                 $fields['locked'] = 'Y';
                 // no direct contributions
                 $fields['rank'] = 40000;
                 // at the end of the list
                 $fields['title'] = i18n::c('External News');
                 $fields['description'] = i18n::c('Received from feeding servers');
                 if (!($fields['id'] = Sections::post($fields))) {
                     Logger::remember('feeds/feeds.php: Impossible to add a section.');
                     return;
                 }
                 $anchor = 'section:' . $fields['id'];
             }
         }
         // process retrieved links
         $links = 0;
         foreach ($news as $item) {
             // link has to be valid
             if (!isset($item['link']) || !($item['title'] . $item['description'])) {
                 if (isset($context['debug_feeds']) && $context['debug_feeds'] == 'Y') {
                     Logger::remember('feeds/feeds.php: feed item is invalid', $item, 'debug');
                 }
                 continue;
             }
             // skip banned servers
             if ($banned_pattern && preg_match($banned_pattern, $item['link'])) {
                 if (isset($context['debug_feeds']) && $context['debug_feeds'] == 'Y') {
                     Logger::remember('feeds/feeds.php: feed host has been banned', $item['link'], 'debug');
                 }
                 continue;
             }
             // one link processed
             $links++;
             // link description
             $fields = array();
             $fields['anchor'] = $anchor;
             $fields['link_url'] = $item['link'];
             $fields['title'] = $item['title'];
             $fields['description'] = $item['description'];
             if ($item['category']) {
                 $fields['description'] .= ' (' . $item['category'] . ')';
             }
             $fields['edit_name'] = $feed_title;
             $fields['edit_address'] = $feed_url;
             $fields['edit_action'] = 'link:feed';
             if ($item['pubDate']) {
                 $fields['edit_date'] = gmstrftime('%Y-%m-%d %H:%M:%S', strtotime($item['pubDate']));
             }
             // update links that already exist in the database
             if (Links::have($item['link'], $anchor, $fields)) {
                 continue;
             }
             // save link in the database
             if (!Links::post($fields)) {
                 Logger::remember('feeds/feeds.php: Impossible to save feed link: ' . Logger::error_pop());
             }
         }
         // one feed has been processed
         $count += 1;
         // remember tick date
         Values::set('feeds.tick.' . $feed_url, $links);
     }
     // cap the number of links used for news
     if (!isset($context['maximum_news']) || !$context['maximum_news']) {
         $context['maximum_news'] = 1000;
     }
     if ($context['maximum_news'] > 10) {
         include_once $context['path_to_root'] . 'links/links.php';
         Links::purge_old_news($context['maximum_news']);
     }
     // compute execution time
     $time = round(get_micro_time() - $start_time, 2);
     // report on work achieved
     if ($count > 1) {
         return 'feeds/feeds.php: ' . $count . ' feeds have been processed (' . $time . ' seconds)' . BR;
     } elseif ($count == 1) {
         return 'feeds/feeds.php: 1 feed has been processed (' . $time . ' seconds)' . BR;
     } else {
         return 'feeds/feeds.php: nothing to do (' . $time . ' seconds)' . BR;
     }
 }