]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch '0.9.x' of gitorious.org:statusnet/mainline into 1.0.x
authorBrion Vibber <brion@pobox.com>
Fri, 11 Jun 2010 19:09:55 +0000 (12:09 -0700)
committerBrion Vibber <brion@pobox.com>
Fri, 11 Jun 2010 19:09:55 +0000 (12:09 -0700)
32 files changed:
actions/apidirectmessage.php
actions/apistatusesdestroy.php
actions/apiusershow.php
actions/designadminpanel.php
actions/foaf.php
classes/Memcached_DataObject.php
classes/Notice.php
db/notice_source.sql
lib/action.php
lib/adminpanelaction.php
lib/apiaction.php
lib/default.php
lib/installer.php
lib/router.php
lib/theme.php
lib/themeuploader.php [new file with mode: 0644]
locale/de/LC_MESSAGES/statusnet.po
locale/fr/LC_MESSAGES/statusnet.po
locale/hsb/LC_MESSAGES/statusnet.po
locale/ia/LC_MESSAGES/statusnet.po
locale/pt/LC_MESSAGES/statusnet.po
locale/ru/LC_MESSAGES/statusnet.po
locale/statusnet.pot
locale/uk/LC_MESSAGES/statusnet.po
plugins/GeonamesPlugin.php
plugins/Sitemap/SitemapPlugin.php [new file with mode: 0644]
plugins/Sitemap/Sitemap_notice_count.php [new file with mode: 0644]
plugins/Sitemap/Sitemap_user_count.php [new file with mode: 0644]
plugins/Sitemap/noticesitemap.php [new file with mode: 0644]
plugins/Sitemap/sitemapaction.php [new file with mode: 0644]
plugins/Sitemap/sitemapindex.php [new file with mode: 0644]
plugins/Sitemap/usersitemap.php [new file with mode: 0644]

index 53da9e0c68a297195c74c6b33eee1c8a5648539a..7a0f46274cbde24478e222da8bdcbbce2bd50f1a 100644 (file)
@@ -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);
index 8d94690636c58c5dfd88775b6445f9769a1373bc..0dfeb48122df5e8efa43992e5408f26d411aa59e 100644 (file)
@@ -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
+            );
+        }
     }
 
     /**
index 6c8fad49ba9f5caac1ea15fb73867606a0f4d056..28993102c0065085def40ecdeae7297a7746efdc 100644 (file)
@@ -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');
index 8c08581b5d1243af59bbb4ce9a2707396ccb6b41..763737175bc73de5703fb2e32d92276592712cf8 100644 (file)
@@ -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');
     }
 
@@ -263,6 +278,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
      *
@@ -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
      *
index 2f054de0c9f73885fafbb49e00e7808ca2cd9e29..09af7b5026a9f0dd03f7781b8658b9ef4470761f 100644 (file)
@@ -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');
index 85273a9b7a265734f4fffadd0f6759722833dc53..747c22ebb6c0af3993f931854476a988a0fe01ec 100644 (file)
@@ -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;
+    }
 }
 
index cf6f9c279da30a482676058029cc84cc9626e1f3..fd8ad5493d0cc1a7a2d4e50b91ebc96530a94aaf 100644 (file)
@@ -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)) {
index 5d86646315c2e156a63b829988654b466e2f58cb..f5db37f04edf2452b31153067907a0ac5c34288c 100644 (file)
@@ -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()),
index 98e5ec2c94e7d89bb0d7472d4d2e0d423ee075f6..2b3b707c5968f88ed72013a3c40e39b2fdfa8a49 100644 (file)
@@ -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));
+                }
+            }
         }
     }
 
index 6c9947608c3e00210bd6eb7871e906e582bc2bda..9e0b2d041b422ea9c5c8ba220f50c1c1e952a47b 100644 (file)
@@ -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)
index 04028aef083220b3a093d8f00e2d22e0cbc40934..a9fad16f875d2766c0869d7c99244ef983b8f8b2 100644 (file)
@@ -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) {
index 754cf5728b28c0d2d9164fae73d933ac032eb0e6..e0081f3165e5a1001fc009e460d5d7ec1e23b26e 100644 (file)
@@ -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' =>
index 1cad2fd20ab1cd6add85acd9d39724b755bcecfb..56b9b6f4a25b510de25d7c02fd36c8ed6586e354 100644 (file)
@@ -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', '<')) {
index ef5fece137d31882ce4559b215c888720bcf2dc1..6cbae82472d7047a340718021cfc656814c3d3fa 100644 (file)
@@ -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(
index 0be8c3b9dfaa8ba2631bb67e08cbfb49b36ceb60..a9d0cbc84d2f57802e7b23a790d6f805b2639b7c 100644 (file)
@@ -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 (file)
index 0000000..18ef8c4
--- /dev/null
@@ -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);
+    }
+
+}
index 9f66162dcd66f35edaf6dbf0a8b523f26d580510..f781e4ee58b79f8928adda26eac8fd5ce9384951 100644 (file)
@@ -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
index cc2f510ea3aceb660365af66157c460ac7e3ede0..7d83639902c7af28db5b9c7b809c1f81d6365b38 100644 (file)
@@ -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
index d487b608581f373ad0663c0d51e1604be4c36ddb..6ed35a7442604943bc0e32b206e9fd35d72b5447 100644 (file)
@@ -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
index 77b7e0dd40c68a910c291b429aec1e986e3242bb..6f52622719600ea06b608649a67bc18333ac951b 100644 (file)
@@ -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
index 70e3788d8e1a0e5c9c7e5afc0a0f8795440018b8..4ad9a911dd5f737cb27c991b38a78ff0218071ff 100644 (file)
@@ -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
index 81cfe0aad12349d76ab3a61e3ea301e047055dfd..df75bded20ba835b60a4ca4dcf432fe8baac0666 100644 (file)
@@ -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
index 789e4bc869eb70a5c2c04488236e1e83105cf523..d2890fa826b224aa0efdc97cfdd2125ccaebb247 100644 (file)
@@ -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 ""
 
index dbf7fc836a93a5bbdcc6a155ab013c372bb973c4..046366ae66639d720e76709f32bac798888ecaf4 100644 (file)
@@ -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\" у тексті "
-"повÑ\96домленнÑ\8f. Ð\9dе Ð±Ð°Ñ\87иÑ\82е Ð³Ñ\80Ñ\83пÑ\83, Ñ\8fка Ð\92аÑ\81 Ñ\86Ñ\96кавиÑ\82Ñ\8c? Ð¡Ð¿Ñ\80обÑ\83йÑ\82е Ñ\97Ñ\97 [знайÑ\82и](%%%%"
-"action.groupsearch%%%%) або [створіть власну!](%%%%action.newgroup%%%%)"
+"учасників використовуючи просту команду «!groupname» у тексті повідомлення. "
+"Ð\9dе Ð±Ð°Ñ\87иÑ\82е Ð³Ñ\80Ñ\83пÑ\83, Ñ\8fка Ð\92аÑ\81 Ñ\86Ñ\96кавиÑ\82Ñ\8c? Ð¡Ð¿Ñ\80обÑ\83йÑ\82е Ñ\97Ñ\97 [знайÑ\82и](%%%%action."
+"groupsearch%%%%) або [створіть власну!](%%%%action.newgroup%%%%)"
 
 #: actions/groups.php:107 actions/usergroups.php:126 lib/groupeditform.php:122
 msgid "Create a new group"
@@ -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/Мікроблоггінг), який працює на вільному програмному "
-"забезпеÑ\87еннÑ\96 [StatusNet](http://status.net/). Ð§Ð»ÐµÐ½Ð¸ Ñ\86Ñ\96Ñ\94Ñ\97 Ð³Ñ\80Ñ\83пи Ñ\80облÑ\8fÑ\82Ñ\8c "
-"короткі дописи про своє життя та інтереси. "
+"**%s** це група користувачів на %%%%site.name%%%% — сервісі [мікроблоґів]"
+"(http://uk.wikipedia.org/wiki/Мікроблогінг), який працює на вільному "
+"пÑ\80огÑ\80амномÑ\83 Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ\87еннÑ\96 [StatusNet](http://status.net/). Ð§Ð»ÐµÐ½Ð¸ Ñ\86Ñ\96Ñ\94Ñ\97 Ð³Ñ\80Ñ\83пи "
+"роблять короткі дописи про своє життя та інтереси. "
 
 #: actions/showgroup.php:497
 msgid "Admins"
@@ -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 "Ð\97 Ð½Ð°Ð¼Ð¸ Ð²Ñ\96д"
+msgstr "РеÑ\94Ñ\81Ñ\82Ñ\80аÑ\86Ñ\96Ñ\8f"
 
 #. TRANS: Average count of posts made per day since account registration
 #: lib/profileaction.php:235
@@ -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."
index bc5899943beab5594abee3ec9495cf12b66d2d2f..3815a31fa681d90ed8b1fb8782f4791918bd18f4 100644 (file)
@@ -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 (file)
index 0000000..7ef5f1a
--- /dev/null
@@ -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 (file)
index 0000000..2a375b3
--- /dev/null
@@ -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 (file)
index 0000000..64b4c34
--- /dev/null
@@ -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 (file)
index 0000000..7d9d2e5
--- /dev/null
@@ -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 (file)
index 0000000..45edfcc
--- /dev/null
@@ -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 (file)
index 0000000..169e303
--- /dev/null
@@ -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 (file)
index 0000000..de12007
--- /dev/null
@@ -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;
+    }
+}