function showXmlDirectMessages()
{
$this->initDocument('xml');
- $this->elementStart('direct-messages', array('type' => 'array'));
+ $this->elementStart('direct-messages', array('type' => 'array',
+ 'xmlns:statusnet' => 'http://status.net/schema/api/1/'));
foreach ($this->messages as $m) {
$dm_array = $this->directMessageArray($m);
parent::handle($args);
if (!in_array($this->format, array('xml', 'json'))) {
- $this->clientError(_('API method not found.'), $code = 404);
- return;
+ $this->clientError(
+ _('API method not found.'),
+ 404
+ );
+ return;
}
- if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
- $this->clientError(_('This method requires a POST or DELETE.'),
- 400, $this->format);
- return;
- }
-
- if (empty($this->notice)) {
- $this->clientError(_('No status found with that ID.'),
- 404, $this->format);
- return;
- }
-
- if ($this->user->id == $this->notice->profile_id) {
- $replies = new Reply;
- $replies->get('notice_id', $this->notice_id);
- $replies->delete();
- $this->notice->delete();
- $this->showNotice();
- } else {
- $this->clientError(_('You may not delete another user\'s status.'),
- 403, $this->format);
- }
+ if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
+ $this->clientError(
+ _('This method requires a POST or DELETE.'),
+ 400,
+ $this->format
+ );
+ return;
+ }
+
+ if (empty($this->notice)) {
+ $this->clientError(
+ _('No status found with that ID.'),
+ 404, $this->format
+ );
+ return;
+ }
+
+ if ($this->user->id == $this->notice->profile_id) {
+ $replies = new Reply;
+ $replies->get('notice_id', $this->notice_id);
+ $replies->delete();
+ $this->notice->delete();
+ $this->showNotice();
+ } else {
+ $this->clientError(
+ _('You may not delete another user\'s status.'),
+ 403,
+ $this->format
+ );
+ }
}
/**
if ($this->format == 'xml') {
$this->initDocument('xml');
- $this->showTwitterXmlUser($twitter_user);
+ $this->showTwitterXmlUser($twitter_user, 'user', true);
$this->endDocument('xml');
} elseif ($this->format == 'json') {
$this->initDocument('json');
return;
}
- // check for an image upload
+ // check for file uploads
$bgimage = $this->saveBackgroundImage();
+ $customTheme = $this->saveCustomTheme();
+
+ $oldtheme = common_config('site', 'theme');
+ if ($customTheme) {
+ // This feels pretty hacky :D
+ $this->args['theme'] = $customTheme;
+ $themeChanged = true;
+ } else {
+ $themeChanged = ($this->trimmed('theme') != $oldtheme);
+ }
static $settings = array('theme', 'logo');
$this->validate($values);
- $oldtheme = common_config('site', 'theme');
-
$config = new Config();
$config->query('BEGIN');
// Only update colors if the theme has not changed.
- if ($oldtheme == $values['theme']) {
+ if (!$themeChanged) {
$bgcolor = new WebColor($this->trimmed('design_background'));
$ccolor = new WebColor($this->trimmed('design_content'));
Config::save('design', 'backgroundimage', $bgimage);
}
+ if (common_config('custom_css', 'enabled')) {
+ $css = $this->arg('css');
+ if ($css != common_config('custom_css', 'css')) {
+ Config::save('custom_css', 'css', $css);
+ }
+ }
+
$config->query('COMMIT');
}
}
}
+ /**
+ * Save the custom theme if the user uploaded one.
+ *
+ * @return mixed custom theme name, if succesful, or null if no theme upload.
+ * @throws ClientException for invalid theme archives
+ * @throws ServerException if trouble saving the theme files
+ */
+
+ function saveCustomTheme()
+ {
+ if (common_config('theme_upload', 'enabled') &&
+ $_FILES['design_upload_theme']['error'] == UPLOAD_ERR_OK) {
+
+ $upload = ThemeUploader::fromUpload('design_upload_theme');
+ $basedir = common_config('local', 'dir');
+ if (empty($basedir)) {
+ $basedir = INSTALLDIR . '/local';
+ }
+ $name = 'custom'; // @todo allow multiples, custom naming?
+ $outdir = $basedir . '/theme/' . $name;
+ $upload->extract($outdir);
+ return $name;
+ } else {
+ return null;
+ }
+ }
+
/**
* Attempt to validate setting values
*
function formData()
{
+ $this->showLogo();
+ $this->showTheme();
+ $this->showBackground();
+ $this->showColors();
+ $this->showAdvanced();
+ }
+ function showLogo()
+ {
$this->out->elementStart('fieldset', array('id' => 'settings_design_logo'));
$this->out->element('legend', null, _('Change logo'));
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
+
+ }
+
+ function showTheme()
+ {
$this->out->elementStart('fieldset', array('id' => 'settings_design_theme'));
$this->out->element('legend', null, _('Change theme'));
false, $this->value('theme'));
$this->unli();
+ if (common_config('theme_upload', 'enabled')) {
+ $this->li();
+ $this->out->element('label', array('for' => 'design_upload_theme'), _('Custom theme'));
+ $this->out->element('input', array('id' => 'design_upload_theme',
+ 'name' => 'design_upload_theme',
+ 'type' => 'file'));
+ $this->out->element('p', 'form_guide', _('You can upload a custom StatusNet theme as a .ZIP archive.'));
+ $this->unli();
+ }
+
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
+ }
+ function showBackground()
+ {
$design = $this->out->design;
$this->out->elementStart('fieldset', array('id' =>
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
+ }
+
+ function showColors()
+ {
+ $design = $this->out->design;
$this->out->elementStart('fieldset', array('id' => 'settings_design_color'));
$this->out->element('legend', null, _('Change colours'));
$this->out->elementStart('ul', 'form_data');
try {
+ // @fixme avoid loop unrolling in non-performance-critical contexts like this
$bgcolor = new WebColor($design->backgroundcolor);
$this->unli();
} catch (WebColorException $e) {
+ // @fixme normalize them individually!
common_log(LOG_ERR, 'Bad color values in site design: ' .
$e->getMessage());
}
$this->out->elementEnd('ul');
}
+ function showAdvanced()
+ {
+ if (common_config('custom_css', 'enabled')) {
+ $this->out->elementStart('fieldset', array('id' => 'settings_design_advanced'));
+ $this->out->element('legend', null, _('Advanced'));
+ $this->out->elementStart('ul', 'form_data');
+
+ $this->li();
+ $this->out->element('label', array('for' => 'css'), _('Custom CSS'));
+ $this->out->element('textarea', array('name' => 'css',
+ 'id' => 'css',
+ 'cols' => '50',
+ 'rows' => '10'),
+ strval(common_config('custom_css', 'css')));
+ $this->unli();
+
+ $this->out->elementEnd('fieldset');
+ $this->out->elementEnd('ul');
+ }
+ }
+
/**
* Action elements
*
}
$person = $this->showMicrobloggingAccount($this->profile,
- common_root_url(), $this->user->uri, false);
+ common_root_url(), $this->user->uri,
+ /*$fetchSubscriptions*/true,
+ /*$isSubscriber*/false);
// Get people who subscribe to user
$this->showMicrobloggingAccount($profile,
($local == 'local') ? common_root_url() : null,
$uri,
- true);
+ /*$fetchSubscriptions*/false,
+ /*$isSubscriber*/($type == LISTENER || $type == BOTH));
if ($foaf_url) {
$this->element('rdfs:seeAlso', array('rdf:resource' => $foaf_url));
}
$this->elementEnd('PersonalProfileDocument');
}
- function showMicrobloggingAccount($profile, $service=null, $useruri=null, $isSubscriber=false)
+ /**
+ * Output FOAF <account> bit for the given profile.
+ *
+ * @param Profile $profile
+ * @param mixed $service Root URL of this StatusNet instance for a local
+ * user, otherwise null.
+ * @param mixed $useruri URI string for the referenced profile..
+ * @param boolean $fetchSubscriptions Should we load and list all their subscriptions?
+ * @param boolean $isSubscriber if not fetching subs, we can still mark the user as following the current page.
+ *
+ * @return array if $fetchSubscribers is set, return a list of info on those
+ * subscriptions.
+ */
+
+ function showMicrobloggingAccount($profile, $service=null, $useruri=null, $fetchSubscriptions=false, $isSubscriber=false)
{
$attr = array();
if ($useruri) {
$person = array();
- if ($isSubscriber) {
- $this->element('sioc:follows', array('rdf:resource'=>$this->user->uri . '#acct'));
- } else {
+ if ($fetchSubscriptions) {
// Get people user is subscribed to
$sub = new Subscription();
$sub->subscriber = $profile->id;
}
unset($sub);
+ } else if ($isSubscriber) {
+ // Just declare that they follow the user whose FOAF we're showing.
+ $this->element('sioc:follows', array('rdf:resource' => $this->user->uri . '#acct'));
}
$this->elementEnd('OnlineAccount');
}
static function cacheKey($cls, $k, $v) {
- if (is_object($cls) || is_object($k) || is_object($v)) {
+ if (is_object($cls) || is_object($k) || (is_object($v) && !($v instanceof DB_DataObject_Cast))) {
$e = new Exception();
common_log(LOG_ERR, __METHOD__ . ' object in param: ' .
str_replace("\n", " ", $e->getTraceAsString()));
}
- return common_cache_key(strtolower($cls).':'.$k.':'.$v);
+ $vstr = self::valueString($v);
+ return common_cache_key(strtolower($cls).':'.$k.':'.$vstr);
}
static function getcached($cls, $k, $v) {
if (empty($this->$key)) {
continue;
}
- $ckeys[] = $this->cacheKey($this->tableName(), $key, $this->$key);
+ $ckeys[] = $this->cacheKey($this->tableName(), $key, self::valueString($this->$key));
} else if ($type == 'K' || $type == 'N') {
$pkey[] = $key;
- $pval[] = $this->$key;
+ $pval[] = self::valueString($this->$key);
} else {
throw new Exception("Unknown key type $key => $type for " . $this->tableName());
}
* low-level database function and add a comment to the
* query string. This should then be visible in process lists
* and slow query logs, to help identify problem areas.
- *
+ *
* Also marks whether this was a web GET/POST or which daemon
* was running it.
*
return $c->set($cacheKey, $value);
}
+
+ static function valueString($v)
+ {
+ $vstr = null;
+ if (is_object($v) && $v instanceof DB_DataObject_Cast) {
+ switch ($v->type) {
+ case 'date':
+ $vstr = $v->year . '-' . $v->month . '-' . $v->day;
+ break;
+ case 'blob':
+ case 'string':
+ case 'sql':
+ case 'datetime':
+ case 'time':
+ throw new ServerException("Unhandled DB_DataObject_Cast type passed as cacheKey value: '$v->type'");
+ break;
+ default:
+ throw new ServerException("Unknown DB_DataObject_Cast type passed as cacheKey value: '$v->type'");
+ break;
+ }
+ } else {
+ $vstr = strval($v);
+ }
+ return $vstr;
+ }
}
if (!empty($cur)) {
$noticeInfoAttr['favorite'] = ($cur->hasFave($this)) ? "true" : "false";
+ $profile = $cur->getProfile();
+ $noticeInfoAttr['repeated'] = ($profile->hasRepeated($this->id)) ? "true" : "false";
}
if (!empty($this->repeat_of)) {
('Facebook','Facebook','http://apps.facebook.com/identica/', now()),
('feed2omb','feed2omb','http://projects.ciarang.com/p/feed2omb/', now()),
('get2gnow', 'get2gnow', 'http://uberchicgeekchick.com/?projects=get2gnow', now()),
+ ('gNewBook', 'gNewBook', 'http://www.gnewbook.org/', now()),
('gravity', 'Gravity', 'http://mobileways.de/gravity', now()),
('Gwibber','Gwibber','http://launchpad.net/gwibber', now()),
('HelloTxt','HelloTxt','http://hellotxt.com/', now()),
Event::handle('EndShowDesign', array($this));
}
Event::handle('EndShowStyles', array($this));
+
+ if (common_config('custom_css', 'enabled')) {
+ $css = common_config('custom_css', 'css');
+ if (Event::handle('StartShowCustomCss', array($this, &$css))) {
+ if (trim($css) != '') {
+ $this->style($css);
+ }
+ Event::handle('EndShowCustomCss', array($this));
+ }
+ }
}
}
$this->clientError(_("Unable to delete design setting."));
return null;
}
+ return $result;
}
- return $result;
+ return null;
}
function canAdmin($name)
}
}
+ // StatusNet-specific
+
+ $twitter_user['statusnet:profile_url'] = $profile->profileurl;
+
return $twitter_user;
}
$twitter_status['user'] = $twitter_user;
}
+ // StatusNet-specific
+
+ $twitter_status['statusnet:html'] = $notice->rendered;
+
return $twitter_status;
}
}
}
- function showTwitterXmlStatus($twitter_status, $tag='status')
+ function showTwitterXmlStatus($twitter_status, $tag='status', $namespaces=false)
{
- $this->elementStart($tag);
+ $attrs = array();
+ if ($namespaces) {
+ $attrs['xmlns:statusnet'] = 'http://status.net/schema/api/1/';
+ }
+ $this->elementStart($tag, $attrs);
foreach($twitter_status as $element => $value) {
switch ($element) {
case 'user':
$this->elementEnd('group');
}
- function showTwitterXmlUser($twitter_user, $role='user')
+ function showTwitterXmlUser($twitter_user, $role='user', $namespaces=false)
{
- $this->elementStart($role);
+ $attrs = array();
+ if ($namespaces) {
+ $attrs['xmlns:statusnet'] = 'http://status.net/schema/api/1/';
+ }
+ $this->elementStart($role, $attrs);
foreach($twitter_user as $element => $value) {
if ($element == 'status') {
$this->showTwitterXmlStatus($twitter_user['status']);
{
$this->initDocument('xml');
$twitter_status = $this->twitterStatusArray($notice);
- $this->showTwitterXmlStatus($twitter_status);
+ $this->showTwitterXmlStatus($twitter_status, 'status', true);
$this->endDocument('xml');
}
{
$this->initDocument('xml');
- $this->elementStart('statuses', array('type' => 'array'));
+ $this->elementStart('statuses', array('type' => 'array',
+ 'xmlns:statusnet' => 'http://status.net/schema/api/1/'));
if (is_array($notice)) {
foreach ($notice as $n) {
$this->elementEnd('entry');
}
- function showXmlDirectMessage($dm)
+ function showXmlDirectMessage($dm, $namespaces=false)
{
- $this->elementStart('direct_message');
+ $attrs = array();
+ if ($namespaces) {
+ $attrs['xmlns:statusnet'] = 'http://status.net/schema/api/1/';
+ }
+ $this->elementStart('direct_message', $attrs);
foreach($dm as $element => $value) {
switch ($element) {
case 'sender':
{
$this->initDocument('xml');
$dmsg = $this->directMessageArray($message);
- $this->showXmlDirectMessage($dmsg);
+ $this->showXmlDirectMessage($dmsg, true);
$this->endDocument('xml');
}
{
$this->initDocument('xml');
- $this->elementStart('users', array('type' => 'array'));
+ $this->elementStart('users', array('type' => 'array',
+ 'xmlns:statusnet' => 'http://status.net/schema/api/1/'));
if (is_array($user)) {
foreach ($user as $u) {
'dir' => null,
'path'=> null,
'ssl' => null),
+ 'theme_upload' =>
+ array('enabled' => extension_loaded('zip')),
'javascript' =>
array('server' => null,
'path'=> null,
'ssl' => null),
+ 'local' => // To override path/server for themes in 'local' dir (not currently applied to local plugins)
+ array('server' => null,
+ 'dir' => null,
+ 'path' => null,
+ 'ssl' => null),
'throttle' =>
array('enabled' => false, // whether to throttle edits; false by default
'count' => 20, // number of allowed messages in timespan
'linkcolor' => null,
'backgroundimage' => null,
'disposition' => null),
+ 'custom_css' =>
+ array('enabled' => true,
+ 'css' => ''),
'notice' =>
array('contentlimit' => null),
'message' =>
{
$pass = true;
- if (file_exists(INSTALLDIR.'/config.php')) {
- $this->warning('Config file "config.php" already exists.');
- $pass = false;
+ $config = INSTALLDIR.'/config.php';
+ if (file_exists($config)) {
+ if (!is_writable($config) || filesize($config) > 0) {
+ $this->warning('Config file "config.php" already exists.');
+ $pass = false;
+ }
}
if (version_compare(PHP_VERSION, '5.2.3', '<')) {
$m->connect('api/favorites/:id.:format',
array('action' => 'ApiTimelineFavorites',
'id' => '[a-zA-Z0-9]+',
- 'format' => '(xmljson|rss|atom)'));
+ 'format' => '(xml|json|rss|atom)'));
$m->connect('api/favorites/create/:id.:format',
array('action' => 'ApiFavoriteCreate',
$m->connect('api/statusnet/groups/timeline/:id.:format',
array('action' => 'ApiTimelineGroup',
'id' => '[a-zA-Z0-9]+',
- 'format' => '(xmljson|rss|atom)'));
+ 'format' => '(xml|json|rss|atom)'));
$m->connect('api/statusnet/groups/show.:format',
array('action' => 'ApiGroupShow',
// Tags
$m->connect('api/statusnet/tags/timeline/:tag.:format',
array('action' => 'ApiTimelineTag',
- 'format' => '(xmljson|rss|atom)'));
+ 'format' => '(xml|json|rss|atom)'));
// media related
$m->connect(
* Themes are directories with some expected sub-directories and files
* in them. They're found in either local/theme (for locally-installed themes)
* or theme/ subdir of installation dir.
+ *
+ * Note that the 'local' directory can be overridden as $config['local']['path']
+ * and $config['local']['dir'] etc.
*
* This used to be a couple of functions, but for various reasons it's nice
* to have a class instead.
if (file_exists($fulldir) && is_dir($fulldir)) {
$this->dir = $fulldir;
- $this->path = common_path('local/theme/'.$name.'/');
+ $this->path = $this->relativeThemePath('local', 'local', 'theme/' . $name);
return;
}
if (file_exists($fulldir) && is_dir($fulldir)) {
$this->dir = $fulldir;
+ $this->path = $this->relativeThemePath('theme', 'theme', $name);
+ }
+ }
- $path = common_config('theme', 'path');
+ /**
+ * Build a full URL to the given theme's base directory, possibly
+ * using an offsite theme server path.
+ *
+ * @param string $group configuration section name to pull paths from
+ * @param string $fallbackSubdir default subdirectory under INSTALLDIR
+ * @param string $name theme name
+ *
+ * @return string URL
+ *
+ * @todo consolidate code with that for other customizable paths
+ */
- if (empty($path)) {
- $path = common_config('site', 'path') . '/theme/';
- }
+ protected function relativeThemePath($group, $fallbackSubdir, $name)
+ {
+ $path = common_config($group, 'path');
- if ($path[strlen($path)-1] != '/') {
- $path .= '/';
+ if (empty($path)) {
+ $path = common_config('site', 'path') . '/';
+ if ($fallbackSubdir) {
+ $path .= $fallbackSubdir . '/';
}
+ }
- if ($path[0] != '/') {
- $path = '/'.$path;
- }
+ if ($path[strlen($path)-1] != '/') {
+ $path .= '/';
+ }
- $server = common_config('theme', 'server');
+ if ($path[0] != '/') {
+ $path = '/'.$path;
+ }
- if (empty($server)) {
- $server = common_config('site', 'server');
- }
+ $server = common_config($group, 'server');
- $ssl = common_config('theme', 'ssl');
+ if (empty($server)) {
+ $server = common_config('site', 'server');
+ }
- if (is_null($ssl)) { // null -> guess
- if (common_config('site', 'ssl') == 'always' &&
- !common_config('theme', 'server')) {
- $ssl = true;
- } else {
- $ssl = false;
- }
+ $ssl = common_config($group, 'ssl');
+
+ if (is_null($ssl)) { // null -> guess
+ if (common_config('site', 'ssl') == 'always' &&
+ !common_config($group, 'server')) {
+ $ssl = true;
+ } else {
+ $ssl = false;
}
+ }
- $protocol = ($ssl) ? 'https' : 'http';
+ $protocol = ($ssl) ? 'https' : 'http';
- $this->path = $protocol . '://'.$server.$path.$name;
- }
+ $path = $protocol . '://'.$server.$path.$name;
+ return $path;
}
/**
protected static function localRoot()
{
- return INSTALLDIR.'/local/theme';
+ $basedir = common_config('local', 'dir');
+
+ if (empty($basedir)) {
+ $basedir = INSTALLDIR . '/local';
+ }
+
+ return $basedir . '/theme';
}
/**
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Utilities for theme files and paths
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Paths
+ * @package StatusNet
+ * @author Brion Vibber <brion@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+/**
+ * Encapsulation of the validation-and-save process when dealing with
+ * a user-uploaded StatusNet theme archive...
+ *
+ * @todo extract theme metadata from css/display.css
+ * @todo allow saving multiple themes
+ */
+class ThemeUploader
+{
+ protected $sourceFile;
+ protected $isUpload;
+ private $prevErrorReporting;
+
+ public function __construct($filename)
+ {
+ if (!class_exists('ZipArchive')) {
+ throw new Exception(_("This server cannot handle theme uploads without ZIP support."));
+ }
+ $this->sourceFile = $filename;
+ }
+
+ public static function fromUpload($name)
+ {
+ if (!isset($_FILES[$name]['error'])) {
+ throw new ServerException(_("Theme upload missing or failed."));
+ }
+ if ($_FILES[$name]['error'] != UPLOAD_ERR_OK) {
+ throw new ServerException(_("Theme upload missing or failed."));
+ }
+ return new ThemeUploader($_FILES[$name]['tmp_name']);
+ }
+
+ /**
+ * @param string $destDir
+ * @throws Exception on bogus files
+ */
+ public function extract($destDir)
+ {
+ $zip = $this->openArchive();
+
+ // First pass: validate but don't save anything to disk.
+ // Any errors will trip an exception.
+ $this->traverseArchive($zip);
+
+ // Second pass: now that we know we're good, actually extract!
+ $tmpDir = $destDir . '.tmp' . getmypid();
+ $this->traverseArchive($zip, $tmpDir);
+
+ $zip->close();
+
+ if (file_exists($destDir)) {
+ $killDir = $tmpDir . '.old';
+ $this->quiet();
+ $ok = rename($destDir, $killDir);
+ $this->loud();
+ if (!$ok) {
+ common_log(LOG_ERR, "Could not move old custom theme from $destDir to $killDir");
+ throw new ServerException(_("Failed saving theme."));
+ }
+ } else {
+ $killDir = false;
+ }
+
+ $this->quiet();
+ $ok = rename($tmpDir, $destDir);
+ $this->loud();
+ if (!$ok) {
+ common_log(LOG_ERR, "Could not move saved theme from $tmpDir to $destDir");
+ throw new ServerException(_("Failed saving theme."));
+ }
+
+ if ($killDir) {
+ $this->recursiveRmdir($killDir);
+ }
+ }
+
+ /**
+ *
+ */
+ protected function traverseArchive($zip, $outdir=false)
+ {
+ $sizeLimit = 2 * 1024 * 1024; // 2 megabyte space limit?
+ $blockSize = 4096; // estimated; any entry probably takes this much space
+
+ $totalSize = 0;
+ $hasMain = false;
+ $commonBaseDir = false;
+
+ for ($i = 0; $i < $zip->numFiles; $i++) {
+ $data = $zip->statIndex($i);
+ $name = str_replace('\\', '/', $data['name']);
+
+ if (substr($name, -1) == '/') {
+ // A raw directory... skip!
+ continue;
+ }
+
+ // Check the directory structure...
+ $path = pathinfo($name);
+ $dirs = explode('/', $path['dirname']);
+ $baseDir = array_shift($dirs);
+ if ($commonBaseDir === false) {
+ $commonBaseDir = $baseDir;
+ } else {
+ if ($commonBaseDir != $baseDir) {
+ throw new ClientException(_("Invalid theme: bad directory structure."));
+ }
+ }
+
+ foreach ($dirs as $dir) {
+ $this->validateFileOrFolder($dir);
+ }
+
+ // Is this a safe or skippable file?
+ if ($this->skippable($path['filename'], $path['extension'])) {
+ // Documentation and such... booooring
+ continue;
+ } else {
+ $this->validateFile($path['filename'], $path['extension']);
+ }
+
+ $fullPath = $dirs;
+ $fullPath[] = $path['basename'];
+ $localFile = implode('/', $fullPath);
+ if ($localFile == 'css/display.css') {
+ $hasMain = true;
+ }
+
+ $size = $data['size'];
+ $estSize = $blockSize * max(1, intval(ceil($size / $blockSize)));
+ $totalSize += $estSize;
+ if ($totalSize > $sizeLimit) {
+ $msg = sprintf(_("Uploaded theme is too large; " .
+ "must be less than %d bytes uncompressed."),
+ $sizeLimit);
+ throw new ClientException($msg);
+ }
+
+ if ($outdir) {
+ $this->extractFile($zip, $data['name'], "$outdir/$localFile");
+ }
+ }
+
+ if (!$hasMain) {
+ throw new ClientException(_("Invalid theme archive: " .
+ "missing file css/display.css"));
+ }
+ }
+
+ protected function skippable($filename, $ext)
+ {
+ $skip = array('txt', 'rtf', 'doc', 'docx', 'odt');
+ if (strtolower($filename) == 'readme') {
+ return true;
+ }
+ if (in_array(strtolower($ext), $skip)) {
+ return true;
+ }
+ return false;
+ }
+
+ protected function validateFile($filename, $ext)
+ {
+ $this->validateFileOrFolder($filename);
+ $this->validateExtension($ext);
+ // @fixme validate content
+ }
+
+ protected function validateFileOrFolder($name)
+ {
+ if (!preg_match('/^[a-z0-9_-]+$/i', $name)) {
+ $msg = _("Theme contains invalid file or folder name. " .
+ "Stick with ASCII letters, digits, underscore, and minus sign.");
+ throw new ClientException($msg);
+ }
+ return true;
+ }
+
+ protected function validateExtension($ext)
+ {
+ $allowed = array('css', 'png', 'gif', 'jpg', 'jpeg');
+ if (!in_array(strtolower($ext), $allowed)) {
+ $msg = sprintf(_("Theme contains file of type '.%s', " .
+ "which is not allowed."),
+ $ext);
+ throw new ClientException($msg);
+ }
+ return true;
+ }
+
+ /**
+ * @return ZipArchive
+ */
+ protected function openArchive()
+ {
+ $zip = new ZipArchive;
+ $ok = $zip->open($this->sourceFile);
+ if ($ok !== true) {
+ common_log(LOG_ERR, "Error opening theme zip archive: " .
+ "{$this->sourceFile} code: {$ok}");
+ throw new Exception(_("Error opening theme archive."));
+ }
+ return $zip;
+ }
+
+ /**
+ * @param ZipArchive $zip
+ * @param string $from original path inside ZIP archive
+ * @param string $to final destination path in filesystem
+ */
+ protected function extractFile($zip, $from, $to)
+ {
+ $dir = dirname($to);
+ if (!file_exists($dir)) {
+ $this->quiet();
+ $ok = mkdir($dir, 0755, true);
+ $this->loud();
+ if (!$ok) {
+ common_log(LOG_ERR, "Failed to mkdir $dir while uploading theme");
+ throw new ServerException(_("Failed saving theme."));
+ }
+ } else if (!is_dir($dir)) {
+ common_log(LOG_ERR, "Output directory $dir not a directory while uploading theme");
+ throw new ServerException(_("Failed saving theme."));
+ }
+
+ // ZipArchive::extractTo would be easier, but won't let us alter
+ // the directory structure.
+ $in = $zip->getStream($from);
+ if (!$in) {
+ common_log(LOG_ERR, "Couldn't open archived file $from while uploading theme");
+ throw new ServerException(_("Failed saving theme."));
+ }
+ $this->quiet();
+ $out = fopen($to, "wb");
+ $this->loud();
+ if (!$out) {
+ common_log(LOG_ERR, "Couldn't open output file $to while uploading theme");
+ throw new ServerException(_("Failed saving theme."));
+ }
+ while (!feof($in)) {
+ $buffer = fread($in, 65536);
+ fwrite($out, $buffer);
+ }
+ fclose($in);
+ fclose($out);
+ }
+
+ private function quiet()
+ {
+ $this->prevErrorReporting = error_reporting();
+ error_reporting($this->prevErrorReporting & ~E_WARNING);
+ }
+
+ private function loud()
+ {
+ error_reporting($this->prevErrorReporting);
+ }
+
+ private function recursiveRmdir($dir)
+ {
+ $list = dir($dir);
+ while (($file = $list->read()) !== false) {
+ if ($file == '.' || $file == '..') {
+ continue;
+ }
+ $full = "$dir/$file";
+ if (is_dir($full)) {
+ $this->recursiveRmdir($full);
+ } else {
+ unlink($full);
+ }
+ }
+ $list->close();
+ rmdir($dir);
+ }
+
+}
msgstr ""
"Project-Id-Version: StatusNet\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-05-25 11:36+0000\n"
-"PO-Revision-Date: 2010-06-03 23:01:09+0000\n"
+"POT-Creation-Date: 2010-06-10 22:48+0000\n"
+"PO-Revision-Date: 2010-06-10 22:49:22+0000\n"
"Language-Team: German\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: MediaWiki 1.17alpha (r67302); Translate extension (2010-05-24)\n"
+"X-Generator: MediaWiki 1.17alpha (r67833); Translate extension (2010-06-10)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: de\n"
"X-Message-Group: out-statusnet\n"
msgstr "Speichern"
#. TRANS: Server error when page not found (404)
-#: actions/all.php:65 actions/public.php:98 actions/replies.php:93
+#: actions/all.php:68 actions/public.php:98 actions/replies.php:93
#: actions/showfavorites.php:138 actions/tag.php:52
msgid "No such page."
msgstr "Seite nicht vorhanden"
-#: actions/all.php:76 actions/allrss.php:68
-#: actions/apiaccountupdatedeliverydevice.php:113
+#: actions/all.php:79 actions/allrss.php:68
+#: actions/apiaccountupdatedeliverydevice.php:114
#: actions/apiaccountupdateprofile.php:105
#: actions/apiaccountupdateprofilebackgroundimage.php:116
#: actions/apiaccountupdateprofileimage.php:105 actions/apiblockcreate.php:97
msgstr "Unbekannter Benutzer."
#. TRANS: Page title. %1$s is user nickname, %2$d is page number
-#: actions/all.php:87
+#: actions/all.php:90
#, php-format
msgid "%1$s and friends, page %2$d"
msgstr "%1$s und Freunde, Seite% 2$d"
#. TRANS: Page title. %1$s is user nickname
#. TRANS: H1 text. %1$s is user nickname
#. TRANS: Message is used as link title. %s is a user nickname.
-#: actions/all.php:90 actions/all.php:182 actions/allrss.php:116
+#: actions/all.php:93 actions/all.php:185 actions/allrss.php:116
#: actions/apitimelinefriends.php:210 actions/apitimelinehome.php:116
#: lib/personalgroupnav.php:100
#, php-format
msgstr "%s und Freunde"
#. TRANS: %1$s is user nickname
-#: actions/all.php:104
+#: actions/all.php:107
#, php-format
msgid "Feed for friends of %s (RSS 1.0)"
msgstr "Feed der Freunde von %s (RSS 1.0)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:113
+#: actions/all.php:116
#, php-format
msgid "Feed for friends of %s (RSS 2.0)"
msgstr "Feed der Freunde von %s (RSS 2.0)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:122
+#: actions/all.php:125
#, php-format
msgid "Feed for friends of %s (Atom)"
msgstr "Feed der Freunde von %s (Atom)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:135
+#: actions/all.php:138
#, php-format
msgid ""
"This is the timeline for %s and friends but no one has posted anything yet."
"Dies ist die Zeitleiste für %s und Freunde aber bisher hat niemand etwas "
"gepostet."
-#: actions/all.php:140
+#: actions/all.php:143
#, php-format
msgid ""
"Try subscribing to more people, [join a group](%%action.groups%%) or post "
"poste selber etwas."
#. TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@"
-#: actions/all.php:143
+#: actions/all.php:146
#, php-format
msgid ""
"You can try to [nudge %1$s](../%2$s) from his profile or [post something to "
"posten](%%%%action.newnotice%%%%?status_textarea=%s) um seine Aufmerksamkeit "
"zu erregen."
-#: actions/all.php:146 actions/replies.php:210 actions/showstream.php:211
+#: actions/all.php:149 actions/replies.php:210 actions/showstream.php:211
#, php-format
msgid ""
"Why not [register an account](%%%%action.register%%%%) and then nudge %s or "
"erregen?"
#. TRANS: H1 text
-#: actions/all.php:179
+#: actions/all.php:182
msgid "You and friends"
msgstr "Du und Freunde"
msgid "Updates from %1$s and friends on %2$s!"
msgstr "Aktualisierungen von %1$s und Freunden auf %2$s!"
-#: actions/apiaccountratelimitstatus.php:70
-#: actions/apiaccountupdatedeliverydevice.php:93
+#: actions/apiaccountratelimitstatus.php:72
+#: actions/apiaccountupdatedeliverydevice.php:94
#: actions/apiaccountupdateprofile.php:97
#: actions/apiaccountupdateprofilebackgroundimage.php:94
#: actions/apiaccountupdateprofilecolors.php:118
msgid "API method not found."
msgstr "API-Methode nicht gefunden."
-#: actions/apiaccountupdatedeliverydevice.php:85
+#: actions/apiaccountupdatedeliverydevice.php:86
#: actions/apiaccountupdateprofile.php:89
#: actions/apiaccountupdateprofilebackgroundimage.php:86
#: actions/apiaccountupdateprofilecolors.php:110
msgid "This method requires a POST."
msgstr "Diese Methode benötigt ein POST."
-#: actions/apiaccountupdatedeliverydevice.php:105
+#: actions/apiaccountupdatedeliverydevice.php:106
msgid ""
"You must specify a parameter named 'device' with a value of one of: sms, im, "
"none."
"Du musst einen Parameter mit Namen 'device' übergeben. Mögliche Werte sind: "
"sms, im, none."
-#: actions/apiaccountupdatedeliverydevice.php:132
+#: actions/apiaccountupdatedeliverydevice.php:133
msgid "Could not update user."
msgstr "Konnte Benutzerdaten nicht aktualisieren."
msgid "This method requires a POST or DELETE."
msgstr "Diese Methode benötigt ein POST oder DELETE."
-#: actions/apistatusesdestroy.php:131
+#: actions/apistatusesdestroy.php:126
msgid "You may not delete another user's status."
msgstr "Du kannst den Status eines anderen Benutzers nicht löschen."
"If you believe this account is being used abusively, you can block them from "
"your subscribers list and report as spam to site administrators at %s"
msgstr ""
+"Wenn du dir sicher bist, das dieses Benutzerkonto missbräuchlich benutzt "
+"wurde, kannst du das Benutzerkonto von deiner Liste der Abonnenten sperren "
+"und es den Seitenadministratoren unter %s als Spam melden."
#. TRANS: Main body of new-subscriber notification e-mail
#: lib/mail.php:254
msgstr ""
"Project-Id-Version: StatusNet\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-05-25 11:36+0000\n"
-"PO-Revision-Date: 2010-06-03 23:01:38+0000\n"
+"POT-Creation-Date: 2010-06-10 22:48+0000\n"
+"PO-Revision-Date: 2010-06-10 22:49:53+0000\n"
"Language-Team: French\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: MediaWiki 1.17alpha (r67302); Translate extension (2010-05-24)\n"
+"X-Generator: MediaWiki 1.17alpha (r67833); Translate extension (2010-06-10)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: fr\n"
"X-Message-Group: out-statusnet\n"
msgstr "Enregistrer"
#. TRANS: Server error when page not found (404)
-#: actions/all.php:65 actions/public.php:98 actions/replies.php:93
+#: actions/all.php:68 actions/public.php:98 actions/replies.php:93
#: actions/showfavorites.php:138 actions/tag.php:52
msgid "No such page."
msgstr "Page non trouvée."
-#: actions/all.php:76 actions/allrss.php:68
-#: actions/apiaccountupdatedeliverydevice.php:113
+#: actions/all.php:79 actions/allrss.php:68
+#: actions/apiaccountupdatedeliverydevice.php:114
#: actions/apiaccountupdateprofile.php:105
#: actions/apiaccountupdateprofilebackgroundimage.php:116
#: actions/apiaccountupdateprofileimage.php:105 actions/apiblockcreate.php:97
msgstr "Utilisateur non trouvé."
#. TRANS: Page title. %1$s is user nickname, %2$d is page number
-#: actions/all.php:87
+#: actions/all.php:90
#, php-format
msgid "%1$s and friends, page %2$d"
msgstr "%1$s et ses amis, page %2$d"
#. TRANS: Page title. %1$s is user nickname
#. TRANS: H1 text. %1$s is user nickname
#. TRANS: Message is used as link title. %s is a user nickname.
-#: actions/all.php:90 actions/all.php:182 actions/allrss.php:116
+#: actions/all.php:93 actions/all.php:185 actions/allrss.php:116
#: actions/apitimelinefriends.php:210 actions/apitimelinehome.php:116
#: lib/personalgroupnav.php:100
#, php-format
msgstr "%s et ses amis"
#. TRANS: %1$s is user nickname
-#: actions/all.php:104
+#: actions/all.php:107
#, php-format
msgid "Feed for friends of %s (RSS 1.0)"
msgstr "Flux pour les amis de %s (RSS 1.0)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:113
+#: actions/all.php:116
#, php-format
msgid "Feed for friends of %s (RSS 2.0)"
msgstr "Flux pour les amis de %s (RSS 2.0)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:122
+#: actions/all.php:125
#, php-format
msgid "Feed for friends of %s (Atom)"
msgstr "Flux pour les amis de %s (Atom)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:135
+#: actions/all.php:138
#, php-format
msgid ""
"This is the timeline for %s and friends but no one has posted anything yet."
"Ceci est le flux pour %s et ses amis mais personne n’a rien posté pour le "
"moment."
-#: actions/all.php:140
+#: actions/all.php:143
#, php-format
msgid ""
"Try subscribing to more people, [join a group](%%action.groups%%) or post "
"(%%action.groups%%) ou de poster quelque chose vous-même."
#. TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@"
-#: actions/all.php:143
+#: actions/all.php:146
#, php-format
msgid ""
"You can try to [nudge %1$s](../%2$s) from his profile or [post something to "
"profil ou [poster quelque chose à son intention](%%%%action.newnotice%%%%?"
"status_textarea=%3$s)."
-#: actions/all.php:146 actions/replies.php:210 actions/showstream.php:211
+#: actions/all.php:149 actions/replies.php:210 actions/showstream.php:211
#, php-format
msgid ""
"Why not [register an account](%%%%action.register%%%%) and then nudge %s or "
"un clin d’œil à %s ou poster un avis à son intention."
#. TRANS: H1 text
-#: actions/all.php:179
+#: actions/all.php:182
msgid "You and friends"
msgstr "Vous et vos amis"
msgid "Updates from %1$s and friends on %2$s!"
msgstr "Statuts de %1$s et ses amis dans %2$s!"
-#: actions/apiaccountratelimitstatus.php:70
-#: actions/apiaccountupdatedeliverydevice.php:93
+#: actions/apiaccountratelimitstatus.php:72
+#: actions/apiaccountupdatedeliverydevice.php:94
#: actions/apiaccountupdateprofile.php:97
#: actions/apiaccountupdateprofilebackgroundimage.php:94
#: actions/apiaccountupdateprofilecolors.php:118
msgid "API method not found."
msgstr "Méthode API non trouvée !"
-#: actions/apiaccountupdatedeliverydevice.php:85
+#: actions/apiaccountupdatedeliverydevice.php:86
#: actions/apiaccountupdateprofile.php:89
#: actions/apiaccountupdateprofilebackgroundimage.php:86
#: actions/apiaccountupdateprofilecolors.php:110
msgid "This method requires a POST."
msgstr "Ce processus requiert un POST."
-#: actions/apiaccountupdatedeliverydevice.php:105
+#: actions/apiaccountupdatedeliverydevice.php:106
msgid ""
"You must specify a parameter named 'device' with a value of one of: sms, im, "
"none."
"Vous devez spécifier un paramètre « device » avec une des valeurs suivantes : "
"sms, im, none."
-#: actions/apiaccountupdatedeliverydevice.php:132
+#: actions/apiaccountupdatedeliverydevice.php:133
msgid "Could not update user."
msgstr "Impossible de mettre à jour l’utilisateur."
msgid "This method requires a POST or DELETE."
msgstr "Ce processus requiert un POST ou un DELETE."
-#: actions/apistatusesdestroy.php:131
+#: actions/apistatusesdestroy.php:126
msgid "You may not delete another user's status."
msgstr "Vous ne pouvez pas supprimer le statut d’un autre utilisateur."
"If you believe this account is being used abusively, you can block them from "
"your subscribers list and report as spam to site administrators at %s"
msgstr ""
+"Si vous pensez que ce compte est utilisé à des fins abusives, vous pouvez le "
+"bloquer de votre liste d'abonnés et le signaler comme spam aux "
+"administrateurs du site, sur %s."
#. TRANS: Main body of new-subscriber notification e-mail
#: lib/mail.php:254
msgstr ""
"Project-Id-Version: StatusNet\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-05-25 11:36+0000\n"
-"PO-Revision-Date: 2010-06-03 23:01:55+0000\n"
+"POT-Creation-Date: 2010-06-10 22:48+0000\n"
+"PO-Revision-Date: 2010-06-10 22:50:14+0000\n"
"Language-Team: Dutch\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: MediaWiki 1.17alpha (r67302); Translate extension (2010-05-24)\n"
+"X-Generator: MediaWiki 1.17alpha (r67833); Translate extension (2010-06-10)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: hsb\n"
"X-Message-Group: out-statusnet\n"
#. TRANS: Checkbox instructions for admin setting "Private"
#: actions/accessadminpanel.php:165
msgid "Prohibit anonymous users (not logged in) from viewing site?"
-msgstr ""
+msgstr "Anonymnym wužiwarjam (njepřizjewjenym) wobhladowanje sydła zakazć?"
#. TRANS: Checkbox label for prohibiting anonymous users from viewing site.
#: actions/accessadminpanel.php:167
msgstr "Składować"
#. TRANS: Server error when page not found (404)
-#: actions/all.php:65 actions/public.php:98 actions/replies.php:93
+#: actions/all.php:68 actions/public.php:98 actions/replies.php:93
#: actions/showfavorites.php:138 actions/tag.php:52
msgid "No such page."
msgstr "Strona njeeksistuje."
-#: actions/all.php:76 actions/allrss.php:68
-#: actions/apiaccountupdatedeliverydevice.php:113
+#: actions/all.php:79 actions/allrss.php:68
+#: actions/apiaccountupdatedeliverydevice.php:114
#: actions/apiaccountupdateprofile.php:105
#: actions/apiaccountupdateprofilebackgroundimage.php:116
#: actions/apiaccountupdateprofileimage.php:105 actions/apiblockcreate.php:97
msgstr "Wužiwar njeeksistuje"
#. TRANS: Page title. %1$s is user nickname, %2$d is page number
-#: actions/all.php:87
+#: actions/all.php:90
#, php-format
msgid "%1$s and friends, page %2$d"
msgstr "%1$s a přećeljo, strona %2$d"
#. TRANS: Page title. %1$s is user nickname
#. TRANS: H1 text. %1$s is user nickname
#. TRANS: Message is used as link title. %s is a user nickname.
-#: actions/all.php:90 actions/all.php:182 actions/allrss.php:116
+#: actions/all.php:93 actions/all.php:185 actions/allrss.php:116
#: actions/apitimelinefriends.php:210 actions/apitimelinehome.php:116
#: lib/personalgroupnav.php:100
#, php-format
msgstr "%s a přećeljo"
#. TRANS: %1$s is user nickname
-#: actions/all.php:104
+#: actions/all.php:107
#, php-format
msgid "Feed for friends of %s (RSS 1.0)"
msgstr "Kanal za přećelow wužiwarja %s (RSS 1.0)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:113
+#: actions/all.php:116
#, php-format
msgid "Feed for friends of %s (RSS 2.0)"
msgstr "Kanal za přećelow wužiwarja %s (RSS 2.0)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:122
+#: actions/all.php:125
#, php-format
msgid "Feed for friends of %s (Atom)"
msgstr "Kanal za přećelow wužiwarja %s (Atom)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:135
+#: actions/all.php:138
#, php-format
msgid ""
"This is the timeline for %s and friends but no one has posted anything yet."
msgstr ""
-#: actions/all.php:140
+#: actions/all.php:143
#, php-format
msgid ""
"Try subscribing to more people, [join a group](%%action.groups%%) or post "
msgstr ""
#. TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@"
-#: actions/all.php:143
+#: actions/all.php:146
#, php-format
msgid ""
"You can try to [nudge %1$s](../%2$s) from his profile or [post something to "
"his or her attention](%%%%action.newnotice%%%%?status_textarea=%3$s)."
msgstr ""
-#: actions/all.php:146 actions/replies.php:210 actions/showstream.php:211
+#: actions/all.php:149 actions/replies.php:210 actions/showstream.php:211
#, php-format
msgid ""
"Why not [register an account](%%%%action.register%%%%) and then nudge %s or "
msgstr ""
#. TRANS: H1 text
-#: actions/all.php:179
+#: actions/all.php:182
msgid "You and friends"
msgstr "Ty a přećeljo"
msgid "Updates from %1$s and friends on %2$s!"
msgstr "Aktualizacije wot %1$s a přećelow na %2$s!"
-#: actions/apiaccountratelimitstatus.php:70
-#: actions/apiaccountupdatedeliverydevice.php:93
+#: actions/apiaccountratelimitstatus.php:72
+#: actions/apiaccountupdatedeliverydevice.php:94
#: actions/apiaccountupdateprofile.php:97
#: actions/apiaccountupdateprofilebackgroundimage.php:94
#: actions/apiaccountupdateprofilecolors.php:118
msgid "API method not found."
msgstr "API-metoda njenamakana."
-#: actions/apiaccountupdatedeliverydevice.php:85
+#: actions/apiaccountupdatedeliverydevice.php:86
#: actions/apiaccountupdateprofile.php:89
#: actions/apiaccountupdateprofilebackgroundimage.php:86
#: actions/apiaccountupdateprofilecolors.php:110
msgid "This method requires a POST."
msgstr "Tuta metoda wužaduje sej POST."
-#: actions/apiaccountupdatedeliverydevice.php:105
+#: actions/apiaccountupdatedeliverydevice.php:106
msgid ""
"You must specify a parameter named 'device' with a value of one of: sms, im, "
"none."
msgstr ""
-#: actions/apiaccountupdatedeliverydevice.php:132
+#: actions/apiaccountupdatedeliverydevice.php:133
msgid "Could not update user."
msgstr "Wužiwar njeje so dał aktualizować."
#: actions/apidirectmessagenew.php:142
msgid "Can't send direct messages to users who aren't your friend."
msgstr ""
+"Njeje móžno, direktne powěsće wužiwarjam pósłać, kotřiž twoji přećeljo "
+"njejsu."
#: actions/apifavoritecreate.php:109 actions/apifavoritedestroy.php:110
#: actions/apistatusesdestroy.php:114
#: actions/apifavoritecreate.php:131 actions/favor.php:84 lib/command.php:285
msgid "Could not create favorite."
-msgstr ""
+msgstr "Faworit njeda so wutworić."
#: actions/apifavoritedestroy.php:123
msgid "That status is not a favorite."
#: actions/apifavoritedestroy.php:135 actions/disfavor.php:87
msgid "Could not delete favorite."
-msgstr ""
+msgstr "Faworit njeda so zhašeć."
#: actions/apifriendshipscreate.php:109
msgid "Could not follow user: User not found."
#: actions/apifriendshipsexists.php:94
msgid "Two user ids or screen_names must be supplied."
-msgstr ""
+msgstr "Dwaj wužiwarskej ID abo wužiwarskej mjenje dyrbitej so podać."
#: actions/apifriendshipsshow.php:134
msgid "Could not determine source user."
#: actions/oauthconnectionssettings.php:147 actions/recoverpassword.php:44
#: actions/smssettings.php:277 lib/designsettings.php:304
msgid "Unexpected form submission."
-msgstr ""
+msgstr "Njewočakowane wotpósłanje formulara."
#: actions/apioauthauthorize.php:259
msgid "An application would like to connect to your account"
msgid "This method requires a POST or DELETE."
msgstr "Tuta metoda wužaduje sej POST abo DELETE."
-#: actions/apistatusesdestroy.php:131
+#: actions/apistatusesdestroy.php:126
msgid "You may not delete another user's status."
msgstr "Njemóžeš status druheho wužiwarja zničić."
#: actions/emailsettings.php:504 actions/emailsettings.php:528
#: actions/smssettings.php:578 actions/smssettings.php:602
msgid "Couldn't update user record."
-msgstr ""
+msgstr "Datowa sadźba wužiwarja njeda so aktualizować."
#. TRANS: Message given after successfully removing an incoming e-mail address.
#: actions/emailsettings.php:508 actions/smssettings.php:581
#: actions/featured.php:99
#, php-format
msgid "A selection of some great users on %s"
-msgstr ""
+msgstr "Wuběr wulkotnych wužiwarjow na %s"
#: actions/file.php:34
msgid "No notice ID."
#: actions/grantrole.php:66 actions/revokerole.php:66
msgid "This role is reserved and cannot be set."
-msgstr ""
+msgstr "Tuta róla je wuměnjena a njeda so stajić."
#: actions/grantrole.php:75
msgid "You cannot grant user roles on this site."
#: actions/groupblock.php:206
msgid "Database error blocking user from group."
-msgstr ""
+msgstr "Zmylk datoweje banki blokuje wužiwarja za skupinu."
#: actions/groupbyid.php:74 actions/userbyid.php:70
msgid "No ID."
#: actions/grouplogo.php:401
msgid "Failed updating logo."
-msgstr ""
+msgstr "Aktualizowanje loga je so njeporadźiło."
#: actions/groupmembers.php:100 lib/groupnav.php:92
#, php-format
"If you can't find the group you're looking for, you can [create it](%%action."
"newgroup%%) yourself."
msgstr ""
+"Jeli njemóžeš skupinu namakać, kotruž pytaš, móžeš [ju wutworić] (%%action."
+"newgroup%%)."
#: actions/groupsearch.php:85
#, php-format
#: actions/joingroup.php:60
msgid "You must be logged in to join a group."
-msgstr ""
+msgstr "Dyrbiš přizjewjeny być, zo by do skupiny zastupił."
#: actions/joingroup.php:88 actions/leavegroup.php:88
msgid "No nickname or ID."
"For security reasons, please re-enter your user name and password before "
"changing your settings."
msgstr ""
+"Prošu zapodaj z přičinow wěstoty swoje wužiwarske mjeno znowa, prjedy hač "
+"změniš swoje nastajenja."
#: actions/login.php:292
msgid "Login with your username and password."
msgid ""
"Don't have a username yet? [Register](%%action.register%%) a new account."
msgstr ""
+"Hišće nimaš wužiwarske mjeno? [Zregistruj (%%action.register%%) nowe konto."
#: actions/makeadmin.php:92
msgid "Only an admin can make another user an admin."
#: actions/profilesettings.php:134 actions/register.php:480
msgid "Where you are, like \"City, State (or Region), Country\""
-msgstr ""
+msgstr "Hdźež sy, na př. \"město, zwjazkowy kraj (abo region) , kraj\""
#: actions/profilesettings.php:138
msgid "Share my current location when posting notices"
#: actions/public.php:83
#, php-format
msgid "Beyond the page limit (%s)."
-msgstr ""
+msgstr "Limit stronow (%s) překročeny."
#: actions/public.php:92
msgid "Could not retrieve public stream."
#: actions/showmessage.php:108
#, php-format
msgid "Message to %1$s on %2$s"
-msgstr ""
+msgstr "Powěsć do %1$s na %2$s"
#: actions/showmessage.php:113
#, php-format
msgid "Message from %1$s on %2$s"
-msgstr ""
+msgstr "Powěsć wot %1$s na %2$s"
#: actions/shownotice.php:90
msgid "Notice deleted."
#: actions/siteadminpanel.php:225
msgid "The name of your site, like \"Yourcompany Microblog\""
-msgstr ""
+msgstr "Mjeno twojeho sydła, kaž \"TwojePředewzaće Microblog\""
#: actions/siteadminpanel.php:229
msgid "Brought by"
#: actions/siteadminpanel.php:257
msgid "Default timezone for the site; usually UTC."
-msgstr ""
+msgstr "Standardne časowe pasmo za sydło; zwjetša UTC."
#: actions/siteadminpanel.php:262
msgid "Default language"
#. TRANS: Message given canceling SMS phone number confirmation for the wrong phone number.
#: actions/smssettings.php:413
msgid "That is the wrong confirmation number."
-msgstr ""
+msgstr "To je wopačne wobkrućenske čisło."
#. TRANS: Message given after successfully canceling SMS phone number confirmation.
#: actions/smssettings.php:427
#: actions/subedit.php:83 classes/Subscription.php:132
msgid "Could not save subscription."
-msgstr ""
+msgstr "Abonement njeda so składować."
#: actions/subscribe.php:77
msgid "This action only accepts POST requests."
#: actions/useradminpanel.php:165
#, php-format
msgid "Invalid default subscripton: '%1$s' is not user."
-msgstr ""
+msgstr "Njepłaćiwy standardny abonement: '%1$s' wužiwar njeje."
#. TRANS: Link description in user account settings menu.
#: actions/useradminpanel.php:218 lib/accountsettingsaction.php:111
#: actions/userauthorization.php:232
msgid "No authorization request!"
-msgstr ""
+msgstr "Žane awtorizaciske naprašowanje!"
#: actions/userauthorization.php:254
msgid "Subscription authorized"
#: actions/userauthorization.php:345
#, php-format
msgid "Avatar URL ‘%s’ is not valid."
-msgstr ""
+msgstr "URL awatara '%s' njeje płaćiwy"
#: actions/userauthorization.php:350
#, php-format
#: classes/Message.php:61
msgid "Could not insert message."
-msgstr ""
+msgstr "Powěsć njeda so zasunyć."
#: classes/Message.php:71
msgid "Could not update message with new URI."
#: classes/User.php:363
#, php-format
msgid "Welcome to %1$s, @%2$s!"
-msgstr ""
+msgstr "Witaj do %1$s, @%2$s!"
#: classes/User_group.php:480
msgid "Could not create group."
-msgstr ""
+msgstr "Skupina njeda so wutowrić."
#: classes/User_group.php:489
msgid "Could not set group URI."
#: classes/User_group.php:510
msgid "Could not set group membership."
-msgstr ""
+msgstr "Skupinske čłonstwo njeda so stajić."
#: classes/User_group.php:524
msgid "Could not save local group info."
#. TRANS: Link title attribute in user account settings menu.
#: lib/accountsettingsaction.php:109
msgid "Change your profile settings"
-msgstr ""
+msgstr "Twoje profilowe nastajenja změnić"
#. TRANS: Link title attribute in user account settings menu.
#: lib/accountsettingsaction.php:116
msgid "Upload an avatar"
-msgstr ""
+msgstr "Awatar nahrać"
#. TRANS: Link title attribute in user account settings menu.
#: lib/accountsettingsaction.php:123
msgid "Change your password"
-msgstr ""
+msgstr "Twoje hesło změnić"
#. TRANS: Link title attribute in user account settings menu.
#: lib/accountsettingsaction.php:130
#. TRANS: Client error message thrown when a user tries to change admin settings but has no access rights.
#: lib/adminpanelaction.php:98
msgid "You cannot make changes to this site."
-msgstr ""
+msgstr "Njemóžeš tute sydło změnić."
#. TRANS: Client error message throw when a certain panel's settings cannot be changed.
#: lib/adminpanelaction.php:110
#. TRANS: Client error message.
#: lib/adminpanelaction.php:229
msgid "showForm() not implemented."
-msgstr ""
+msgstr "showForm() njeimplementowany."
#. TRANS: Client error message
#: lib/adminpanelaction.php:259
msgid "saveSettings() not implemented."
-msgstr ""
+msgstr "saveSettings() njeimplementowany."
#. TRANS: Client error message thrown if design settings could not be deleted in
#. TRANS: the admin panel Design.
#. TRANS: Menu item title/tooltip
#: lib/adminpanelaction.php:349
msgid "Basic site configuration"
-msgstr ""
+msgstr "Zakładna sydłowa konfiguracija"
#. TRANS: Menu item for site administration
#: lib/adminpanelaction.php:351
#. TRANS: Form input field instructions.
#: lib/applicationeditform.php:233
msgid "Organization responsible for this application"
-msgstr ""
+msgstr "Organizacija, kotraž je za tutu aplikaciju zamołwita"
#. TRANS: Form input field instructions.
#: lib/applicationeditform.php:242
msgid "URL for the homepage of the organization"
-msgstr ""
+msgstr "URL za startowu stronu organizacije"
#. TRANS: Form input field instructions.
#: lib/applicationeditform.php:251
#. TRANS: Radio button label for access type.
#: lib/applicationeditform.php:320
msgid "Read-only"
-msgstr ""
+msgstr "Jenož čitajomny"
#. TRANS: Radio button label for access type.
#: lib/applicationeditform.php:339
msgid "Read-write"
-msgstr ""
+msgstr "Popisujomny"
#. TRANS: Form guide.
#: lib/applicationeditform.php:341
#. TRANS: Application access type
#: lib/applicationlist.php:136
msgid "read-write"
-msgstr ""
+msgstr "popisujomny"
#. TRANS: Application access type
#: lib/applicationlist.php:138
msgid "read-only"
-msgstr ""
+msgstr "jenož čitajomny"
#. TRANS: Used in application list. %1$s is a modified date, %2$s is access type (read-write or read-only)
#: lib/applicationlist.php:144
#: lib/command.php:127
#, php-format
msgid "Could not find a user with nickname %s"
-msgstr ""
+msgstr "Wužiwar z přimjenom %s njeda so namakać"
#. TRANS: Message given getting a non-existing user.
#. TRANS: %s is the nickname of the user that could not be found.
#: lib/command.php:147
#, php-format
msgid "Could not find a local user with nickname %s"
-msgstr ""
+msgstr "Lokalny wužiwar z přimjenom %s njeda so namakać"
#: lib/command.php:180
msgid "Sorry, this command is not yet implemented."
-msgstr ""
+msgstr "Tutón přikaz hišće njeje implementowany."
#: lib/command.php:225
msgid "It does not make a lot of sense to nudge yourself!"
#: lib/command.php:620
msgid "Specify the name of the user to subscribe to"
-msgstr ""
+msgstr "Podaj mjeno wužiwarja, kotrehož chceš abonować"
#: lib/command.php:628
msgid "Can't subscribe to OMB profiles by command."
#: lib/command.php:634
#, php-format
msgid "Subscribed to %s"
-msgstr ""
+msgstr "%s abonowany"
#: lib/command.php:655 lib/command.php:754
msgid "Specify the name of the user to unsubscribe from"
-msgstr ""
+msgstr "Podaj mjeno wužiwarja, kotrehož chceš wotskazać"
#: lib/command.php:664
#, php-format
msgid "Unsubscribed from %s"
-msgstr ""
+msgstr "%s wotskazany"
#: lib/command.php:682 lib/command.php:705
msgid "Command not yet implemented."
#: lib/command.php:723
msgid "Login command is disabled"
-msgstr ""
+msgstr "Přizjewjenski přikaz je znjemóžnjeny"
#: lib/command.php:734
#, php-format
#: lib/command.php:778
msgid "You are not subscribed to anyone."
-msgstr ""
+msgstr "Njejsy nikoho abonował."
#: lib/command.php:780
msgid "You are subscribed to this person:"
#: lib/command.php:800
msgid "No one is subscribed to you."
-msgstr ""
+msgstr "Nichtó njeje će abonował."
#: lib/command.php:802
msgid "This person is subscribed to you:"
#: lib/common.php:136
msgid "I looked for configuration files in the following places: "
-msgstr ""
+msgstr "Sym na slědowacych městnach za konfiguraciskimi datajemi pytał: "
#: lib/common.php:138
msgid "You may wish to run the installer to fix this."
-msgstr ""
+msgstr "Móže być, zo chceš instalaciski program startować, zo by to porjedźił."
#: lib/common.php:139
msgid "Go to the installer."
-msgstr ""
+msgstr "K instalaciji"
#: lib/connectsettingsaction.php:110
msgid "IM"
#: lib/connectsettingsaction.php:121
msgid "Authorized connected applications"
-msgstr ""
+msgstr "Awtorizowane zwjazane aplikacije"
#: lib/dberroraction.php:60
msgid "Database error"
#: lib/galleryaction.php:143
msgid "Go"
-msgstr ""
+msgstr "Start"
#: lib/grantroleform.php:91
#, php-format
msgid ""
"Location for the group, if any, like \"City, State (or Region), Country\""
msgstr ""
+"Městno za skupinu, jeli eksistuje, na př. \"město, zwjazkowy kraj (abo "
+"region), kraj\""
#: lib/groupeditform.php:187
#, php-format
#: lib/groupnav.php:108
#, php-format
msgid "Edit %s group properties"
-msgstr ""
+msgstr "Kajkosće skupiny %s wobdźěłać"
#: lib/groupnav.php:113
msgid "Logo"
#: lib/htmloutputter.php:104
msgid "This page is not available in a media type you accept"
msgstr ""
+"Tuta strona we wot tebje akceptowanym medijowym typje k dispoziciji "
+"njesteji."
#: lib/imagefile.php:72
msgid "Unsupported image file format."
#: lib/joinform.php:114
msgid "Join"
-msgstr ""
+msgstr "Zastupić"
#: lib/leaveform.php:114
msgid "Leave"
"\n"
"\t%s"
msgstr ""
+"Dospołnu rozmołwu móžes tu čitać:\n"
+"\n"
+"%s"
#: lib/mail.php:657
#, php-format
#: lib/mailbox.php:89
msgid "Only the user can read their own mailboxes."
-msgstr ""
+msgstr "Jenož wužiwar móže swoje póstowe kašćiki čitać."
#: lib/mailbox.php:139
msgid ""
#: lib/mailhandler.php:37
msgid "Could not parse message."
-msgstr ""
+msgstr "Powěsć njeda so analyzować."
#: lib/mailhandler.php:42
msgid "Not a registered user."
#: lib/mediafile.php:98 lib/mediafile.php:123
msgid "There was a database error while saving your file. Please try again."
msgstr ""
+"Při składowanju twojeje dataje je zmylk w datowej bance wustupił. Prošu "
+"spytaj hišće raz."
#: lib/mediafile.php:142
msgid "The uploaded file exceeds the upload_max_filesize directive in php.ini."
#: lib/mediafile.php:162
msgid "Failed to write file to disk."
-msgstr ""
+msgstr "Dataju njeda so na tačel pisać."
#: lib/mediafile.php:165
msgid "File upload stopped by extension."
-msgstr ""
+msgstr "Datajowe nahraće přez rozšěrjenje zastajene."
#: lib/mediafile.php:179 lib/mediafile.php:216
msgid "File exceeds user's quota."
#: lib/mediafile.php:196 lib/mediafile.php:233
msgid "File could not be moved to destination directory."
-msgstr ""
+msgstr "Dataja njeda so do ciloweho zapisa přesunyć."
#: lib/mediafile.php:201 lib/mediafile.php:237
msgid "Could not determine file's MIME type."
-msgstr ""
+msgstr "MIME-typ dataje njeda so zwěsćić."
#: lib/mediafile.php:270
#, php-format
msgid " Try using another %s format."
-msgstr ""
+msgstr "Spytaj druhi format %s."
#: lib/mediafile.php:275
#, php-format
#: lib/oauthstore.php:306
msgid "Error updating remote profile"
-msgstr ""
+msgstr "Zmylk při aktualizowanju zdaleneho profila"
#: lib/oauthstore.php:311
msgid "Error inserting remote profile"
#: lib/oauthstore.php:490
msgid "Couldn't insert new subscription."
-msgstr ""
+msgstr "Nowy abonement njeda so zasunyć."
#: lib/personalgroupnav.php:99
msgid "Personal"
#: lib/router.php:709
msgid "No single user defined for single-user mode."
-msgstr ""
+msgstr "Žadyn jednotliwy wužiwar za modus jednotliweho wužiwarja definowany."
#: lib/sandboxform.php:67
msgid "Sandbox"
#: lib/subgroupnav.php:83
#, php-format
msgid "People %s subscribes to"
-msgstr ""
+msgstr "Ludźo, kotrychž %s abonuje"
#: lib/subgroupnav.php:91
#, php-format
#: lib/subgroupnav.php:99
#, php-format
msgid "Groups %s is a member of"
-msgstr ""
+msgstr "Skupiny, w kotrychž %s je čłon"
#: lib/subgroupnav.php:105
msgid "Invite"
#: lib/userprofile.php:237
msgid "User deletion in progress..."
-msgstr ""
+msgstr "Wužiwar so haša..."
#: lib/userprofile.php:263
msgid "Edit profile settings"
#: lib/userprofile.php:326
msgid "Moderate"
-msgstr ""
+msgstr "Moderěrować"
#: lib/userprofile.php:364
msgid "User role"
#: lib/userprofile.php:367
msgctxt "role"
msgid "Moderator"
-msgstr ""
+msgstr "Moderator"
#. TRANS: Used in notices to indicate when the notice was made compared to now.
#: lib/util.php:1100
msgstr ""
"Project-Id-Version: StatusNet\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-05-25 11:36+0000\n"
-"PO-Revision-Date: 2010-06-03 23:01:59+0000\n"
+"POT-Creation-Date: 2010-06-10 22:48+0000\n"
+"PO-Revision-Date: 2010-06-10 22:50:20+0000\n"
"Language-Team: Interlingua\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: MediaWiki 1.17alpha (r67302); Translate extension (2010-05-24)\n"
+"X-Generator: MediaWiki 1.17alpha (r67833); Translate extension (2010-06-10)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: ia\n"
"X-Message-Group: out-statusnet\n"
msgstr "Salveguardar"
#. TRANS: Server error when page not found (404)
-#: actions/all.php:65 actions/public.php:98 actions/replies.php:93
+#: actions/all.php:68 actions/public.php:98 actions/replies.php:93
#: actions/showfavorites.php:138 actions/tag.php:52
msgid "No such page."
msgstr "Pagina non existe."
-#: actions/all.php:76 actions/allrss.php:68
-#: actions/apiaccountupdatedeliverydevice.php:113
+#: actions/all.php:79 actions/allrss.php:68
+#: actions/apiaccountupdatedeliverydevice.php:114
#: actions/apiaccountupdateprofile.php:105
#: actions/apiaccountupdateprofilebackgroundimage.php:116
#: actions/apiaccountupdateprofileimage.php:105 actions/apiblockcreate.php:97
msgstr "Usator non existe."
#. TRANS: Page title. %1$s is user nickname, %2$d is page number
-#: actions/all.php:87
+#: actions/all.php:90
#, php-format
msgid "%1$s and friends, page %2$d"
msgstr "%1$s e amicos, pagina %2$d"
#. TRANS: Page title. %1$s is user nickname
#. TRANS: H1 text. %1$s is user nickname
#. TRANS: Message is used as link title. %s is a user nickname.
-#: actions/all.php:90 actions/all.php:182 actions/allrss.php:116
+#: actions/all.php:93 actions/all.php:185 actions/allrss.php:116
#: actions/apitimelinefriends.php:210 actions/apitimelinehome.php:116
#: lib/personalgroupnav.php:100
#, php-format
msgstr "%s e amicos"
#. TRANS: %1$s is user nickname
-#: actions/all.php:104
+#: actions/all.php:107
#, php-format
msgid "Feed for friends of %s (RSS 1.0)"
msgstr "Syndication pro le amicos de %s (RSS 1.0)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:113
+#: actions/all.php:116
#, php-format
msgid "Feed for friends of %s (RSS 2.0)"
msgstr "Syndication pro le amicos de %s (RSS 2.0)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:122
+#: actions/all.php:125
#, php-format
msgid "Feed for friends of %s (Atom)"
msgstr "Syndication pro le amicos de %s (Atom)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:135
+#: actions/all.php:138
#, php-format
msgid ""
"This is the timeline for %s and friends but no one has posted anything yet."
"Isto es le chronologia pro %s e su amicos, ma necuno ha ancora publicate "
"alique."
-#: actions/all.php:140
+#: actions/all.php:143
#, php-format
msgid ""
"Try subscribing to more people, [join a group](%%action.groups%%) or post "
"action.groups%%) o publica alique tu mesme."
#. TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@"
-#: actions/all.php:143
+#: actions/all.php:146
#, php-format
msgid ""
"You can try to [nudge %1$s](../%2$s) from his profile or [post something to "
"Tu pote tentar [dar un pulsata a %1$s](../%2$s) in su profilo o [publicar un "
"message a su attention](%%%%action.newnotice%%%%?status_textarea=%3$s)."
-#: actions/all.php:146 actions/replies.php:210 actions/showstream.php:211
+#: actions/all.php:149 actions/replies.php:210 actions/showstream.php:211
#, php-format
msgid ""
"Why not [register an account](%%%%action.register%%%%) and then nudge %s or "
"pulsata a %s o publicar un message a su attention."
#. TRANS: H1 text
-#: actions/all.php:179
+#: actions/all.php:182
msgid "You and friends"
msgstr "Tu e amicos"
msgid "Updates from %1$s and friends on %2$s!"
msgstr "Actualisationes de %1$s e su amicos in %2$s!"
-#: actions/apiaccountratelimitstatus.php:70
-#: actions/apiaccountupdatedeliverydevice.php:93
+#: actions/apiaccountratelimitstatus.php:72
+#: actions/apiaccountupdatedeliverydevice.php:94
#: actions/apiaccountupdateprofile.php:97
#: actions/apiaccountupdateprofilebackgroundimage.php:94
#: actions/apiaccountupdateprofilecolors.php:118
msgid "API method not found."
msgstr "Methodo API non trovate."
-#: actions/apiaccountupdatedeliverydevice.php:85
+#: actions/apiaccountupdatedeliverydevice.php:86
#: actions/apiaccountupdateprofile.php:89
#: actions/apiaccountupdateprofilebackgroundimage.php:86
#: actions/apiaccountupdateprofilecolors.php:110
msgid "This method requires a POST."
msgstr "Iste methodo require un POST."
-#: actions/apiaccountupdatedeliverydevice.php:105
+#: actions/apiaccountupdatedeliverydevice.php:106
msgid ""
"You must specify a parameter named 'device' with a value of one of: sms, im, "
"none."
"Tu debe specificar un parametro nominate 'device' con un del valores: sms, "
"im, none."
-#: actions/apiaccountupdatedeliverydevice.php:132
+#: actions/apiaccountupdatedeliverydevice.php:133
msgid "Could not update user."
msgstr "Non poteva actualisar le usator."
msgid "This method requires a POST or DELETE."
msgstr "Iste methodo require un commando POST o DELETE."
-#: actions/apistatusesdestroy.php:131
+#: actions/apistatusesdestroy.php:126
msgid "You may not delete another user's status."
msgstr "Tu non pote deler le stato de un altere usator."
"If you believe this account is being used abusively, you can block them from "
"your subscribers list and report as spam to site administrators at %s"
msgstr ""
+"Si tu crede que iste conto es usate abusivemente, tu pote blocar lo de tu "
+"lista de subscriptores e reportar lo como spam al administratores del sito a "
+"%s"
#. TRANS: Main body of new-subscriber notification e-mail
#: lib/mail.php:254
msgstr ""
"Project-Id-Version: StatusNet\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-05-25 11:36+0000\n"
-"PO-Revision-Date: 2010-06-03 23:02:37+0000\n"
+"POT-Creation-Date: 2010-06-10 22:48+0000\n"
+"PO-Revision-Date: 2010-06-10 22:51:15+0000\n"
"Language-Team: Portuguese\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: MediaWiki 1.17alpha (r67302); Translate extension (2010-05-24)\n"
+"X-Generator: MediaWiki 1.17alpha (r67833); Translate extension (2010-06-10)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: pt\n"
"X-Message-Group: out-statusnet\n"
msgstr "Gravar"
#. TRANS: Server error when page not found (404)
-#: actions/all.php:65 actions/public.php:98 actions/replies.php:93
+#: actions/all.php:68 actions/public.php:98 actions/replies.php:93
#: actions/showfavorites.php:138 actions/tag.php:52
msgid "No such page."
msgstr "Página não foi encontrada."
-#: actions/all.php:76 actions/allrss.php:68
-#: actions/apiaccountupdatedeliverydevice.php:113
+#: actions/all.php:79 actions/allrss.php:68
+#: actions/apiaccountupdatedeliverydevice.php:114
#: actions/apiaccountupdateprofile.php:105
#: actions/apiaccountupdateprofilebackgroundimage.php:116
#: actions/apiaccountupdateprofileimage.php:105 actions/apiblockcreate.php:97
msgstr "Utilizador não foi encontrado."
#. TRANS: Page title. %1$s is user nickname, %2$d is page number
-#: actions/all.php:87
+#: actions/all.php:90
#, php-format
msgid "%1$s and friends, page %2$d"
msgstr "%1$s e amigos, página %2$d"
#. TRANS: Page title. %1$s is user nickname
#. TRANS: H1 text. %1$s is user nickname
#. TRANS: Message is used as link title. %s is a user nickname.
-#: actions/all.php:90 actions/all.php:182 actions/allrss.php:116
+#: actions/all.php:93 actions/all.php:185 actions/allrss.php:116
#: actions/apitimelinefriends.php:210 actions/apitimelinehome.php:116
#: lib/personalgroupnav.php:100
#, php-format
msgstr "%s e amigos"
#. TRANS: %1$s is user nickname
-#: actions/all.php:104
+#: actions/all.php:107
#, php-format
msgid "Feed for friends of %s (RSS 1.0)"
msgstr "Fonte para os amigos de %s (RSS 1.0)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:113
+#: actions/all.php:116
#, php-format
msgid "Feed for friends of %s (RSS 2.0)"
msgstr "Fonte para os amigos de %s (RSS 2.0)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:122
+#: actions/all.php:125
#, php-format
msgid "Feed for friends of %s (Atom)"
msgstr "Fonte para os amigos de %s (Atom)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:135
+#: actions/all.php:138
#, php-format
msgid ""
"This is the timeline for %s and friends but no one has posted anything yet."
msgstr ""
"Estas são as notas de %s e dos amigos, mas ainda não publicaram nenhuma."
-#: actions/all.php:140
+#: actions/all.php:143
#, php-format
msgid ""
"Try subscribing to more people, [join a group](%%action.groups%%) or post "
"publicar qualquer coisa."
#. TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@"
-#: actions/all.php:143
+#: actions/all.php:146
#, php-format
msgid ""
"You can try to [nudge %1$s](../%2$s) from his profile or [post something to "
"Pode tentar [dar um toque em %1$s](../%2$s) a partir do perfil ou [publicar "
"qualquer coisa à sua atenção](%%%%action.newnotice%%%%?status_textarea=%3$s)."
-#: actions/all.php:146 actions/replies.php:210 actions/showstream.php:211
+#: actions/all.php:149 actions/replies.php:210 actions/showstream.php:211
#, php-format
msgid ""
"Why not [register an account](%%%%action.register%%%%) and then nudge %s or "
"publicar uma nota à sua atenção."
#. TRANS: H1 text
-#: actions/all.php:179
+#: actions/all.php:182
msgid "You and friends"
msgstr "Você e seus amigos"
msgid "Updates from %1$s and friends on %2$s!"
msgstr "Actualizações de %1$s e amigos no %2$s!"
-#: actions/apiaccountratelimitstatus.php:70
-#: actions/apiaccountupdatedeliverydevice.php:93
+#: actions/apiaccountratelimitstatus.php:72
+#: actions/apiaccountupdatedeliverydevice.php:94
#: actions/apiaccountupdateprofile.php:97
#: actions/apiaccountupdateprofilebackgroundimage.php:94
#: actions/apiaccountupdateprofilecolors.php:118
msgid "API method not found."
msgstr "Método da API não encontrado."
-#: actions/apiaccountupdatedeliverydevice.php:85
+#: actions/apiaccountupdatedeliverydevice.php:86
#: actions/apiaccountupdateprofile.php:89
#: actions/apiaccountupdateprofilebackgroundimage.php:86
#: actions/apiaccountupdateprofilecolors.php:110
msgid "This method requires a POST."
msgstr "Este método requer um POST."
-#: actions/apiaccountupdatedeliverydevice.php:105
+#: actions/apiaccountupdatedeliverydevice.php:106
msgid ""
"You must specify a parameter named 'device' with a value of one of: sms, im, "
"none."
msgstr ""
"Tem de especificar um parâmetro 'aparelho' com um dos valores: sms, im, none."
-#: actions/apiaccountupdatedeliverydevice.php:132
+#: actions/apiaccountupdatedeliverydevice.php:133
msgid "Could not update user."
msgstr "Não foi possível actualizar o utilizador."
msgid "This method requires a POST or DELETE."
msgstr "Este método requer um POST ou DELETE."
-#: actions/apistatusesdestroy.php:131
+#: actions/apistatusesdestroy.php:126
msgid "You may not delete another user's status."
msgstr "Não pode apagar o estado de outro utilizador."
#: actions/profilesettings.php:151
msgid "Language"
-msgstr "Idioma"
+msgstr "Língua"
#: actions/profilesettings.php:152
msgid "Preferred language"
-msgstr "Idioma preferido"
+msgstr "Língua preferida"
#: actions/profilesettings.php:161
msgid "Timezone"
#: actions/profilesettings.php:241
msgid "Language is too long (max 50 chars)."
-msgstr "Idioma é demasiado extenso (máx. 50 caracteres)."
+msgstr "Língua é demasiado extensa (máx. 50 caracteres)."
#: actions/profilesettings.php:253 actions/tagother.php:178
#, php-format
#: actions/siteadminpanel.php:262
msgid "Default language"
-msgstr "Idioma do site, por omissão"
+msgstr "Língua, por omissão"
#: actions/siteadminpanel.php:263
msgid "Site language when autodetection from browser settings is not available"
msgstr ""
-"Idioma do site quando a sua detecção na configuração do browser não é "
+"Língua do site quando a sua detecção na configuração do browser não é "
"possível"
#: actions/siteadminpanel.php:271
"If you believe this account is being used abusively, you can block them from "
"your subscribers list and report as spam to site administrators at %s"
msgstr ""
+"Se acredita que esta conta está sendo usada abusivamente pode bloqueá-la da "
+"sua lista de subscritores e reportá-la como spam aos administradores do site "
+"em %s"
#. TRANS: Main body of new-subscriber notification e-mail
#: lib/mail.php:254
msgstr ""
"Project-Id-Version: StatusNet\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-05-25 11:36+0000\n"
-"PO-Revision-Date: 2010-06-03 23:02:43+0000\n"
+"POT-Creation-Date: 2010-06-10 22:48+0000\n"
+"PO-Revision-Date: 2010-06-10 22:51:24+0000\n"
"Language-Team: Russian\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: MediaWiki 1.17alpha (r67302); Translate extension (2010-05-24)\n"
+"X-Generator: MediaWiki 1.17alpha (r67833); Translate extension (2010-06-10)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: ru\n"
"X-Message-Group: out-statusnet\n"
msgstr "Сохранить"
#. TRANS: Server error when page not found (404)
-#: actions/all.php:65 actions/public.php:98 actions/replies.php:93
+#: actions/all.php:68 actions/public.php:98 actions/replies.php:93
#: actions/showfavorites.php:138 actions/tag.php:52
msgid "No such page."
msgstr "Нет такой страницы."
-#: actions/all.php:76 actions/allrss.php:68
-#: actions/apiaccountupdatedeliverydevice.php:113
+#: actions/all.php:79 actions/allrss.php:68
+#: actions/apiaccountupdatedeliverydevice.php:114
#: actions/apiaccountupdateprofile.php:105
#: actions/apiaccountupdateprofilebackgroundimage.php:116
#: actions/apiaccountupdateprofileimage.php:105 actions/apiblockcreate.php:97
msgstr "Нет такого пользователя."
#. TRANS: Page title. %1$s is user nickname, %2$d is page number
-#: actions/all.php:87
+#: actions/all.php:90
#, php-format
msgid "%1$s and friends, page %2$d"
msgstr "%1$s и друзья, страница %2$d"
#. TRANS: Page title. %1$s is user nickname
#. TRANS: H1 text. %1$s is user nickname
#. TRANS: Message is used as link title. %s is a user nickname.
-#: actions/all.php:90 actions/all.php:182 actions/allrss.php:116
+#: actions/all.php:93 actions/all.php:185 actions/allrss.php:116
#: actions/apitimelinefriends.php:210 actions/apitimelinehome.php:116
#: lib/personalgroupnav.php:100
#, php-format
msgstr "%s и друзья"
#. TRANS: %1$s is user nickname
-#: actions/all.php:104
+#: actions/all.php:107
#, php-format
msgid "Feed for friends of %s (RSS 1.0)"
msgstr "Лента друзей %s (RSS 1.0)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:113
+#: actions/all.php:116
#, php-format
msgid "Feed for friends of %s (RSS 2.0)"
msgstr "Лента друзей %s (RSS 2.0)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:122
+#: actions/all.php:125
#, php-format
msgid "Feed for friends of %s (Atom)"
msgstr "Лента друзей %s (Atom)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:135
+#: actions/all.php:138
#, php-format
msgid ""
"This is the timeline for %s and friends but no one has posted anything yet."
msgstr "Это лента %s и друзей, однако пока никто ничего не отправил."
-#: actions/all.php:140
+#: actions/all.php:143
#, php-format
msgid ""
"Try subscribing to more people, [join a group](%%action.groups%%) or post "
"action.groups%%) или отправьте что-нибудь сами."
#. TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@"
-#: actions/all.php:143
+#: actions/all.php:146
#, php-format
msgid ""
"You can try to [nudge %1$s](../%2$s) from his profile or [post something to "
"что-нибудь для привлечения его или её внимания](%%%%action.newnotice%%%%?"
"status_textarea=%3$s)."
-#: actions/all.php:146 actions/replies.php:210 actions/showstream.php:211
+#: actions/all.php:149 actions/replies.php:210 actions/showstream.php:211
#, php-format
msgid ""
"Why not [register an account](%%%%action.register%%%%) and then nudge %s or "
"s или отправить запись для привлечения его или её внимания?"
#. TRANS: H1 text
-#: actions/all.php:179
+#: actions/all.php:182
msgid "You and friends"
msgstr "Вы и друзья"
msgid "Updates from %1$s and friends on %2$s!"
msgstr "Обновлено от %1$s и его друзей на %2$s!"
-#: actions/apiaccountratelimitstatus.php:70
-#: actions/apiaccountupdatedeliverydevice.php:93
+#: actions/apiaccountratelimitstatus.php:72
+#: actions/apiaccountupdatedeliverydevice.php:94
#: actions/apiaccountupdateprofile.php:97
#: actions/apiaccountupdateprofilebackgroundimage.php:94
#: actions/apiaccountupdateprofilecolors.php:118
msgid "API method not found."
msgstr "Метод API не найден."
-#: actions/apiaccountupdatedeliverydevice.php:85
+#: actions/apiaccountupdatedeliverydevice.php:86
#: actions/apiaccountupdateprofile.php:89
#: actions/apiaccountupdateprofilebackgroundimage.php:86
#: actions/apiaccountupdateprofilecolors.php:110
msgid "This method requires a POST."
msgstr "Этот метод требует POST."
-#: actions/apiaccountupdatedeliverydevice.php:105
+#: actions/apiaccountupdatedeliverydevice.php:106
msgid ""
"You must specify a parameter named 'device' with a value of one of: sms, im, "
"none."
"Вы должны указать параметр с именем «device» и одним из значений: sms, im, "
"none."
-#: actions/apiaccountupdatedeliverydevice.php:132
+#: actions/apiaccountupdatedeliverydevice.php:133
msgid "Could not update user."
msgstr "Не удаётся обновить пользователя."
msgid "This method requires a POST or DELETE."
msgstr "Этот метод требует POST или DELETE."
-#: actions/apistatusesdestroy.php:131
+#: actions/apistatusesdestroy.php:126
msgid "You may not delete another user's status."
msgstr "Вы не можете удалять статус других пользователей."
"If you believe this account is being used abusively, you can block them from "
"your subscribers list and report as spam to site administrators at %s"
msgstr ""
+"Если вы считаете, эта учётная запись используется со злоупотреблениями, вы "
+"можете заблокировать её включение в свой список подписчиков и сообщить о "
+"спаме администраторам сайта по %s"
#. TRANS: Main body of new-subscriber notification e-mail
#: lib/mail.php:254
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-06-03 23:00+0000\n"
+"POT-Creation-Date: 2010-06-10 22:48+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
msgstr ""
#. TRANS: Server error when page not found (404)
-#: actions/all.php:65 actions/public.php:98 actions/replies.php:93
+#: actions/all.php:68 actions/public.php:98 actions/replies.php:93
#: actions/showfavorites.php:138 actions/tag.php:52
msgid "No such page."
msgstr ""
-#: actions/all.php:76 actions/allrss.php:68
-#: actions/apiaccountupdatedeliverydevice.php:113
+#: actions/all.php:79 actions/allrss.php:68
+#: actions/apiaccountupdatedeliverydevice.php:114
#: actions/apiaccountupdateprofile.php:105
#: actions/apiaccountupdateprofilebackgroundimage.php:116
#: actions/apiaccountupdateprofileimage.php:105 actions/apiblockcreate.php:97
msgstr ""
#. TRANS: Page title. %1$s is user nickname, %2$d is page number
-#: actions/all.php:87
+#: actions/all.php:90
#, php-format
msgid "%1$s and friends, page %2$d"
msgstr ""
#. TRANS: Page title. %1$s is user nickname
#. TRANS: H1 text. %1$s is user nickname
#. TRANS: Message is used as link title. %s is a user nickname.
-#: actions/all.php:90 actions/all.php:182 actions/allrss.php:116
+#: actions/all.php:93 actions/all.php:185 actions/allrss.php:116
#: actions/apitimelinefriends.php:210 actions/apitimelinehome.php:116
#: lib/personalgroupnav.php:100
#, php-format
msgstr ""
#. TRANS: %1$s is user nickname
-#: actions/all.php:104
+#: actions/all.php:107
#, php-format
msgid "Feed for friends of %s (RSS 1.0)"
msgstr ""
#. TRANS: %1$s is user nickname
-#: actions/all.php:113
+#: actions/all.php:116
#, php-format
msgid "Feed for friends of %s (RSS 2.0)"
msgstr ""
#. TRANS: %1$s is user nickname
-#: actions/all.php:122
+#: actions/all.php:125
#, php-format
msgid "Feed for friends of %s (Atom)"
msgstr ""
#. TRANS: %1$s is user nickname
-#: actions/all.php:135
+#: actions/all.php:138
#, php-format
msgid ""
"This is the timeline for %s and friends but no one has posted anything yet."
msgstr ""
-#: actions/all.php:140
+#: actions/all.php:143
#, php-format
msgid ""
"Try subscribing to more people, [join a group](%%action.groups%%) or post "
msgstr ""
#. TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@"
-#: actions/all.php:143
+#: actions/all.php:146
#, php-format
msgid ""
"You can try to [nudge %1$s](../%2$s) from his profile or [post something to "
"his or her attention](%%%%action.newnotice%%%%?status_textarea=%3$s)."
msgstr ""
-#: actions/all.php:146 actions/replies.php:210 actions/showstream.php:211
+#: actions/all.php:149 actions/replies.php:210 actions/showstream.php:211
#, php-format
msgid ""
"Why not [register an account](%%%%action.register%%%%) and then nudge %s or "
msgstr ""
#. TRANS: H1 text
-#: actions/all.php:179
+#: actions/all.php:182
msgid "You and friends"
msgstr ""
msgid "Updates from %1$s and friends on %2$s!"
msgstr ""
-#: actions/apiaccountratelimitstatus.php:70
-#: actions/apiaccountupdatedeliverydevice.php:93
+#: actions/apiaccountratelimitstatus.php:72
+#: actions/apiaccountupdatedeliverydevice.php:94
#: actions/apiaccountupdateprofile.php:97
#: actions/apiaccountupdateprofilebackgroundimage.php:94
#: actions/apiaccountupdateprofilecolors.php:118
msgid "API method not found."
msgstr ""
-#: actions/apiaccountupdatedeliverydevice.php:85
+#: actions/apiaccountupdatedeliverydevice.php:86
#: actions/apiaccountupdateprofile.php:89
#: actions/apiaccountupdateprofilebackgroundimage.php:86
#: actions/apiaccountupdateprofilecolors.php:110
msgid "This method requires a POST."
msgstr ""
-#: actions/apiaccountupdatedeliverydevice.php:105
+#: actions/apiaccountupdatedeliverydevice.php:106
msgid ""
"You must specify a parameter named 'device' with a value of one of: sms, im, "
"none."
msgstr ""
-#: actions/apiaccountupdatedeliverydevice.php:132
+#: actions/apiaccountupdatedeliverydevice.php:133
msgid "Could not update user."
msgstr ""
msgid "This method requires a POST or DELETE."
msgstr ""
-#: actions/apistatusesdestroy.php:131
+#: actions/apistatusesdestroy.php:126
msgid "You may not delete another user's status."
msgstr ""
msgstr ""
"Project-Id-Version: StatusNet\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-05-27 22:55+0000\n"
-"PO-Revision-Date: 2010-06-03 23:02:57+0000\n"
+"POT-Creation-Date: 2010-06-10 22:48+0000\n"
+"PO-Revision-Date: 2010-06-10 22:51:44+0000\n"
"Language-Team: Ukrainian\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: MediaWiki 1.17alpha (r67302); Translate extension (2010-05-24)\n"
+"X-Generator: MediaWiki 1.17alpha (r67833); Translate extension (2010-06-10)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: uk\n"
"X-Message-Group: out-statusnet\n"
msgstr "Зберегти"
#. TRANS: Server error when page not found (404)
-#: actions/all.php:65 actions/public.php:98 actions/replies.php:93
+#: actions/all.php:68 actions/public.php:98 actions/replies.php:93
#: actions/showfavorites.php:138 actions/tag.php:52
msgid "No such page."
msgstr "Немає такої сторінки."
-#: actions/all.php:76 actions/allrss.php:68
-#: actions/apiaccountupdatedeliverydevice.php:113
+#: actions/all.php:79 actions/allrss.php:68
+#: actions/apiaccountupdatedeliverydevice.php:114
#: actions/apiaccountupdateprofile.php:105
#: actions/apiaccountupdateprofilebackgroundimage.php:116
#: actions/apiaccountupdateprofileimage.php:105 actions/apiblockcreate.php:97
msgstr "Такого користувача немає."
#. TRANS: Page title. %1$s is user nickname, %2$d is page number
-#: actions/all.php:87
+#: actions/all.php:90
#, php-format
msgid "%1$s and friends, page %2$d"
msgstr "%1$s та друзі, сторінка %2$d"
#. TRANS: Page title. %1$s is user nickname
#. TRANS: H1 text. %1$s is user nickname
#. TRANS: Message is used as link title. %s is a user nickname.
-#: actions/all.php:90 actions/all.php:182 actions/allrss.php:116
+#: actions/all.php:93 actions/all.php:185 actions/allrss.php:116
#: actions/apitimelinefriends.php:210 actions/apitimelinehome.php:116
#: lib/personalgroupnav.php:100
#, php-format
msgstr "%s з друзями"
#. TRANS: %1$s is user nickname
-#: actions/all.php:104
+#: actions/all.php:107
#, php-format
msgid "Feed for friends of %s (RSS 1.0)"
msgstr "Стрічка дописів для друзів %s (RSS 1.0)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:113
+#: actions/all.php:116
#, php-format
msgid "Feed for friends of %s (RSS 2.0)"
msgstr "Стрічка дописів для друзів %s (RSS 2.0)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:122
+#: actions/all.php:125
#, php-format
msgid "Feed for friends of %s (Atom)"
msgstr "Стрічка дописів для друзів %s (Atom)"
#. TRANS: %1$s is user nickname
-#: actions/all.php:135
+#: actions/all.php:138
#, php-format
msgid ""
"This is the timeline for %s and friends but no one has posted anything yet."
msgstr "Це стрічка дописів %s і друзів, але вона поки що порожня."
-#: actions/all.php:140
+#: actions/all.php:143
#, php-format
msgid ""
"Try subscribing to more people, [join a group](%%action.groups%%) or post "
"або напишіть щось самі."
#. TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@"
-#: actions/all.php:143
+#: actions/all.php:146
#, php-format
msgid ""
"You can try to [nudge %1$s](../%2$s) from his profile or [post something to "
"Ви можете [«розштовхати» %1$s](../%2$s) зі сторінки його профілю або [щось "
"йому написати](%%%%action.newnotice%%%%?status_textarea=%3$s)."
-#: actions/all.php:146 actions/replies.php:210 actions/showstream.php:211
+#: actions/all.php:149 actions/replies.php:210 actions/showstream.php:211
#, php-format
msgid ""
"Why not [register an account](%%%%action.register%%%%) and then nudge %s or "
"«розштовхати» %s або щось йому написати."
#. TRANS: H1 text
-#: actions/all.php:179
+#: actions/all.php:182
msgid "You and friends"
msgstr "Ви з друзями"
msgid "Updates from %1$s and friends on %2$s!"
msgstr "Оновлення від %1$s та друзів на %2$s!"
-#: actions/apiaccountratelimitstatus.php:70
-#: actions/apiaccountupdatedeliverydevice.php:93
+#: actions/apiaccountratelimitstatus.php:72
+#: actions/apiaccountupdatedeliverydevice.php:94
#: actions/apiaccountupdateprofile.php:97
#: actions/apiaccountupdateprofilebackgroundimage.php:94
#: actions/apiaccountupdateprofilecolors.php:118
msgid "API method not found."
msgstr "API метод не знайдено."
-#: actions/apiaccountupdatedeliverydevice.php:85
+#: actions/apiaccountupdatedeliverydevice.php:86
#: actions/apiaccountupdateprofile.php:89
#: actions/apiaccountupdateprofilebackgroundimage.php:86
#: actions/apiaccountupdateprofilecolors.php:110
msgid "This method requires a POST."
msgstr "Цей метод потребує POST."
-#: actions/apiaccountupdatedeliverydevice.php:105
+#: actions/apiaccountupdatedeliverydevice.php:106
msgid ""
"You must specify a parameter named 'device' with a value of one of: sms, im, "
"none."
msgstr ""
"Ви мусите встановити параметр «device» з одним зі значень: sms, im, none."
-#: actions/apiaccountupdatedeliverydevice.php:132
+#: actions/apiaccountupdatedeliverydevice.php:133
msgid "Could not update user."
msgstr "Не вдалося оновити користувача."
#: actions/newgroup.php:172
#, php-format
msgid "Alias \"%s\" already in use. Try another one."
-msgstr "Додаткове ім’я \"%s\" вже використовується. Спробуйте інше."
+msgstr "Додаткове ім’я «%s» вже використовується. Спробуйте інше."
#: actions/apigroupcreate.php:289 actions/editgroup.php:238
#: actions/newgroup.php:178
msgid "This method requires a POST or DELETE."
msgstr "Цей метод потребує або НАПИСАТИ, або ВИДАЛИТИ."
-#: actions/apistatusesdestroy.php:131
+#: actions/apistatusesdestroy.php:126
msgid "You may not delete another user's status."
msgstr "Ви не можете видалити статус іншого користувача."
#: actions/confirmaddress.php:161
#, php-format
msgid "The address \"%s\" has been confirmed for your account."
-msgstr "Адресу \"%s\" було підтверджено для Вашого акаунту."
+msgstr "Адресу «%s» було підтверджено для Вашого акаунту."
#: actions/conversation.php:99
msgid "Conversation"
#: actions/editgroup.php:228 actions/newgroup.php:168
#, php-format
msgid "Invalid alias: \"%s\""
-msgstr "Помилкове додаткове ім’я: \"%s\""
+msgstr "Помилкове додаткове ім’я: «%s»"
#: actions/editgroup.php:258
msgid "Could not update group."
#. TRANS: Instructions for e-mail address input form.
#: actions/emailsettings.php:135
msgid "Email address, like \"UserName@example.org\""
-msgstr "Електронна адреса, на зразок \"UserName@example.org\""
+msgstr "Електронна адреса, на зразок «UserName@example.org»"
#. TRANS: Button label for adding an e-mail address in e-mail settings form.
#. TRANS: Button label for adding an IM address in IM settings form.
#. TRANS: Checkbox label in e-mail preferences form.
#: actions/emailsettings.php:199
msgid "Send me email when someone sends me an \"@-reply\"."
-msgstr "Надсилати мені листа, коли на мій допис з’являється \"@-відповідь\"."
+msgstr "Надсилати мені листа, коли на мій допис з’являється «@-відповідь»."
#. TRANS: Checkbox label in e-mail preferences form.
#: actions/emailsettings.php:205
msgstr ""
"Групи на сайті %%%%site.name%%%% дозволять Вам відшукати людей зі спільними "
"інтересами. Лише приєднайтеся до групи і надсилайте повідомлення до усіх її "
-"учасників використовуючи просту команду \"!groupname\" у тексті "
-"повÑ\96домленнÑ\8f. Ð\9dе баÑ\87иÑ\82е гÑ\80Ñ\83пÑ\83, Ñ\8fка Ð\92аÑ\81 Ñ\86Ñ\96кавиÑ\82Ñ\8c? СпÑ\80обÑ\83йÑ\82е Ñ\97Ñ\97 [знайÑ\82и](%%%%"
-"action.groupsearch%%%%) або [створіть власну!](%%%%action.newgroup%%%%)"
+"учасників використовуючи просту команду «!groupname» у тексті повідомлення. "
+"Ð\9dе баÑ\87иÑ\82е гÑ\80Ñ\83пÑ\83, Ñ\8fка Ð\92аÑ\81 Ñ\86Ñ\96кавиÑ\82Ñ\8c? СпÑ\80обÑ\83йÑ\82е Ñ\97Ñ\97 [знайÑ\82и](%%%%action."
+"groupsearch%%%%) або [створіть власну!](%%%%action.newgroup%%%%)"
#: actions/groups.php:107 actions/usergroups.php:126 lib/groupeditform.php:122
msgid "Create a new group"
"Jabber or GTalk address, like \"UserName@example.org\". First, make sure to "
"add %s to your buddy list in your IM client or on GTalk."
msgstr ""
-"Jabber або GTalk адреса, на зразок \"UserName@example.org\". Але спершу "
+"Jabber або GTalk адреса, на зразок «UserName@example.org». Але спершу "
"переконайтеся, що додали %s до списку контактів в своєму IM-клієнті або в "
"GTalk."
#: actions/noticesearchrss.php:96
#, php-format
msgid "Updates with \"%s\""
-msgstr "Оновлення з \"%s\""
+msgstr "Оновлення з «%s»"
#: actions/noticesearchrss.php:98
#, php-format
msgid "Updates matching search term \"%1$s\" on %2$s!"
-msgstr "Всі оновлення за збігом з \"%s\" на %2$s!"
+msgstr "Всі оновлення за збігом з «%s» на %2$s!"
#: actions/nudge.php:85
msgid ""
#: actions/profilesettings.php:253 actions/tagother.php:178
#, php-format
msgid "Invalid tag: \"%s\""
-msgstr "Недійсний теґ: \"%s\""
+msgstr "Недійсний теґ: «%s»"
#: actions/profilesettings.php:306
msgid "Couldn't update user for autosubscribe."
"friends, family, and colleagues! ([Read more](%%doc.help%%))"
msgstr ""
"Це %%site.name%% — сервіс [мікроблоґів](http://uk.wikipedia.org/wiki/"
-"Мікроблоггінг), який працює на вільному програмному забезпеченні [StatusNet]"
+"Мікроблогінг), який працює на вільному програмному забезпеченні [StatusNet]"
"(http://status.net/). [Приєднуйтесь](%%action.register%%) зараз і зможете "
"розділити своє життя з друзями, родиною і колегами! ([Дізнатися більше](%%"
"doc.help%%))"
"tool."
msgstr ""
"Це %%site.name%% — сервіс [мікроблоґів](http://uk.wikipedia.org/wiki/"
-"Мікроблоггінг), який працює на вільному програмному забезпеченні [StatusNet]"
+"Мікроблогінг), який працює на вільному програмному забезпеченні [StatusNet]"
"(http://status.net/)."
#: actions/publictagcloud.php:57
"of this group and many more! ([Read more](%%%%doc.help%%%%))"
msgstr ""
"**%s** це група на %%%%site.name%%%% — сервісі [мікроблоґів](http://uk."
-"wikipedia.org/wiki/Мікроблоггінг), який працює на вільному програмному "
+"wikipedia.org/wiki/Мікроблогінг), який працює на вільному програмному "
"забезпеченні [StatusNet](http://status.net/). Члени цієї групи роблять "
"короткі дописи про своє життя та інтереси. [Приєднуйтесь](%%%%action.register"
"%%%%) зараз і долучіться до спілкування! ([Дізнатися більше](%%%%doc.help%%%"
"[StatusNet](http://status.net/) tool. Its members share short messages about "
"their life and interests. "
msgstr ""
-"**%s** це група користувачів на %%site.name%% — сервісі [мікроблоґів](http://"
-"uk.wikipedia.org/wiki/Мікроблоггінг), який працює на вільному програмному "
-"забезпеÑ\87еннÑ\96 [StatusNet](http://status.net/). Члени Ñ\86Ñ\96Ñ\94Ñ\97 гÑ\80Ñ\83пи Ñ\80облÑ\8fÑ\82Ñ\8c "
-"короткі дописи про своє життя та інтереси. "
+"**%s** це група користувачів на %%%%site.name%%%% — сервісі [мікроблоґів]"
+"(http://uk.wikipedia.org/wiki/Мікроблогінг), який працює на вільному "
+"пÑ\80огÑ\80амномÑ\83 забезпеÑ\87еннÑ\96 [StatusNet](http://status.net/). Члени Ñ\86Ñ\96Ñ\94Ñ\97 гÑ\80Ñ\83пи "
+"роблять короткі дописи про своє життя та інтереси. "
#: actions/showgroup.php:497
msgid "Admins"
"follow **%s**'s notices and many more! ([Read more](%%%%doc.help%%%%))"
msgstr ""
"**%s** користується %%%%site.name%%%% — сервісом [мікроблоґів](http://uk."
-"wikipedia.org/wiki/Мікроблоґ), який працює на вільному програмному "
+"wikipedia.org/wiki/Мікроблогінг), який працює на вільному програмному "
"забезпеченні [StatusNet](http://status.net/). [Приєднуйтесь](%%%%action."
"register%%%%) зараз і слідкуйте за дописами **%s**, також на Вас чекає "
"багато іншого! ([Дізнатися більше](%%%%doc.help%%%%))"
"[StatusNet](http://status.net/) tool. "
msgstr ""
"**%s** є власником акаунту на сайті %%%%site.name%%%% — сервісі [мікроблоґів]"
-"(http://uk.wikipedia.org/wiki/Мікроблоггінг), який працює на вільному "
+"(http://uk.wikipedia.org/wiki/Мікроблогінг), який працює на вільному "
"програмному забезпеченні [StatusNet](http://status.net/). "
#: actions/showstream.php:305
#: actions/sitenoticeadminpanel.php:176
msgid "Site notice text"
-msgstr "Текст повідомлення сайту"
+msgstr "Текст повідомлення"
#: actions/sitenoticeadminpanel.php:178
msgid "Site-wide notice text (255 chars max; HTML okay)"
-msgstr "Текст повідомлення сайту (255 символів максимум; HTML дозволено)"
+msgstr ""
+"Текст повідомлення сайту (255 символів максимум; деякий HTML дозволено)"
#: actions/sitenoticeadminpanel.php:198
msgid "Save site notice"
#: actions/useradminpanel.php:165
#, php-format
msgid "Invalid default subscripton: '%1$s' is not user."
-msgstr "Помилкова підписка за замовчуванням: '%1$s' не є користувачем."
+msgstr "Помилкова підписка за замовчуванням: «%1$s» не є користувачем."
#. TRANS: Link description in user account settings menu.
#: actions/useradminpanel.php:218 lib/accountsettingsaction.php:111
#: actions/userauthorization.php:308
#, php-format
msgid "Listenee URI ‘%s’ is too long."
-msgstr "URI слухача ‘%s’ задовге."
+msgstr "URI слухача «%s» задовге."
#: actions/userauthorization.php:314
#, php-format
msgid "Listenee URI ‘%s’ is a local user."
-msgstr "URI слухача ‘%s’ це локальний користувач"
+msgstr "URI слухача «%s» це локальний користувач"
#: actions/userauthorization.php:329
#, php-format
msgid "Profile URL ‘%s’ is for a local user."
-msgstr "URL-адреса профілю ‘%s’ для локального користувача."
+msgstr "URL-адреса профілю «%s» для локального користувача."
#: actions/userauthorization.php:345
#, php-format
msgid "Avatar URL ‘%s’ is not valid."
-msgstr "URL-адреса автари ‘%s’ помилкова."
+msgstr "URL-адреса аватари «%s» помилкова."
#: actions/userauthorization.php:350
#, php-format
msgid "Can’t read avatar URL ‘%s’."
-msgstr "Не можна прочитати URL аватари ‘%s’."
+msgstr "Не можна прочитати URL аватари «%s»."
#: actions/userauthorization.php:355
#, php-format
msgid "Wrong image type for avatar URL ‘%s’."
-msgstr "Неправильний тип зображення для URL-адреси аватари ‘%s’."
+msgstr "Неправильний тип зображення для URL-адреси аватари «%s»."
#: actions/userdesignsettings.php:76 lib/designsettings.php:65
msgid "Profile design"
#: lib/grantroleform.php:91
#, php-format
msgid "Grant this user the \"%s\" role"
-msgstr "Надати цьому користувачеві роль \"%s\""
+msgstr "Надати цьому користувачеві роль «%s»"
#: lib/groupeditform.php:163
msgid "URL of the homepage or blog of the group or topic"
#: lib/profileaction.php:196
msgid "Member since"
-msgstr "Ð\97 нами вÑ\96д"
+msgstr "РеÑ\94Ñ\81Ñ\82Ñ\80аÑ\86Ñ\96Ñ\8f"
#. TRANS: Average count of posts made per day since account registration
#: lib/profileaction.php:235
#: lib/revokeroleform.php:91
#, php-format
msgid "Revoke the \"%s\" role from this user"
-msgstr "Відкликати роль \"%s\" для цього користувача"
+msgstr "Відкликати роль «%s» для цього користувача"
#: lib/router.php:709
msgid "No single user defined for single-user mode."
return true;
}
- $url = 'http://sw.geonames.org/' . $location->location_id . '/';
+ $url = 'http://sws.geonames.org/' . $location->location_id . '/';
// it's been filled, so don't process further.
return false;
--- /dev/null
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * Creates a dynamic sitemap for a StatusNet site
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Sample
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+/**
+ * Sitemap plugin
+ *
+ * @category Sample
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class SitemapPlugin extends Plugin
+{
+ const USERS_PER_MAP = 50000;
+ const NOTICES_PER_MAP = 50000;
+
+ /**
+ * Load related modules when needed
+ *
+ * @param string $cls Name of the class to be loaded
+ *
+ * @return boolean hook value; true means continue processing, false means stop.
+ */
+
+ function onAutoload($cls)
+ {
+ $dir = dirname(__FILE__);
+
+ switch ($cls)
+ {
+ case 'Sitemap_user_count':
+ case 'Sitemap_notice_count':
+ require_once $dir . '/' . $cls . '.php';
+ return false;
+ case 'SitemapindexAction':
+ case 'NoticesitemapAction':
+ case 'UsersitemapAction':
+ require_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
+ return false;
+ case 'SitemapAction':
+ require_once $dir . '/' . strtolower($cls) . '.php';
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Add sitemap-related information at the end of robots.txt
+ *
+ * @param Action $action Action being run
+ *
+ * @return boolean hook value.
+ */
+
+ function onEndRobotsTxt($action)
+ {
+ $url = common_local_url('sitemapindex');
+
+ print "\nSitemap: $url\n";
+
+ return true;
+ }
+
+ /**
+ * Map URLs to actions
+ *
+ * @param Net_URL_Mapper $m path-to-action mapper
+ *
+ * @return boolean hook value; true means continue processing, false means stop.
+ */
+
+ function onRouterInitialized($m)
+ {
+ $m->connect('sitemapindex.xml',
+ array('action' => 'sitemapindex'));
+
+ $m->connect('/notice-sitemap-:year-:month-:day-:index.xml',
+ array('action' => 'noticesitemap'),
+ array('year' => '[0-9]{4}',
+ 'month' => '[01][0-9]',
+ 'day' => '[0123][0-9]',
+ 'index' => '[1-9][0-9]*'));
+
+ $m->connect('/user-sitemap-:year-:month-:day-:index.xml',
+ array('action' => 'usersitemap'),
+ array('year' => '[0-9]{4}',
+ 'month' => '[01][0-9]',
+ 'day' => '[0123][0-9]',
+ 'index' => '[1-9][0-9]*'));
+ return true;
+ }
+
+ /**
+ * Database schema setup
+ *
+ * We cache some data persistently to avoid overlong queries.
+ *
+ * @see Sitemap_user_count
+ * @see Sitemap_notice_count
+ *
+ * @return boolean hook value; true means continue processing, false means stop.
+ */
+
+ function onCheckSchema()
+ {
+ $schema = Schema::get();
+
+ $schema->ensureTable('sitemap_user_count',
+ array(new ColumnDef('registration_date', 'date', null,
+ true, 'PRI'),
+ new ColumnDef('user_count', 'integer'),
+ new ColumnDef('created', 'datetime',
+ null, false),
+ new ColumnDef('modified', 'timestamp')));
+
+ $schema->ensureTable('sitemap_notice_count',
+ array(new ColumnDef('notice_date', 'date', null,
+ true, 'PRI'),
+ new ColumnDef('notice_count', 'integer'),
+ new ColumnDef('created', 'datetime',
+ null, false),
+ new ColumnDef('modified', 'timestamp')));
+
+ return true;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Data class for counting notice postings by date
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for counting notices by date
+ *
+ * We make a separate sitemap for each notice posted by date.
+ * To save ourselves some (not inconsiderable) processing effort,
+ * we cache this data in the sitemap_notice_count table. Each
+ * row represents a day since the site has been started, with a count
+ * of notices posted on that day. Since, after the end of the day,
+ * this number doesn't change, it's a good candidate for persistent caching.
+ *
+ * @category Data
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ *
+ * @see DB_DataObject
+ */
+
+class Sitemap_notice_count extends Memcached_DataObject
+{
+ public $__table = 'sitemap_notice_count'; // table name
+
+ public $notice_date; // date primary_key not_null
+ public $notice_count; // int(4)
+ public $created;
+ public $modified;
+
+ /**
+ * Get an instance by key
+ *
+ * This is a utility method to get a single instance with a given key value.
+ *
+ * @param string $k Key to use to lookup (usually 'notice_id' for this class)
+ * @param mixed $v Value to lookup
+ *
+ * @return Sitemap_notice_count object found, or null for no hits
+ *
+ */
+
+ function staticGet($k, $v=null)
+ {
+ return Memcached_DataObject::staticGet('Sitemap_notice_count', $k, $v);
+ }
+
+ /**
+ * return table definition for DB_DataObject
+ *
+ * DB_DataObject needs to know something about the table to manipulate
+ * instances. This method provides all the DB_DataObject needs to know.
+ *
+ * @return array array of column definitions
+ */
+
+ function table()
+ {
+ return array('notice_date' => DB_DATAOBJECT_DATE + DB_DATAOBJECT_NOTNULL,
+ 'notice_count' => DB_DATAOBJECT_INT,
+ 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL,
+ 'modified' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL);
+ }
+
+ /**
+ * return key definitions for DB_DataObject
+ *
+ * DB_DataObject needs to know about keys that the table has; this function
+ * defines them.
+ *
+ * @return array key definitions
+ */
+
+ function keys()
+ {
+ return array('notice_date' => 'K');
+ }
+
+ /**
+ * return key definitions for Memcached_DataObject
+ *
+ * Our caching system uses the same key definitions, but uses a different
+ * method to get them.
+ *
+ * @return array key definitions
+ */
+
+ function keyTypes()
+ {
+ return $this->keys();
+ }
+
+ static function getAll()
+ {
+ $noticeCounts = self::cacheGet('sitemap:notice:counts');
+
+ if ($noticeCounts === false) {
+
+ $snc = new Sitemap_notice_count();
+ $snc->orderBy('notice_date DESC');
+
+ // Fetch the first one to check up-to-date-itude
+
+ $n = $snc->find(true);
+
+ $today = self::today();
+ $noticeCounts = array();
+
+ if (!$n) { // No counts saved yet
+ $noticeCounts = self::initializeCounts();
+ } else if ($snc->notice_date < $today) { // There are counts but not up to today
+ $noticeCounts = self::fillInCounts($snc->notice_date);
+ } else if ($snc->notice_date == $today) { // Refresh today's
+ $noticeCounts[$today] = self::updateToday();
+ }
+
+ // starts with second-to-last date
+
+ while ($snc->fetch()) {
+ $noticeCounts[$snc->notice_date] = $snc->notice_count;
+ }
+
+ self::cacheSet('sitemap:notice:counts', $noticeCounts);
+ }
+
+ return $noticeCounts;
+ }
+
+ static function initializeCounts()
+ {
+ $firstDate = self::getFirstDate(); // awww
+ $today = self::today();
+
+ $counts = array();
+
+ for ($d = $firstDate; $d <= $today; $d = self::incrementDay($d)) {
+ $n = self::getCount($d);
+ self::insertCount($d, $n);
+ $counts[$d] = $n;
+ }
+
+ return $counts;
+ }
+
+ static function fillInCounts($lastDate)
+ {
+ $today = self::today();
+
+ $counts = array();
+
+ $n = self::getCount($lastDate);
+ self::updateCount($lastDate, $n);
+
+ $counts[$lastDate] = $n;
+
+ for ($d = self::incrementDay($lastDate); $d <= $today; $d = self::incrementDay($d)) {
+ $n = self::getCount($d);
+ self::insertCount($d, $n);
+ }
+
+ return $counts;
+ }
+
+ static function updateToday()
+ {
+ $today = self::today();
+
+ $n = self::getCount($today);
+ self::updateCount($today, $n);
+
+ return $n;
+ }
+
+ static function getCount($d)
+ {
+ $notice = new Notice();
+ $notice->whereAdd('created BETWEEN "'.$d.' 00:00:00" AND "'.self::incrementDay($d).' 00:00:00"');
+ $notice->whereAdd('is_local = ' . Notice::LOCAL_PUBLIC);
+ $n = $notice->count();
+
+ return $n;
+ }
+
+ static function insertCount($d, $n)
+ {
+ $snc = new Sitemap_notice_count();
+
+ $snc->notice_date = DB_DataObject_Cast::date($d);
+
+ $snc->notice_count = $n;
+ $snc->created = common_sql_now();
+ $snc->modified = $snc->created;
+
+ if (!$snc->insert()) {
+ common_log(LOG_WARNING, "Could not save user counts for '$d'");
+ }
+ }
+
+ static function updateCount($d, $n)
+ {
+ $snc = Sitemap_notice_count::staticGet('notice_date', DB_DataObject_Cast::date($d));
+
+ if (empty($snc)) {
+ throw new Exception("No such registration date: $d");
+ }
+
+ $orig = clone($snc);
+
+ $snc->notice_date = DB_DataObject_Cast::date($d);
+
+ $snc->notice_count = $n;
+ $snc->created = common_sql_now();
+ $snc->modified = $snc->created;
+
+ if (!$snc->update($orig)) {
+ common_log(LOG_WARNING, "Could not save user counts for '$d'");
+ }
+ }
+
+ static function incrementDay($d)
+ {
+ $dt = self::dateStrToInt($d);
+ return self::dateIntToStr($dt + 24 * 60 * 60);
+ }
+
+ static function dateStrToInt($d)
+ {
+ return strtotime($d.' 00:00:00');
+ }
+
+ static function dateIntToStr($dt)
+ {
+ return date('Y-m-d', $dt);
+ }
+
+ static function getFirstDate()
+ {
+ $n = new Notice();
+
+ $n->selectAdd();
+ $n->selectAdd('date(min(created)) as first_date');
+
+ if ($n->find(true)) {
+ return $n->first_date;
+ } else {
+ // Is this right?
+ return self::dateIntToStr(time());
+ }
+ }
+
+ static function today()
+ {
+ return self::dateIntToStr(time());
+ }
+}
--- /dev/null
+<?php
+/**
+ * Data class for counting user registrations by date
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for counting users by date
+ *
+ * We make a separate sitemap for each user registered by date.
+ * To save ourselves some processing effort, we cache this data
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ *
+ * @see DB_DataObject
+ */
+
+class Sitemap_user_count extends Memcached_DataObject
+{
+ public $__table = 'sitemap_user_count'; // table name
+
+ public $registration_date; // date primary_key not_null
+ public $user_count; // int(4)
+ public $created;
+ public $modified;
+
+ /**
+ * Get an instance by key
+ *
+ * This is a utility method to get a single instance with a given key value.
+ *
+ * @param string $k Key to use to lookup (usually 'user_id' for this class)
+ * @param mixed $v Value to lookup
+ *
+ * @return Sitemap_user_count object found, or null for no hits
+ *
+ */
+
+ function staticGet($k, $v=null)
+ {
+ return Memcached_DataObject::staticGet('Sitemap_user_count', $k, $v);
+ }
+
+ /**
+ * return table definition for DB_DataObject
+ *
+ * DB_DataObject needs to know something about the table to manipulate
+ * instances. This method provides all the DB_DataObject needs to know.
+ *
+ * @return array array of column definitions
+ */
+
+ function table()
+ {
+ return array('registration_date' => DB_DATAOBJECT_DATE + DB_DATAOBJECT_NOTNULL,
+ 'user_count' => DB_DATAOBJECT_INT,
+ 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL,
+ 'modified' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL);
+ }
+
+ /**
+ * return key definitions for DB_DataObject
+ *
+ * DB_DataObject needs to know about keys that the table has; this function
+ * defines them.
+ *
+ * @return array key definitions
+ */
+
+ function keys()
+ {
+ return array('registration_date' => 'K');
+ }
+
+ function sequenceKey()
+ {
+ return array(false, false, false);
+ }
+
+ /**
+ * return key definitions for Memcached_DataObject
+ *
+ * Our caching system uses the same key definitions, but uses a different
+ * method to get them.
+ *
+ * @return array key definitions
+ */
+
+ function keyTypes()
+ {
+ return $this->keys();
+ }
+
+ static function getAll()
+ {
+ $userCounts = self::cacheGet('sitemap:user:counts');
+
+ if ($userCounts === false) {
+
+ $suc = new Sitemap_user_count();
+ $suc->orderBy('registration_date DESC');
+
+ // Fetch the first one to check up-to-date-itude
+
+ $n = $suc->find(true);
+
+ $today = self::today();
+ $userCounts = array();
+
+ if (!$n) { // No counts saved yet
+ $userCounts = self::initializeCounts();
+ } else if ($suc->registration_date < $today) { // There are counts but not up to today
+ $userCounts = self::fillInCounts($suc->registration_date);
+ } else if ($suc->registration_date == $today) { // Refresh today's
+ $userCounts[$today] = self::updateToday();
+ }
+
+ // starts with second-to-last date
+
+ while ($suc->fetch()) {
+ $userCounts[$suc->registration_date] = $suc->user_count;
+ }
+
+ self::cacheSet('sitemap:user:counts', $userCounts);
+ }
+
+ return $userCounts;
+ }
+
+ static function initializeCounts()
+ {
+ $firstDate = self::getFirstDate(); // awww
+ $today = self::today();
+
+ $counts = array();
+
+ for ($d = $firstDate; $d <= $today; $d = self::incrementDay($d)) {
+ $n = self::getCount($d);
+ self::insertCount($d, $n);
+ $counts[$d] = $n;
+ }
+
+ return $counts;
+ }
+
+ static function fillInCounts($lastDate)
+ {
+ $today = self::today();
+
+ $counts = array();
+
+ $n = self::getCount($lastDate);
+ self::updateCount($lastDate, $n);
+
+ $counts[$lastDate] = $n;
+
+ for ($d = self::incrementDay($lastDate); $d <= $today; $d = self::incrementDay($d)) {
+ $n = self::getCount($d);
+ self::insertCount($d, $n);
+ }
+
+ return $counts;
+ }
+
+ static function updateToday()
+ {
+ $today = self::today();
+
+ $n = self::getCount($today);
+ self::updateCount($today, $n);
+
+ return $n;
+ }
+
+ static function getCount($d)
+ {
+ $user = new User();
+ $user->whereAdd('created BETWEEN "'.$d.' 00:00:00" AND "'.self::incrementDay($d).' 00:00:00"');
+ $n = $user->count();
+
+ return $n;
+ }
+
+ static function insertCount($d, $n)
+ {
+ $suc = new Sitemap_user_count();
+
+ $suc->registration_date = DB_DataObject_Cast::date($d);
+ $suc->user_count = $n;
+ $suc->created = common_sql_now();
+ $suc->modified = $suc->created;
+
+ if (!$suc->insert()) {
+ common_log(LOG_WARNING, "Could not save user counts for '$d'");
+ }
+ }
+
+ static function updateCount($d, $n)
+ {
+ $suc = Sitemap_user_count::staticGet('registration_date', DB_DataObject_Cast::date($d));
+
+ if (empty($suc)) {
+ throw new Exception("No such registration date: $d");
+ }
+
+ $orig = clone($suc);
+
+ $suc->registration_date = DB_DataObject_Cast::date($d);
+ $suc->user_count = $n;
+ $suc->created = common_sql_now();
+ $suc->modified = $suc->created;
+
+ if (!$suc->update($orig)) {
+ common_log(LOG_WARNING, "Could not save user counts for '$d'");
+ }
+ }
+
+ static function incrementDay($d)
+ {
+ $dt = self::dateStrToInt($d);
+ return self::dateIntToStr($dt + 24 * 60 * 60);
+ }
+
+ static function dateStrToInt($d)
+ {
+ return strtotime($d.' 00:00:00');
+ }
+
+ static function dateIntToStr($dt)
+ {
+ return date('Y-m-d', $dt);
+ }
+
+ static function getFirstDate()
+ {
+ $u = new User();
+ $u->selectAdd();
+ $u->selectAdd('date(min(created)) as first_date');
+ if ($u->find(true)) {
+ return $u->first_date;
+ } else {
+ // Is this right?
+ return self::dateIntToStr(time());
+ }
+ }
+
+ static function today()
+ {
+ return self::dateIntToStr(time());
+ }
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show list of user pages
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Sitemap
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+/**
+ * sitemap for users
+ *
+ * @category Sitemap
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class NoticesitemapAction extends SitemapAction
+{
+ var $notices = null;
+ var $j = 0;
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $y = $this->trimmed('year');
+
+ $m = $this->trimmed('month');
+ $d = $this->trimmed('day');
+
+ $i = $this->trimmed('index');
+
+ $y += 0;
+ $m += 0;
+ $d += 0;
+ $i += 0;
+
+ $this->notices = $this->getNotices($y, $m, $d, $i);
+ $this->j = 0;
+
+ return true;
+ }
+
+ function nextUrl()
+ {
+ if ($this->j < count($this->notices)) {
+ $n = $this->notices[$this->j];
+ $this->j++;
+ return array(common_local_url('shownotice', array('notice' => $n[0])),
+ common_date_w3dtf($n[1]),
+ 'never',
+ null);
+ } else {
+ return null;
+ }
+ }
+
+ function getNotices($y, $m, $d, $i)
+ {
+ $n = Notice::cacheGet("sitemap:notice:$y:$m:$d:$i");
+
+ if ($n === false) {
+
+ $notice = new Notice();
+
+ $begindt = sprintf('%04d-%02d-%02d 00:00:00', $y, $m, $d);
+
+ // XXX: estimates 1d == 24h, which screws up days
+ // with leap seconds (1d == 24h + 1s). Thankfully they're
+ // few and far between.
+
+ $theend = strtotime($begindt) + (24 * 60 * 60);
+ $enddt = common_sql_date($theend);
+
+ $notice->selectAdd();
+ $notice->selectAdd('id, created');
+
+ $notice->whereAdd("created >= '$begindt'");
+ $notice->whereAdd("created < '$enddt'");
+
+ $notice->whereAdd('is_local = ' . Notice::LOCAL_PUBLIC);
+
+ $notice->orderBy('created');
+
+ $offset = ($i-1) * SitemapPlugin::NOTICES_PER_MAP;
+ $limit = SitemapPlugin::NOTICES_PER_MAP;
+
+ $notice->limit($offset, $limit);
+
+ $notice->find();
+
+ $n = array();
+
+ while ($notice->fetch()) {
+ $n[] = array($notice->id, $notice->created);
+ }
+
+ $c = Cache::instance();
+
+ if (!empty($c)) {
+ $c->set(Cache::key("sitemap:notice:$y:$m:$d:$i"),
+ $n,
+ Cache::COMPRESSED,
+ ((time() > $theend) ? (time() + 90 * 24 * 60 * 60) : (time() + 5 * 60)));
+ }
+ }
+
+ return $n;
+ }
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Superclass for sitemap-generating actions
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Sitemap
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+/**
+ * superclass for sitemap actions
+ *
+ * @category Sitemap
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class SitemapAction extends Action
+{
+ /**
+ * handle the action
+ *
+ * @param array $args unused.
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ header('Content-Type: text/xml; charset=UTF-8');
+ $this->startXML();
+
+ $this->elementStart('urlset', array('xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9'));
+
+ while (list($url, $lm, $cf, $p) = $this->nextUrl()) {
+ $this->showUrl($url, $lm, $cf, $p);
+ }
+
+ $this->elementEnd('urlset');
+
+ $this->endXML();
+ }
+
+ function showUrl($url, $lastMod=null, $changeFreq=null, $priority=null)
+ {
+ $this->elementStart('url');
+ $this->element('loc', null, $url);
+ if (!is_null($lastMod)) {
+ $this->element('lastmod', null, $lastMod);
+ }
+ if (!is_null($changeFreq)) {
+ $this->element('changefreq', null, $changeFreq);
+ }
+ if (!is_null($priority)) {
+ $this->element('priority', null, $priority);
+ }
+ $this->elementEnd('url');
+ }
+
+ function nextUrl()
+ {
+ return null;
+ }
+
+ function isReadOnly()
+ {
+ return true;
+ }
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Generate sitemap index
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Sitemap
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+/**
+ * Show the sitemap index
+ *
+ * @category Sitemap
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class SitemapindexAction extends Action
+{
+ /**
+ * handle the action
+ *
+ * @param array $args unused.
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ header('Content-Type: text/xml; charset=UTF-8');
+ $this->startXML();
+
+ $this->elementStart('sitemapindex', array('xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9'));
+
+ $this->showNoticeSitemaps();
+ $this->showUserSitemaps();
+
+ $this->elementEnd('sitemapindex');
+
+ $this->endXML();
+ }
+
+ function showUserSitemaps()
+ {
+ $userCounts = Sitemap_user_count::getAll();
+
+ foreach ($userCounts as $dt => $cnt) {
+ $cnt = $cnt+0;
+
+ if ($cnt == 0) {
+ continue;
+ }
+
+ $n = (int)$cnt / (int)SitemapPlugin::USERS_PER_MAP;
+ if (($cnt % SitemapPlugin::USERS_PER_MAP) != 0) {
+ $n++;
+ }
+ for ($i = 1; $i <= $n; $i++) {
+ $this->showSitemap('user', $dt, $i);
+ }
+ }
+ }
+
+ function showNoticeSitemaps()
+ {
+ $noticeCounts = Sitemap_notice_count::getAll();
+
+ foreach ($noticeCounts as $dt => $cnt) {
+ if ($cnt == 0) {
+ continue;
+ }
+ $n = $cnt / SitemapPlugin::NOTICES_PER_MAP;
+ if ($cnt % SitemapPlugin::NOTICES_PER_MAP) {
+ $n++;
+ }
+ for ($i = 1; $i <= $n; $i++) {
+ $this->showSitemap('notice', $dt, $i);
+ }
+ }
+ }
+
+ function showSitemap($prefix, $dt, $i)
+ {
+ list($y, $m, $d) = explode('-', $dt);
+
+ $this->elementStart('sitemap');
+ $this->element('loc', null, common_local_url($prefix.'sitemap',
+ array('year' => $y,
+ 'month' => $m,
+ 'day' => $d,
+ 'index' => $i)));
+
+ $begdate = strtotime("$y-$m-$d 00:00:00");
+ $enddate = $begdate + (24 * 60 * 60);
+
+ if ($enddate < time()) {
+ $this->element('lastmod', null, date(DATE_W3C, $enddate));
+ }
+
+ $this->elementEnd('sitemap');
+ }
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Show list of user pages
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Sitemap
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+/**
+ * sitemap for users
+ *
+ * @category Sitemap
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class UsersitemapAction extends SitemapAction
+{
+ var $users = null;
+ var $j = 0;
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $y = $this->trimmed('year');
+
+ $m = $this->trimmed('month');
+ $d = $this->trimmed('day');
+
+ $i = $this->trimmed('index');
+
+ $y += 0;
+ $m += 0;
+ $d += 0;
+ $i += 0;
+
+ $this->users = $this->getUsers($y, $m, $d, $i);
+ $this->j = 0;
+ return true;
+ }
+
+ function nextUrl()
+ {
+ if ($this->j < count($this->users)) {
+ $nickname = $this->users[$this->j];
+ $this->j++;
+ return array(common_profile_url($nickname), null, null, '1.0');
+ } else {
+ return null;
+ }
+ }
+
+ function getUsers($y, $m, $d, $i)
+ {
+ $u = User::cacheGet("sitemap:user:$y:$m:$d:$i");
+
+ if ($u === false) {
+
+ $user = new User();
+
+ $begindt = sprintf('%04d-%02d-%02d 00:00:00', $y, $m, $d);
+
+ // XXX: estimates 1d == 24h, which screws up days
+ // with leap seconds (1d == 24h + 1s). Thankfully they're
+ // few and far between.
+
+ $theend = strtotime($begindt) + (24 * 60 * 60);
+ $enddt = common_sql_date($theend);
+
+ $user->selectAdd();
+ $user->selectAdd('nickname');
+ $user->whereAdd("created >= '$begindt'");
+ $user->whereAdd("created < '$enddt'");
+
+ $user->orderBy('created');
+
+ $offset = ($i-1) * SitemapPlugin::USERS_PER_MAP;
+ $limit = SitemapPlugin::USERS_PER_MAP;
+
+ $user->limit($offset, $limit);
+
+ $user->find();
+
+ while ($user->fetch()) {
+ $u[] = $user->nickname;
+ }
+
+ $c = Cache::instance();
+
+ if (!empty($c)) {
+ $c->set(Cache::key("sitemap:user:$y:$m:$d:$i"),
+ $u,
+ Cache::COMPRESSED,
+ ((time() > $theend) ? (time() + 90 * 24 * 60 * 60) : (time() + 5 * 60)));
+ }
+ }
+
+ return $u;
+ }
+}