/** * 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); } } }
// 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')));
$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'];
/** * 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; }
$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;
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
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');
$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;
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>';
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()); }
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('/</', '/>/'), 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; } }
/** * 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; } }