示例#1
0
文件: links.php 项目: rair/yacs
 /**
  * record a click
  *
  * @param string the external url that is targeted
  *
  */
 public static function click($url)
 {
     global $context;
     // we record only GET requests
     if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] != 'GET') {
         return;
     }
     // do not count crawling
     if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/(blo\\.gs|\\bblog|bot\\b|crawler\\b|frontier\\b|slurp\\b|spider\\b)/i', $_SERVER['HTTP_USER_AGENT'])) {
         return;
     }
     // record the activity
     Activities::post($url, 'click');
     // do not record clicks driving to search engines
     if (preg_match('/\\b(google|yahoo)\\b/i', $url)) {
         return;
     }
     // if this url is known
     $query = "SELECT * FROM " . SQL::table_name('links') . " AS links" . " WHERE links.link_url LIKE '" . SQL::escape($url) . "'";
     if ($item = SQL::query_first($query)) {
         // increment the number of clicks
         $query = "UPDATE " . SQL::table_name('links') . " SET hits=hits+1 WHERE id = " . SQL::escape($item['id']);
         SQL::query($query);
         // else create a new record with a count of one click
     } else {
         // get the section for clicks
         $anchor = Sections::lookup('clicks');
         // no section yet, create one
         if (!$anchor) {
             $fields['nick_name'] = 'clicks';
             $fields['title'] = i18n::c('Clicks');
             $fields['introduction'] = i18n::c('Clicked links are referenced here.');
             $fields['description'] = i18n::c('YACS ties automatically external links to this section on use. Therefore, you will have below a global picture of external sites that are referenced through your site.');
             $fields['active_set'] = 'N';
             // for associates only
             $fields['locked'] = 'Y';
             // no direct contributions
             $fields['index_map'] = 'N';
             // listd only to associates
             $fields['rank'] = 20000;
             // towards the end of the list
             // reference the new section
             if ($fields['id'] = Sections::post($fields)) {
                 $anchor = 'section:' . $fields['id'];
             }
         }
         // create a new link in the database
         $fields = array();
         $fields['anchor'] = $anchor;
         $fields['link_url'] = $url;
         $fields['hits'] = 1;
         Surfer::check_default_editor($fields);
         if ($fields['id'] = Links::post($fields)) {
             Links::clear($fields);
         }
     }
 }
示例#2
0
文件: new.php 项目: rair/yacs
     // a private sub-section, for internal work
     if ($_REQUEST['active'] != 'N') {
         $fields = array();
         $fields['active_set'] = 'N';
         $fields['anchor'] = 'section:' . $_REQUEST['id'];
         $fields['articles_layout'] = 'yabb';
         $fields['content_options'] = 'auto_publish with_neighbours members_edit';
         $fields['introduction'] = i18n::c('Reserved to project members');
         $fields['index_map'] = 'N';
         // not mentioned at the home page
         $fields['options'] = 'forward_notifications view_as_tabs';
         // to list editors and watchers explicitly
         $fields['rank'] = 4000;
         $fields['thumbnail_url'] = $context['url_to_home'] . $context['url_to_root'] . 'skins/_reference/thumbnails/meeting.gif';
         $fields['title'] = i18n::c('Private activities');
         Sections::post($fields);
     }
 }
 // increment the post counter of the surfer
 Users::increment_posts(Surfer::get_id());
 // reward the poster
 $context['page_title'] = i18n::s('Congratulation, you have successfully added a new web space');
 // follow-up commands
 $follow_up = i18n::s('What do you want to do now?');
 $menu = array();
 if ($_REQUEST['space_type'] == 'blog') {
     $menu = array_merge($menu, array(Sections::get_permalink($_REQUEST) => i18n::s('View the new blog')));
 } elseif ($_REQUEST['space_type'] == 'project') {
     $menu = array_merge($menu, array(Sections::get_permalink($_REQUEST) => i18n::s('View the new project')));
 } else {
     $menu = array_merge($menu, array(Sections::get_permalink($_REQUEST) => i18n::s('View the new group')));
示例#3
0
文件: new.php 项目: rair/yacs
 $anchor = Sections::lookup('letters');
 // no section yet, create one
 if (!$anchor) {
     $context['text'] .= i18n::s('Creating a section for archived letters') . BR . "\n";
     $fields['nick_name'] = 'letters';
     $fields['title'] = i18n::c('Archived letters');
     $fields['introduction'] = i18n::c('To remember our previous messages');
     $fields['description'] = i18n::c('YACS puts automatically sent letters into this section.');
     $fields['locked'] = 'Y';
     // no direct contributions
     $fields['index_map'] = 'N';
     // listed only to associates
     $fields['rank'] = 30000;
     // at the end of the list
     // reference the new section
     if ($fields['id'] = Sections::post($fields, FALSE)) {
         $anchor = 'section:' . $fields['id'];
     }
 }
 // archive the letter
 $context['text'] .= i18n::s('Archiving the new letter') . BR . "\n";
 // save the letter as a published article, but don't use special categories
 $fields = array();
 $fields['anchor'] = $anchor;
 $fields['title'] = $_REQUEST['letter_title'];
 $label = $_REQUEST['letter_recipients'];
 if ($_REQUEST['letter_recipients'] == 'custom' && isset($_REQUEST['mail_to'])) {
     $label = $_REQUEST['mail_to'];
 }
 $fields['introduction'] = sprintf(i18n::c('Sent %s to "%s"'), Skin::build_date(time(), 'full', $context['preferred_language']), $label);
 $fields['description'] = $_REQUEST['letter_body'];
示例#4
0
文件: sections.php 项目: rair/yacs
 /**
  * duplicate all sections for a given anchor
  *
  * This function duplicates records in the database, and changes anchors
  * to attach new records as per second parameter.
  *
  * @param string the source anchor
  * @param string the target anchor
  * @return int the number of duplicated records
  *
  * @see shared/anchors.php
  */
 public static function duplicate_for_anchor($anchor_from, $anchor_to)
 {
     global $context;
     // look for records attached to this anchor
     $count = 0;
     $query = "SELECT * FROM " . SQL::table_name('sections') . " WHERE anchor LIKE '" . SQL::escape($anchor_from) . "'";
     if (($result = SQL::query($query)) && SQL::count($result)) {
         // the list of transcoded strings
         $transcoded = array();
         // process all matching records one at a time
         while ($item = SQL::fetch($result)) {
             // a new id will be allocated
             $old_id = $item['id'];
             unset($item['id']);
             // creator has to be the person who duplicates
             unset($item['create_address']);
             unset($item['create_date']);
             unset($item['create_id']);
             unset($item['create_name']);
             unset($item['edit_address']);
             unset($item['edit_date']);
             unset($item['edit_id']);
             unset($item['edit_name']);
             // target anchor
             $item['anchor'] = $anchor_to;
             // actual duplication
             if ($item['id'] = Sections::post($item, FALSE)) {
                 // more pairs of strings to transcode
                 $transcoded[] = array('/\\[section=' . preg_quote($old_id, '/') . '/i', '[section=' . $item['id']);
                 // duplicate elements related to this item
                 Anchors::duplicate_related_to('section:' . $old_id, 'section:' . $item['id']);
                 // stats
                 $count++;
             }
         }
         // transcode in anchor
         if ($anchor = Anchors::get($anchor_to)) {
             $anchor->transcode($transcoded);
         }
     }
     // number of duplicated records
     return $count;
 }
示例#5
0
文件: populate.php 项目: rair/yacs
 $fields['options'] = 'articles_by_title';
 // alphabetical order
 $fields['content_options'] = 'view_as_wiki auto_publish edit_as_simple with_export_tools';
 if ($_REQUEST['contribution'] == 'Y') {
     // anyone can contribute
     $fields['content_options'] .= ' anonymous_edit';
 } elseif ($_REQUEST['contribution'] == 'R') {
     // only members can contribute
     $fields['content_options'] .= ' members_edit';
 } elseif ($_REQUEST['contribution'] == 'N') {
     // only associates and owners can contribute
     $fields['locked'] = 'Y';
 }
 $fields['rank'] = 10000;
 // default value
 if ($fields['id'] = Sections::post($fields)) {
     // increment the post counter of the surfer
     Users::increment_posts(Surfer::get_id());
     // splash
     $context['text'] .= '<p>' . i18n::s('Congratulations, you have shared new content.') . '</p>';
     // feed-back on contributor scope
     switch ($_REQUEST['contribution']) {
         case 'Y':
             $context['text'] .= '<p>' . i18n::s('Any surfer is allowed to contribute anonymously to this wiki. Internet robots are prevented to submit new posts, however you should continuously review content to avoid unappropriate material.') . '</p>';
             break;
         case 'R':
             $context['text'] .= '<p>' . i18n::s('Any authenticated member of the community is allowed to contribute to this wiki. You should control community membership to avoid unappropriate material.') . '</p>';
             break;
         case 'N':
             $context['text'] .= '<p>' . i18n::s('You have selected to implement a private wiki. Assign individual members as editors of this wiki to enable contributions beyond associates.') . '</p>';
             break;
示例#6
0
文件: edit.php 项目: rair/yacs
             if (preg_match('/\\bwith_files\\b/i', $_REQUEST['options']) && Surfer::may_upload()) {
                 $menu = array_merge($menu, array('files/edit.php?anchor=' . urlencode('section:' . $_REQUEST['id']) => i18n::s('Add a file')));
             }
             $follow_up .= Skin::build_list($menu, 'menu_bar');
             $context['text'] .= Skin::build_block($follow_up, 'bottom');
             // log page modification
             $label = sprintf(i18n::c('%s: %s'), i18n::c('Contribution'), strip_tags($_REQUEST['title']));
             $description = '<a href="' . Sections::get_permalink($_REQUEST) . '">' . $_REQUEST['title'] . '</a>';
             Logger::notify('sections/edit.php: ' . $label, $description);
             // display the updated page
         } else {
             Safe::redirect(Sections::get_permalink($item));
         }
     }
     // create a new section
 } elseif (!($_REQUEST['id'] = Sections::post($_REQUEST))) {
     $item = $_REQUEST;
     $with_form = TRUE;
     // successful post
 } else {
     // post an overlay, with the new section id --don't stop on error
     if (is_object($overlay)) {
         $overlay->remember('insert', $_REQUEST, 'section:' . $_REQUEST['id']);
     }
     // notification to send by e-mail
     $mail = array();
     $anchor_title = is_object($anchor) ? strip_tags($anchor->get_title()) : i18n::s('Root');
     $mail['subject'] = sprintf(i18n::c('%s: %s'), $anchor_title, strip_tags($_REQUEST['title']));
     $mail['notification'] = Sections::build_notification('create', $_REQUEST);
     $mail['headers'] = Mailer::set_thread('section:' . $_REQUEST['id']);
     // touch the related anchor
示例#7
0
文件: populate.php 项目: rair/yacs
     if (Sections::post($fields)) {
         $text .= sprintf(i18n::s('A section "%s" has been created.'), $fields['nick_name']) . BR . "\n";
     } else {
         $text .= Logger::error_pop() . BR . "\n";
     }
 }
 // 'my_section' section
 if (Sections::get('my_section')) {
     $text .= sprintf(i18n::s('A section "%s" already exists.'), 'my_section') . BR . "\n";
 } else {
     $fields = array();
     $fields['nick_name'] = 'my_section';
     $fields['title'] = i18n::c('My Section');
     $fields['introduction'] = i18n::c('Sample plain section');
     $fields['content_options'] = 'with_export_tools';
     if (Sections::post($fields)) {
         $text .= sprintf(i18n::s('A section "%s" has been created.'), $fields['nick_name']) . BR . "\n";
     } else {
         $text .= Logger::error_pop() . BR . "\n";
     }
 }
 // categories
 //
 $text .= Skin::build_block(i18n::s('Categories'), 'subtitle');
 // 'my_category' category
 if (Categories::get('my_category')) {
     $text .= sprintf(i18n::s('Category "%s" already exists.'), 'my_category') . BR . "\n";
 } else {
     $fields = array();
     $fields['nick_name'] = 'my_category';
     $fields['title'] = i18n::c('My category');
示例#8
0
文件: contact.php 项目: rair/yacs
    $fields['introduction'] =& i18n::c('For on-demand conversations');
    $fields['locked'] = 'Y';
    // no direct contributions
    $fields['active'] = 'N';
    // private
    $fields['active_set'] = 'N';
    $fields['home_panel'] = 'none';
    // content is not pushed at the front page
    $fields['index_map'] = 'N';
    // listed only to associates
    $fields['articles_layout'] = 'yabb';
    // these are threads
    $fields['content_options'] = 'with_export_tools auto_publish with_comments_as_wall';
    $fields['maximum_items'] = 20000;
    // limit the overall number of threads
    Sections::post($fields, FALSE);
}
// identify all recipients
$items = array();
$item = NULL;
if (is_array($id)) {
    // one recipient at a time
    foreach ($id as $target) {
        $target = trim(str_replace(array("\r\n", "\r", "\n", "\t"), ' ', $target));
        // look for a user with this nick name
        if (!($user = Users::lookup($target))) {
            if ($target) {
                Logger::error(sprintf(i18n::s('Error while sending the message to %s'), $target));
            }
            // skip this recipient
            continue;
示例#9
0
文件: manage.php 项目: rair/yacs
         unset($section['create_address']);
         unset($section['create_date']);
         unset($section['create_id']);
         unset($section['create_name']);
         unset($section['edit_address']);
         unset($section['edit_date']);
         unset($section['edit_id']);
         unset($section['edit_name']);
         // change the handle
         unset($section['handle']);
         // ensure this is a copy
         $section['title'] = sprintf(i18n::s('Copy of %s'), $section['title']);
         // target anchor
         $section['anchor'] = $_REQUEST['duplicate_to'];
         // actual duplication
         if ($section['id'] = Sections::post($section, FALSE)) {
             // also duplicate the provided overlay, if any -- re-use 'overlay_type' only
             $overlay = Overlay::load($section, 'section:' . $section['id']);
             // post an overlay, with the new section id
             if (is_object($overlay)) {
                 $overlay->remember('insert', $section, 'section:' . $section['id']);
             }
             // duplicate elements related to this item
             Anchors::duplicate_related_to('section:' . $old_id, 'section:' . $section['id']);
             // stats
             $count++;
         }
     }
 }
 // report on results
 $context['text'] .= '<p>' . sprintf(i18n::ns('%d section has been duplicated.', '%d sections have been duplicated.', $count), $count) . '</p>';
示例#10
0
文件: duplicate.php 项目: rair/yacs
 unset($item['id']);
 unset($item['handle']);
 // the duplicator becomes the author
 unset($item['create_address']);
 unset($item['create_date']);
 unset($item['create_id']);
 unset($item['create_name']);
 unset($item['edit_address']);
 unset($item['edit_date']);
 unset($item['edit_id']);
 unset($item['edit_name']);
 // ensure this is a copy
 $item['title'] = sprintf(i18n::s('Copy of %s'), $item['title']);
 $item['index_title'] = $item['title'];
 // create a new page
 if ($item['id'] = Sections::post($item, FALSE)) {
     // also duplicate the provided overlay, if any -- re-use 'overlay_type' only
     $overlay = Overlay::load($item, 'section:' . $item['id']);
     // post an overlay, with the new section id
     if (is_object($overlay)) {
         $overlay->remember('insert', $item, 'section:' . $item['id']);
     }
     // duplicate all related items, images, etc.
     Anchors::duplicate_related_to($original_anchor, 'section:' . $item['id']);
     // if poster is a registered user
     if (Surfer::get_id()) {
         // increment the post counter of the surfer
         Users::increment_posts(Surfer::get_id());
         // add this page to watch list
         Members::assign('section:' . $item['id'], 'user:' . Surfer::get_id());
     }
示例#11
0
文件: import.php 项目: rair/yacs
 function parse_tag_close($parser, $tag)
 {
     global $context;
     global $in_overlay, $overlay_class, $overlay_parameters;
     global $parsed_cdata, $parsed_item, $parsed_overlay, $parsing_report;
     // save gathered data if necessary
     switch ($tag) {
         case 'article':
             // end of article
             // transcode owner id
             $parsed_item['owner_id'] = Surfer::get_id();
             if (isset($parsed_item['owner_nick_name']) && ($user = Users::get($parsed_item['owner_nick_name']))) {
                 $parsed_item['owner_id'] = $user['id'];
             }
             // transcode creator id
             $parsed_item['create_id'] = Surfer::get_id();
             if (isset($parsed_item['create_nick_name']) && ($user = Users::get($parsed_item['create_nick_name']))) {
                 $parsed_item['create_id'] = $user['id'];
             }
             // transcode editor id
             $parsed_item['edit_id'] = Surfer::get_id();
             if (isset($parsed_item['edit_nick_name']) && ($user = Users::get($parsed_item['edit_nick_name']))) {
                 $parsed_item['edit_id'] = $user['id'];
             }
             // transcode publisher id
             $parsed_item['publish_id'] = Surfer::get_id();
             if (isset($parsed_item['publish_nick_name']) && ($user = Users::get($parsed_item['publish_nick_name']))) {
                 $parsed_item['publish_id'] = $user['id'];
             }
             // bind to given overlay
             $overlay = NULL;
             if ($overlay_class) {
                 $overlay = Overlay::bind($overlay_class . ' ' . $overlay_parameters);
             }
             // when the page has been overlaid
             if (is_object($overlay)) {
                 // update the overlay from content
                 foreach ($parsed_overlay as $label => $value) {
                     $overlay->attributes[$label] = $value;
                 }
                 // save content of the overlay in this item
                 $parsed_item['overlay'] = $overlay->save();
                 $parsed_item['overlay_id'] = $overlay->get_id();
             }
             // find anchor from handle
             if (isset($parsed_item['anchor_handle']) && ($reference = Sections::lookup($parsed_item['anchor_handle']))) {
                 $parsed_item['anchor'] = $reference;
             }
             // update an existing page
             if (isset($parsed_item['handle']) && ($item = Articles::get($parsed_item['handle']))) {
                 // transcode page id
                 $parsed_item['id'] = $item['id'];
                 // stop on error
                 if (!Articles::put($parsed_item) || is_object($overlay) && !$overlay->remember('update', $parsed_item, 'article:' . $item['id'])) {
                     Logger::error(sprintf('Unable to save article %s', $parsed_item['title'] . ' (' . $parsed_item['id'] . ')'));
                 }
                 // create a new page
             } else {
                 unset($parsed_item['id']);
                 // stop on error
                 if (!($parsed_item['id'] = Articles::post($parsed_item))) {
                     Logger::error(sprintf('Unable to save article %s', $parsed_item['title']));
                 } else {
                     // save overlay content
                     if (is_object($overlay)) {
                         $overlay->remember('insert', $parsed_item, 'article:' . $parsed_item['id']);
                     }
                 }
             }
             // report to surfer
             $parsing_report .= '<li>' . Skin::build_link(Articles::get_permalink($parsed_item), $parsed_item['title']) . "</li>\n";
             // ready for next item
             $overlay_class = NULL;
             $overlay_parameters = '';
             $parsed_overlay = array();
             $parsed_item = array();
             Safe::set_time_limit(30);
             break;
         case 'overlay':
             // end of overlay data
             $in_overlay = FALSE;
             break;
         case 'section':
             // end of section
             // transcode owner id
             $parsed_item['owner_id'] = Surfer::get_id();
             if (isset($parsed_item['owner_nick_name']) && ($user = Users::get($parsed_item['owner_nick_name']))) {
                 $parsed_item['owner_id'] = $user['id'];
             }
             // transcode creator id
             $parsed_item['create_id'] = Surfer::get_id();
             if (isset($parsed_item['create_nick_name']) && ($user = Users::get($parsed_item['create_nick_name']))) {
                 $parsed_item['create_id'] = $user['id'];
             }
             // transcode editor id
             $parsed_item['edit_id'] = Surfer::get_id();
             if (isset($parsed_item['edit_nick_name']) && ($user = Users::get($parsed_item['edit_nick_name']))) {
                 $parsed_item['edit_id'] = $user['id'];
             }
             // bind to given overlay
             $overlay = NULL;
             if ($overlay_class) {
                 $overlay = Overlay::bind($overlay_class . ' ' . $overlay_parameters);
             }
             // when the page has been overlaid
             if (is_object($overlay)) {
                 // update the overlay from content
                 foreach ($parsed_overlay as $label => $value) {
                     $overlay->attributes[$label] = $value;
                 }
                 // save content of the overlay in this item
                 $parsed_item['overlay'] = $overlay->save();
                 $parsed_item['overlay_id'] = $overlay->get_id();
             }
             // find anchor from handle
             if (isset($parsed_item['anchor_handle']) && ($reference = Sections::lookup($parsed_item['anchor_handle']))) {
                 $parsed_item['anchor'] = $reference;
             }
             // update an existing section
             if (isset($parsed_item['handle']) && ($item = Sections::get($parsed_item['handle']))) {
                 // transcode section id
                 $parsed_item['id'] = $item['id'];
                 // stop on error
                 if (!Sections::put($parsed_item) || is_object($overlay) && !$overlay->remember('update', $parsed_item, 'section:' . $item['id'])) {
                     Logger::error(sprintf('Unable to save section %s', $parsed_item['title'] . ' (' . $parsed_item['id'] . ')'));
                 }
                 // create a new page
             } else {
                 unset($parsed_item['id']);
                 // stop on error
                 if (!($parsed_item['id'] = Sections::post($parsed_item))) {
                     Logger::error(sprintf('Unable to save section %s', $parsed_item['title']));
                 } else {
                     // save overlay content
                     if (is_object($overlay)) {
                         $overlay->remember('insert', $parsed_item, 'section:' . $parsed_item['id']);
                     }
                 }
             }
             // report to surfer
             $parsing_report .= '<li>' . Skin::build_link(Sections::get_permalink($parsed_item), $parsed_item['title']) . "</li>\n";
             // ready for next item
             $overlay_class = NULL;
             $overlay_parameters = '';
             $parsed_overlay = array();
             $parsed_item = array();
             Safe::set_time_limit(30);
             break;
         default:
             // just another attribute
             // decode cdata
             $parsed_cdata = trim(preg_replace(array('/&lt;/', '/&gt;/'), array('<', '>'), $parsed_cdata));
             // feeding the overlay or the item itself
             if ($in_overlay) {
                 $parsed_overlay[$tag] = $parsed_cdata;
             } else {
                 $parsed_item[$tag] = $parsed_cdata;
             }
             // ready for next attribute
             $parsed_cdata = '';
             break;
     }
 }
示例#12
0
文件: feeds.php 项目: rair/yacs
 /**
  * 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;
     }
 }