diff options
32 files changed, 2165 insertions, 368 deletions
diff --git a/actions/apidirectmessage.php b/actions/apidirectmessage.php index 53da9e0c6..7a0f46274 100644 --- a/actions/apidirectmessage.php +++ b/actions/apidirectmessage.php @@ -232,7 +232,8 @@ class ApiDirectMessageAction extends ApiAuthAction 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); diff --git a/actions/apistatusesdestroy.php b/actions/apistatusesdestroy.php index 8d9469063..0dfeb4812 100644 --- a/actions/apistatusesdestroy.php +++ b/actions/apistatusesdestroy.php @@ -100,32 +100,43 @@ class ApiStatusesDestroyAction extends ApiAuthAction 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 + ); + } } /** diff --git a/actions/apiusershow.php b/actions/apiusershow.php index 6c8fad49b..28993102c 100644 --- a/actions/apiusershow.php +++ b/actions/apiusershow.php @@ -113,7 +113,7 @@ class ApiUserShowAction extends ApiPrivateAuthAction 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'); diff --git a/actions/designadminpanel.php b/actions/designadminpanel.php index 8c08581b5..763737175 100644 --- a/actions/designadminpanel.php +++ b/actions/designadminpanel.php @@ -126,9 +126,19 @@ class DesignadminpanelAction extends AdminPanelAction 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'); @@ -140,15 +150,13 @@ class DesignadminpanelAction extends AdminPanelAction $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')); @@ -190,6 +198,13 @@ class DesignadminpanelAction extends AdminPanelAction 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'); } @@ -264,6 +279,33 @@ class DesignadminpanelAction extends AdminPanelAction } /** + * 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 * * @return void @@ -371,7 +413,15 @@ class DesignAdminPanelForm extends AdminForm 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')); @@ -384,6 +434,11 @@ class DesignAdminPanelForm extends AdminForm $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')); @@ -407,10 +462,23 @@ class DesignAdminPanelForm extends AdminForm 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' => @@ -486,6 +554,11 @@ class DesignAdminPanelForm extends AdminForm $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')); @@ -493,6 +566,7 @@ class DesignAdminPanelForm extends AdminForm $this->out->elementStart('ul', 'form_data'); try { + // @fixme avoid loop unrolling in non-performance-critical contexts like this $bgcolor = new WebColor($design->backgroundcolor); @@ -560,6 +634,7 @@ class DesignAdminPanelForm extends AdminForm $this->unli(); } catch (WebColorException $e) { + // @fixme normalize them individually! common_log(LOG_ERR, 'Bad color values in site design: ' . $e->getMessage()); } @@ -569,6 +644,27 @@ class DesignAdminPanelForm extends AdminForm $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 * diff --git a/actions/foaf.php b/actions/foaf.php index 2f054de0c..09af7b502 100644 --- a/actions/foaf.php +++ b/actions/foaf.php @@ -154,7 +154,9 @@ class FoafAction extends Action } $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 @@ -209,7 +211,8 @@ class FoafAction extends Action $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)); } @@ -234,7 +237,21 @@ class FoafAction extends Action $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) { @@ -256,9 +273,7 @@ class FoafAction extends Action $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; @@ -283,6 +298,9 @@ class FoafAction extends Action } 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'); diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 85273a9b7..747c22ebb 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -128,12 +128,13 @@ class Memcached_DataObject extends Safe_DataObject } 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) { @@ -229,10 +230,10 @@ class Memcached_DataObject extends Safe_DataObject 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()); } @@ -351,7 +352,7 @@ class Memcached_DataObject extends Safe_DataObject * 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. * @@ -604,5 +605,30 @@ class Memcached_DataObject extends Safe_DataObject 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; + } } diff --git a/classes/Notice.php b/classes/Notice.php index cf6f9c279..fd8ad5493 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -1254,6 +1254,8 @@ class Notice extends Memcached_DataObject 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)) { diff --git a/db/notice_source.sql b/db/notice_source.sql index 5d8664631..f5db37f04 100644 --- a/db/notice_source.sql +++ b/db/notice_source.sql @@ -18,6 +18,7 @@ VALUES ('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()), diff --git a/lib/action.php b/lib/action.php index 98e5ec2c9..2b3b707c5 100644 --- a/lib/action.php +++ b/lib/action.php @@ -235,6 +235,16 @@ class Action extends HTMLOutputter // lawsuit 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)); + } + } } } diff --git a/lib/adminpanelaction.php b/lib/adminpanelaction.php index 6c9947608..9e0b2d041 100644 --- a/lib/adminpanelaction.php +++ b/lib/adminpanelaction.php @@ -284,9 +284,10 @@ class AdminPanelAction extends Action $this->clientError(_("Unable to delete design setting.")); return null; } + return $result; } - return $result; + return null; } function canAdmin($name) diff --git a/lib/apiaction.php b/lib/apiaction.php index 04028aef0..a9fad16f8 100644 --- a/lib/apiaction.php +++ b/lib/apiaction.php @@ -297,6 +297,10 @@ class ApiAction extends Action } } + // StatusNet-specific + + $twitter_user['statusnet:profile_url'] = $profile->profileurl; + return $twitter_user; } @@ -398,6 +402,10 @@ class ApiAction extends Action $twitter_status['user'] = $twitter_user; } + // StatusNet-specific + + $twitter_status['statusnet:html'] = $notice->rendered; + return $twitter_status; } @@ -565,9 +573,13 @@ class ApiAction extends Action } } - 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': @@ -601,9 +613,13 @@ class ApiAction extends Action $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']); @@ -685,7 +701,7 @@ class ApiAction extends Action { $this->initDocument('xml'); $twitter_status = $this->twitterStatusArray($notice); - $this->showTwitterXmlStatus($twitter_status); + $this->showTwitterXmlStatus($twitter_status, 'status', true); $this->endDocument('xml'); } @@ -701,7 +717,8 @@ class ApiAction extends Action { $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) { @@ -868,9 +885,13 @@ class ApiAction extends Action $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': @@ -947,7 +968,7 @@ class ApiAction extends Action { $this->initDocument('xml'); $dmsg = $this->directMessageArray($message); - $this->showXmlDirectMessage($dmsg); + $this->showXmlDirectMessage($dmsg, true); $this->endDocument('xml'); } @@ -1064,7 +1085,8 @@ class ApiAction extends Action { $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) { diff --git a/lib/default.php b/lib/default.php index 754cf5728..e0081f316 100644 --- a/lib/default.php +++ b/lib/default.php @@ -141,10 +141,17 @@ $default = '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 @@ -260,6 +267,9 @@ $default = 'linkcolor' => null, 'backgroundimage' => null, 'disposition' => null), + 'custom_css' => + array('enabled' => true, + 'css' => ''), 'notice' => array('contentlimit' => null), 'message' => diff --git a/lib/installer.php b/lib/installer.php index 1cad2fd20..56b9b6f4a 100644 --- a/lib/installer.php +++ b/lib/installer.php @@ -82,9 +82,12 @@ abstract class Installer { $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', '<')) { diff --git a/lib/router.php b/lib/router.php index ef5fece13..6cbae8247 100644 --- a/lib/router.php +++ b/lib/router.php @@ -540,7 +540,7 @@ class Router $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', @@ -597,7 +597,7 @@ class Router $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', @@ -664,7 +664,7 @@ class Router // 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( diff --git a/lib/theme.php b/lib/theme.php index 0be8c3b9d..a9d0cbc84 100644 --- a/lib/theme.php +++ b/lib/theme.php @@ -38,6 +38,9 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { * 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. @@ -76,7 +79,7 @@ class Theme 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; } @@ -89,42 +92,63 @@ class Theme 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; } /** @@ -236,7 +260,13 @@ class Theme protected static function localRoot() { - return INSTALLDIR.'/local/theme'; + $basedir = common_config('local', 'dir'); + + if (empty($basedir)) { + $basedir = INSTALLDIR . '/local'; + } + + return $basedir . '/theme'; } /** diff --git a/lib/themeuploader.php b/lib/themeuploader.php new file mode 100644 index 000000000..18ef8c4d1 --- /dev/null +++ b/lib/themeuploader.php @@ -0,0 +1,311 @@ +<?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); + } + +} diff --git a/locale/de/LC_MESSAGES/statusnet.po b/locale/de/LC_MESSAGES/statusnet.po index 9f66162dc..f781e4ee5 100644 --- a/locale/de/LC_MESSAGES/statusnet.po +++ b/locale/de/LC_MESSAGES/statusnet.po @@ -16,12 +16,12 @@ msgid "" 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" @@ -92,13 +92,13 @@ msgid "Save" 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 @@ -122,7 +122,7 @@ msgid "No such user." 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" @@ -130,7 +130,7 @@ 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 @@ -138,25 +138,25 @@ msgid "%s and friends" 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." @@ -164,7 +164,7 @@ msgstr "" "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 " @@ -174,7 +174,7 @@ msgstr "" "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 " @@ -184,7 +184,7 @@ msgstr "" "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 " @@ -195,7 +195,7 @@ msgstr "" "erregen?" #. TRANS: H1 text -#: actions/all.php:179 +#: actions/all.php:182 msgid "You and friends" msgstr "Du und Freunde" @@ -207,8 +207,8 @@ 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 @@ -232,7 +232,7 @@ msgstr "Aktualisierungen von %1$s und Freunden auf %2$s!" 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 @@ -246,7 +246,7 @@ msgstr "API-Methode nicht gefunden." 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." @@ -254,7 +254,7 @@ msgstr "" "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." @@ -641,7 +641,7 @@ msgstr "Zugang zu deinem Konto erlauben oder ablehnen" 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." @@ -6161,6 +6161,9 @@ msgid "" "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 diff --git a/locale/fr/LC_MESSAGES/statusnet.po b/locale/fr/LC_MESSAGES/statusnet.po index cc2f510ea..7d8363990 100644 --- a/locale/fr/LC_MESSAGES/statusnet.po +++ b/locale/fr/LC_MESSAGES/statusnet.po @@ -15,12 +15,12 @@ msgid "" 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" @@ -90,13 +90,13 @@ msgid "Save" 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 @@ -120,7 +120,7 @@ msgid "No such user." 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" @@ -128,7 +128,7 @@ 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 @@ -136,25 +136,25 @@ msgid "%s and friends" 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." @@ -162,7 +162,7 @@ msgstr "" "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 " @@ -172,7 +172,7 @@ msgstr "" "(%%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 " @@ -182,7 +182,7 @@ msgstr "" "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 " @@ -192,7 +192,7 @@ msgstr "" "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" @@ -204,8 +204,8 @@ 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 @@ -229,7 +229,7 @@ msgstr "Statuts de %1$s et ses amis dans %2$s!" 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 @@ -243,7 +243,7 @@ msgstr "Méthode API non trouvée !" 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." @@ -251,7 +251,7 @@ msgstr "" "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." @@ -643,7 +643,7 @@ msgstr "Autoriser ou refuser l’accès à votre compte." 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." @@ -6188,6 +6188,9 @@ msgid "" "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 diff --git a/locale/hsb/LC_MESSAGES/statusnet.po b/locale/hsb/LC_MESSAGES/statusnet.po index d487b6085..6ed35a744 100644 --- a/locale/hsb/LC_MESSAGES/statusnet.po +++ b/locale/hsb/LC_MESSAGES/statusnet.po @@ -9,12 +9,12 @@ msgid "" 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" @@ -40,7 +40,7 @@ msgstr "Registrowanje" #. 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 @@ -85,13 +85,13 @@ msgid "Save" 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 @@ -115,7 +115,7 @@ msgid "No such user." 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" @@ -123,7 +123,7 @@ 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 @@ -131,31 +131,31 @@ msgid "%s and friends" 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 " @@ -163,14 +163,14 @@ msgid "" 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 " @@ -178,7 +178,7 @@ msgid "" msgstr "" #. TRANS: H1 text -#: actions/all.php:179 +#: actions/all.php:182 msgid "You and friends" msgstr "Ty a pÅ™ećeljo" @@ -190,8 +190,8 @@ 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 @@ -215,7 +215,7 @@ msgstr "Aktualizacije wot %1$s a pÅ™ećelow na %2$s!" 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 @@ -229,13 +229,13 @@ msgstr "API-metoda njenamakana." 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ć." @@ -328,6 +328,8 @@ msgstr "PÅ™ijimowar njenamakany." #: 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 @@ -340,7 +342,7 @@ msgstr "Tutón status je hižo faworit." #: 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." @@ -348,7 +350,7 @@ msgstr "Tón status faworit njeje." #: 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." @@ -369,7 +371,7 @@ msgstr "NjemóžeÅ¡ slÄ›dowanje swójskich aktiwitow blokować." #: 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." @@ -556,7 +558,7 @@ msgstr "" #: 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" @@ -609,7 +611,7 @@ msgstr "PÅ™istup ke kontowym informacijam dowolić abo wotpokazać." 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ć." @@ -1510,7 +1512,7 @@ msgstr "Žana adresa za dochadźace e-mejle." #: 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 @@ -1585,7 +1587,7 @@ msgstr "" #: 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." @@ -1649,7 +1651,7 @@ msgstr "NjepÅ‚aćiwa róla." #: 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." @@ -1712,7 +1714,7 @@ msgstr "Tutoho wužiwarja za tutu skupinu blokować" #: 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." @@ -1763,7 +1765,7 @@ msgstr "Logo zaktualizowane." #: 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 @@ -1860,6 +1862,8 @@ msgid "" "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 @@ -2152,7 +2156,7 @@ msgstr "" #: 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." @@ -2217,6 +2221,8 @@ msgid "" "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." @@ -2227,6 +2233,7 @@ msgstr "PÅ™izjewjenje z twojim wužiwarskim mjenom a hesÅ‚om." 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." @@ -2819,7 +2826,7 @@ msgstr "MÄ›stno" #: 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" @@ -2899,7 +2906,7 @@ msgstr "Nastajenja skÅ‚adowane." #: 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." @@ -3644,12 +3651,12 @@ msgstr "" #: 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." @@ -3773,7 +3780,7 @@ msgstr "SydÅ‚owe mjeno" #: 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" @@ -3805,7 +3812,7 @@ msgstr "Standardne Äasowe pasmo" #: 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" @@ -3968,7 +3975,7 @@ msgstr "" #. 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 @@ -4074,7 +4081,7 @@ msgstr "Njejsy tón profil abonowaÅ‚." #: 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." @@ -4287,7 +4294,7 @@ msgstr "" #: 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 @@ -4369,7 +4376,7 @@ msgstr "Tutón abonement wotpokazać" #: actions/userauthorization.php:232 msgid "No authorization request!" -msgstr "" +msgstr "Žane awtorizaciske napraÅ¡owanje!" #: actions/userauthorization.php:254 msgid "Subscription authorized" @@ -4416,7 +4423,7 @@ msgstr "" #: 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 @@ -4569,7 +4576,7 @@ msgstr "" #: 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." @@ -4650,11 +4657,11 @@ msgstr "Abonoment njeje so daÅ‚ zniÄić." #: 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." @@ -4662,7 +4669,7 @@ msgstr "URI skupiny njeda so nastajić." #: 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." @@ -4671,17 +4678,17 @@ msgstr "Informacije wo lokalnej skupinje njedachu so skÅ‚adować." #. 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 @@ -4985,7 +4992,7 @@ msgstr "" #. 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 @@ -4995,12 +5002,12 @@ msgstr "ZmÄ›ny na tutym woknje njejsu dowolene." #. 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. @@ -5011,7 +5018,7 @@ msgstr "" #. 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 @@ -5104,12 +5111,12 @@ msgstr "URL žórÅ‚a" #. 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 @@ -5134,12 +5141,12 @@ msgstr "" #. 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 @@ -5154,12 +5161,12 @@ msgstr "PÅ™etorhnyć" #. 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 @@ -5229,18 +5236,18 @@ msgstr "Wužiwar nima poslednju powÄ›sć" #: 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!" @@ -5370,7 +5377,7 @@ msgstr "" #: 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." @@ -5379,16 +5386,16 @@ msgstr "OMB-profile njedadźa so pÅ™ez pÅ™ikaz abonować." #: 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." @@ -5412,7 +5419,7 @@ msgstr "" #: lib/command.php:723 msgid "Login command is disabled" -msgstr "" +msgstr "PÅ™izjewjenski pÅ™ikaz je znjemóžnjeny" #: lib/command.php:734 #, php-format @@ -5426,7 +5433,7 @@ msgstr "%s wotskazany" #: 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:" @@ -5438,7 +5445,7 @@ msgstr[3] "Sy tute wosoby abonowaÅ‚:" #: 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:" @@ -5508,15 +5515,15 @@ msgstr "Žana konfiguraciska dataja namakana. " #: 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" @@ -5536,7 +5543,7 @@ msgstr "Zwiski" #: lib/connectsettingsaction.php:121 msgid "Authorized connected applications" -msgstr "" +msgstr "Awtorizowane zwjazane aplikacije" #: lib/dberroraction.php:60 msgid "Database error" @@ -5611,7 +5618,7 @@ msgstr "" #: lib/galleryaction.php:143 msgid "Go" -msgstr "" +msgstr "Start" #: lib/grantroleform.php:91 #, php-format @@ -5635,6 +5642,8 @@ msgstr "Skupinu abo temu w %d znamjeÅ¡kach wopisać" 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 @@ -5657,7 +5666,7 @@ msgstr "" #: 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" @@ -5690,6 +5699,8 @@ msgstr "" #: 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." @@ -5740,7 +5751,7 @@ msgstr "Njeznate žórÅ‚o postoweho kašćika %d." #: lib/joinform.php:114 msgid "Join" -msgstr "" +msgstr "Zastupić" #: lib/leaveform.php:114 msgid "Leave" @@ -5934,6 +5945,9 @@ msgid "" "\n" "\t%s" msgstr "" +"DospoÅ‚nu rozmoÅ‚wu móžes tu Äitać:\n" +"\n" +"%s" #: lib/mail.php:657 #, php-format @@ -5970,7 +5984,7 @@ msgstr "" #: 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 "" @@ -5984,7 +5998,7 @@ msgstr "wot" #: 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." @@ -6006,6 +6020,8 @@ msgstr "NjepodpÄ›rany powÄ›sćowy typ: %s" #: 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." @@ -6027,11 +6043,11 @@ msgstr "Temporerny rjadowka faluje." #: 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." @@ -6039,16 +6055,16 @@ msgstr "" #: 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 @@ -6174,7 +6190,7 @@ msgstr "Zmylk pÅ™i zasunjenju awatara" #: 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" @@ -6186,7 +6202,7 @@ msgstr "Dwójna zdźělenka" #: lib/oauthstore.php:490 msgid "Couldn't insert new subscription." -msgstr "" +msgstr "Nowy abonement njeda so zasunyć." #: lib/personalgroupnav.php:99 msgid "Personal" @@ -6305,7 +6321,7 @@ msgstr "Rólu \"%s\" tutoho wužiwarja wotwoÅ‚ać" #: 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" @@ -6366,7 +6382,7 @@ msgstr "" #: lib/subgroupnav.php:83 #, php-format msgid "People %s subscribes to" -msgstr "" +msgstr "Ludźo, kotrychž %s abonuje" #: lib/subgroupnav.php:91 #, php-format @@ -6376,7 +6392,7 @@ msgstr "Ludźo, kotÅ™iž su %s abonowali" #: 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" @@ -6444,7 +6460,7 @@ msgstr "Wužiwarske akcije" #: lib/userprofile.php:237 msgid "User deletion in progress..." -msgstr "" +msgstr "Wužiwar so haÅ¡a..." #: lib/userprofile.php:263 msgid "Edit profile settings" @@ -6464,7 +6480,7 @@ msgstr "PowÄ›sć" #: lib/userprofile.php:326 msgid "Moderate" -msgstr "" +msgstr "ModerÄ›rować" #: lib/userprofile.php:364 msgid "User role" @@ -6478,7 +6494,7 @@ msgstr "Administrator" #: 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 diff --git a/locale/ia/LC_MESSAGES/statusnet.po b/locale/ia/LC_MESSAGES/statusnet.po index 77b7e0dd4..6f5262271 100644 --- a/locale/ia/LC_MESSAGES/statusnet.po +++ b/locale/ia/LC_MESSAGES/statusnet.po @@ -8,12 +8,12 @@ msgid "" 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" @@ -83,13 +83,13 @@ msgid "Save" 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 @@ -113,7 +113,7 @@ msgid "No such user." 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" @@ -121,7 +121,7 @@ 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 @@ -129,25 +129,25 @@ msgid "%s and friends" 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." @@ -155,7 +155,7 @@ msgstr "" "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 " @@ -165,7 +165,7 @@ msgstr "" "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 " @@ -174,7 +174,7 @@ msgstr "" "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 " @@ -184,7 +184,7 @@ msgstr "" "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" @@ -196,8 +196,8 @@ 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 @@ -221,7 +221,7 @@ msgstr "Actualisationes de %1$s e su amicos in %2$s!" 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 @@ -235,7 +235,7 @@ msgstr "Methodo API non trovate." 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." @@ -243,7 +243,7 @@ msgstr "" "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." @@ -627,7 +627,7 @@ msgstr "Permitter o refusar accesso al informationes de tu conto." 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." @@ -6119,6 +6119,9 @@ msgid "" "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 diff --git a/locale/pt/LC_MESSAGES/statusnet.po b/locale/pt/LC_MESSAGES/statusnet.po index 70e3788d8..4ad9a911d 100644 --- a/locale/pt/LC_MESSAGES/statusnet.po +++ b/locale/pt/LC_MESSAGES/statusnet.po @@ -10,12 +10,12 @@ msgid "" 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" @@ -85,13 +85,13 @@ msgid "Save" 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 @@ -115,7 +115,7 @@ msgid "No such user." 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" @@ -123,7 +123,7 @@ 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 @@ -131,32 +131,32 @@ msgid "%s and friends" 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 " @@ -166,7 +166,7 @@ msgstr "" "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 " @@ -175,7 +175,7 @@ msgstr "" "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 " @@ -185,7 +185,7 @@ msgstr "" "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" @@ -197,8 +197,8 @@ 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 @@ -222,7 +222,7 @@ msgstr "Actualizações de %1$s e amigos no %2$s!" 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 @@ -236,14 +236,14 @@ msgstr "Método da API não encontrado." 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." @@ -624,7 +624,7 @@ msgstr "Permitir ou negar acesso à informação da sua conta." 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." @@ -2952,11 +2952,11 @@ msgstr "" #: 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" @@ -2982,7 +2982,7 @@ msgstr "Fuso horário não foi seleccionado." #: 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 @@ -4013,12 +4013,12 @@ msgstr "Fuso horário por omissão, para o site; normalmente, UTC." #: 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 @@ -6114,6 +6114,9 @@ msgid "" "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 diff --git a/locale/ru/LC_MESSAGES/statusnet.po b/locale/ru/LC_MESSAGES/statusnet.po index 81cfe0aad..df75bded2 100644 --- a/locale/ru/LC_MESSAGES/statusnet.po +++ b/locale/ru/LC_MESSAGES/statusnet.po @@ -12,12 +12,12 @@ msgid "" 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" @@ -89,13 +89,13 @@ msgid "Save" 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 @@ -119,7 +119,7 @@ msgid "No such user." 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" @@ -127,7 +127,7 @@ 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 @@ -135,31 +135,31 @@ msgid "%s and friends" 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 " @@ -169,7 +169,7 @@ msgstr "" "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 " @@ -179,7 +179,7 @@ msgstr "" "что-нибудь Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð²Ð»ÐµÑ‡ÐµÐ½Ð¸Ñ ÐµÐ³Ð¾ или её вниманиÑ](%%%%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 " @@ -189,7 +189,7 @@ msgstr "" "s или отправить запиÑÑŒ Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð²Ð»ÐµÑ‡ÐµÐ½Ð¸Ñ ÐµÐ³Ð¾ или её вниманиÑ?" #. TRANS: H1 text -#: actions/all.php:179 +#: actions/all.php:182 msgid "You and friends" msgstr "Ð’Ñ‹ и друзьÑ" @@ -201,8 +201,8 @@ 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 @@ -226,7 +226,7 @@ msgstr "Обновлено от %1$s и его друзей на %2$s!" 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 @@ -240,7 +240,7 @@ msgstr "Метод API не найден." 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." @@ -248,7 +248,7 @@ msgstr "" "Ð’Ñ‹ должны указать параметр Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ «device» и одним из значений: sms, im, " "none." -#: actions/apiaccountupdatedeliverydevice.php:132 +#: actions/apiaccountupdatedeliverydevice.php:133 msgid "Could not update user." msgstr "Ðе удаётÑÑ Ð¾Ð±Ð½Ð¾Ð²Ð¸Ñ‚ÑŒ пользователÑ." @@ -636,7 +636,7 @@ 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 "Ð’Ñ‹ не можете удалÑÑ‚ÑŒ ÑÑ‚Ð°Ñ‚ÑƒÑ Ð´Ñ€ÑƒÐ³Ð¸Ñ… пользователей." @@ -6136,6 +6136,9 @@ msgid "" "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 diff --git a/locale/statusnet.pot b/locale/statusnet.pot index 789e4bc86..d2890fa82 100644 --- a/locale/statusnet.pot +++ b/locale/statusnet.pot @@ -8,7 +8,7 @@ msgid "" 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" @@ -81,13 +81,13 @@ msgid "Save" 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 @@ -111,7 +111,7 @@ msgid "No such user." 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 "" @@ -119,7 +119,7 @@ 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 @@ -127,31 +127,31 @@ msgid "%s and friends" 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 " @@ -159,14 +159,14 @@ msgid "" 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 " @@ -174,7 +174,7 @@ msgid "" msgstr "" #. TRANS: H1 text -#: actions/all.php:179 +#: actions/all.php:182 msgid "You and friends" msgstr "" @@ -186,8 +186,8 @@ 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 @@ -211,7 +211,7 @@ msgstr "" 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 @@ -225,13 +225,13 @@ msgstr "" 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 "" @@ -605,7 +605,7 @@ 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 "" diff --git a/locale/uk/LC_MESSAGES/statusnet.po b/locale/uk/LC_MESSAGES/statusnet.po index dbf7fc836..046366ae6 100644 --- a/locale/uk/LC_MESSAGES/statusnet.po +++ b/locale/uk/LC_MESSAGES/statusnet.po @@ -11,12 +11,12 @@ msgid "" 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" @@ -89,13 +89,13 @@ msgid "Save" 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 @@ -119,7 +119,7 @@ msgid "No such user." 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" @@ -127,7 +127,7 @@ 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 @@ -135,31 +135,31 @@ msgid "%s and friends" 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 " @@ -169,7 +169,7 @@ 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 " @@ -178,7 +178,7 @@ msgstr "" "Ви можете [«розштовхати» %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 " @@ -188,7 +188,7 @@ msgstr "" "«розштовхати» %s або щоÑÑŒ йому напиÑати." #. TRANS: H1 text -#: actions/all.php:179 +#: actions/all.php:182 msgid "You and friends" msgstr "Ви з друзÑми" @@ -200,8 +200,8 @@ 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 @@ -225,7 +225,7 @@ msgstr "ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ %1$s та друзів на %2$s!" 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 @@ -239,14 +239,14 @@ msgstr "API метод не знайдено." 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 "Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ кориÑтувача." @@ -453,7 +453,7 @@ msgstr "Помилкове додаткове ім’Ñ: «%s»." #: 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 @@ -632,7 +632,7 @@ 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 "Ви не можете видалити ÑÑ‚Ð°Ñ‚ÑƒÑ Ñ–Ð½ÑˆÐ¾Ð³Ð¾ кориÑтувача." @@ -970,7 +970,7 @@ 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" @@ -1315,7 +1315,7 @@ msgstr "Ð¾Ð¿Ð¸Ñ Ð½Ð°Ð´Ñ‚Ð¾ довгий (%d знаків макÑимум)." #: 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." @@ -1385,7 +1385,7 @@ msgstr "СкаÑувати" #. 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. @@ -1444,7 +1444,7 @@ msgstr "ÐадÑилати мені лиÑта, коли хтоÑÑŒ має прР#. 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 @@ -1882,9 +1882,9 @@ msgid "" msgstr "" "Групи на Ñайті %%%%site.name%%%% дозволÑÑ‚ÑŒ Вам відшукати людей зі Ñпільними " "інтереÑами. Лише приєднайтеÑÑ Ð´Ð¾ групи Ñ– надÑилайте Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð´Ð¾ уÑÑ–Ñ… Ñ—Ñ— " -"учаÑників викориÑтовуючи проÑту команду \"!groupname\" у текÑÑ‚Ñ– " -"повідомленнÑ. Ðе бачите групу, Ñка Ð’Ð°Ñ Ñ†Ñ–ÐºÐ°Ð²Ð¸Ñ‚ÑŒ? Спробуйте Ñ—Ñ— [знайти](%%%%" -"action.groupsearch%%%%) або [Ñтворіть влаÑну!](%%%%action.newgroup%%%%)" +"учаÑників викориÑтовуючи проÑту команду «!groupname» у текÑÑ‚Ñ– повідомленнÑ. " +"Ðе бачите групу, Ñка Ð’Ð°Ñ Ñ†Ñ–ÐºÐ°Ð²Ð¸Ñ‚ÑŒ? Спробуйте Ñ—Ñ— [знайти](%%%%action." +"groupsearch%%%%) або [Ñтворіть влаÑну!](%%%%action.newgroup%%%%)" #: actions/groups.php:107 actions/usergroups.php:126 lib/groupeditform.php:122 msgid "Create a new group" @@ -1991,7 +1991,7 @@ msgid "" "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." @@ -2468,12 +2468,12 @@ msgstr "" #: 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 "" @@ -2997,7 +2997,7 @@ msgstr "Мова задовга (50 знаків макÑимум)." #: 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." @@ -3079,7 +3079,7 @@ msgid "" "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%%))" @@ -3092,7 +3092,7 @@ msgid "" "tool." msgstr "" "Це %%site.name%% — ÑÐµÑ€Ð²Ñ–Ñ [мікроблоґів](http://uk.wikipedia.org/wiki/" -"Мікроблоггінг), Ñкий працює на вільному програмному забезпеченні [StatusNet]" +"Мікроблогінг), Ñкий працює на вільному програмному забезпеченні [StatusNet]" "(http://status.net/)." #: actions/publictagcloud.php:57 @@ -3803,7 +3803,7 @@ msgid "" "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%%%" @@ -3817,10 +3817,10 @@ msgid "" "[StatusNet](http://status.net/) tool. Its members share short messages about " "their life and interests. " msgstr "" -"**%s** це група кориÑтувачів на %%site.name%% — ÑервіÑÑ– [мікроблоґів](http://" -"uk.wikipedia.org/wiki/Мікроблоггінг), Ñкий працює на вільному програмному " -"забезпеченні [StatusNet](http://status.net/). Члени цієї групи роблÑÑ‚ÑŒ " -"короткі допиÑи про Ñвоє Ð¶Ð¸Ñ‚Ñ‚Ñ Ñ‚Ð° інтереÑи. " +"**%s** це група кориÑтувачів на %%%%site.name%%%% — ÑервіÑÑ– [мікроблоґів]" +"(http://uk.wikipedia.org/wiki/Мікроблогінг), Ñкий працює на вільному " +"програмному забезпеченні [StatusNet](http://status.net/). Члени цієї групи " +"роблÑÑ‚ÑŒ короткі допиÑи про Ñвоє Ð¶Ð¸Ñ‚Ñ‚Ñ Ñ‚Ð° інтереÑи. " #: actions/showgroup.php:497 msgid "Admins" @@ -3914,7 +3914,7 @@ msgid "" "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%%%%))" @@ -3927,7 +3927,7 @@ msgid "" "[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 @@ -4064,11 +4064,12 @@ msgstr "МакÑимальна довжина Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÑÐ°Ð¹Ñ‚Ñ #: 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" @@ -4519,7 +4520,7 @@ msgstr "Помилковий текÑÑ‚ привітаннÑ. МакÑималь #: 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 @@ -4643,32 +4644,32 @@ msgstr "URI Ñлухача «%s» тут не знайдено" #: 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" @@ -5928,7 +5929,7 @@ msgstr "Вперед" #: 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" @@ -6670,7 +6671,7 @@ msgstr "ІД" #: lib/profileaction.php:196 msgid "Member since" -msgstr "З нами від" +msgstr "РеєÑтраціÑ" #. TRANS: Average count of posts made per day since account registration #: lib/profileaction.php:235 @@ -6724,7 +6725,7 @@ msgstr "Повторити цей допиÑ" #: 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." diff --git a/plugins/GeonamesPlugin.php b/plugins/GeonamesPlugin.php index bc5899943..3815a31fa 100644 --- a/plugins/GeonamesPlugin.php +++ b/plugins/GeonamesPlugin.php @@ -376,7 +376,7 @@ class GeonamesPlugin extends Plugin 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; diff --git a/plugins/Sitemap/SitemapPlugin.php b/plugins/Sitemap/SitemapPlugin.php new file mode 100644 index 000000000..7ef5f1aa9 --- /dev/null +++ b/plugins/Sitemap/SitemapPlugin.php @@ -0,0 +1,163 @@ +<?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; + } +} diff --git a/plugins/Sitemap/Sitemap_notice_count.php b/plugins/Sitemap/Sitemap_notice_count.php new file mode 100644 index 000000000..2a375b3e4 --- /dev/null +++ b/plugins/Sitemap/Sitemap_notice_count.php @@ -0,0 +1,288 @@ +<?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()); + } +} diff --git a/plugins/Sitemap/Sitemap_user_count.php b/plugins/Sitemap/Sitemap_user_count.php new file mode 100644 index 000000000..64b4c3442 --- /dev/null +++ b/plugins/Sitemap/Sitemap_user_count.php @@ -0,0 +1,284 @@ +<?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()); + } +} diff --git a/plugins/Sitemap/noticesitemap.php b/plugins/Sitemap/noticesitemap.php new file mode 100644 index 000000000..7d9d2e5d6 --- /dev/null +++ b/plugins/Sitemap/noticesitemap.php @@ -0,0 +1,137 @@ +<?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; + } +} diff --git a/plugins/Sitemap/sitemapaction.php b/plugins/Sitemap/sitemapaction.php new file mode 100644 index 000000000..45edfccc5 --- /dev/null +++ b/plugins/Sitemap/sitemapaction.php @@ -0,0 +1,95 @@ +<?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; + } +} diff --git a/plugins/Sitemap/sitemapindex.php b/plugins/Sitemap/sitemapindex.php new file mode 100644 index 000000000..169e3031c --- /dev/null +++ b/plugins/Sitemap/sitemapindex.php @@ -0,0 +1,128 @@ +<?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'); + } +} diff --git a/plugins/Sitemap/usersitemap.php b/plugins/Sitemap/usersitemap.php new file mode 100644 index 000000000..de1200715 --- /dev/null +++ b/plugins/Sitemap/usersitemap.php @@ -0,0 +1,128 @@ +<?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; + } +} |