]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch 'userdesign' of evan@dev.controlyourself.ca:/var/www/evan into userdesign
authorEvan Prodromou <evan@controlyourself.ca>
Wed, 10 Jun 2009 04:52:02 +0000 (21:52 -0700)
committerEvan Prodromou <evan@controlyourself.ca>
Wed, 10 Jun 2009 04:52:02 +0000 (21:52 -0700)
45 files changed:
README
actions/api.php
actions/designsettings.php
actions/invite.php
actions/replies.php
actions/showfavorites.php
actions/shownotice.php
actions/twitapiaccount.php
actions/twitapidirect_messages.php
actions/twitapistatuses.php
actions/twitapiusers.php
actions/usergroups.php
classes/Design.php [new file with mode: 0644]
classes/Fave.php
classes/Memcached_DataObject.php
classes/Notice.php
classes/Notice_inbox.php
classes/Notice_tag.php
classes/Profile.php
classes/Reply.php
classes/User.php
classes/User_group.php
classes/laconica.ini [changed mode: 0644->0755]
db/laconica.sql
db/notice_source.sql
lib/action.php
lib/attachmentlist.php
lib/currentuserdesignaction.php [new file with mode: 0644]
lib/galleryaction.php
lib/mail.php
lib/mailbox.php
lib/noticelist.php
lib/omb.php
lib/ownerdesignaction.php [new file with mode: 0644]
lib/personal.php [deleted file]
lib/profileaction.php
lib/settingsaction.php
lib/stream.php [deleted file]
lib/twitterapi.php
lib/util.php
scripts/fixup_utf8.php
scripts/getvaliddaemons.php
scripts/inboxqueuehandler.php [deleted file]
scripts/memcachedqueuehandler.php [deleted file]
scripts/stopdaemons.sh

diff --git a/README b/README
index 9cbe84f0e51bfc68244314c8ac39dc5530802e18..2099f94d62f46f953bd13716ef9b3abc0d98cd00 100644 (file)
--- a/README
+++ b/README
@@ -2,8 +2,8 @@
 README
 ------
 
-Laconica 0.7.3 ("You Are The Everything")
-7 April 2009
+Laconica 0.7.4 ("Can't Get There From Here")
+29 May 2009
 
 This is the README file for Laconica, the Open Source microblogging
 platform. It includes installation instructions, descriptions of
@@ -71,29 +71,29 @@ for additional terms.
 New this version
 ================
 
-This is a minor bug-fix and feature release since version 0.7.2.1,
-released Mar 11 2009. Notable changes this version:
-
-- A plugin to allow a templating language for customization
-- A plugin for Piwik Analytics engine
-- A bookmarklet for posting a notice about a Web page you're reading
-- A welcome notice ('welcomebot') and default subscription for new users
-- Support for SSL for some or all pages on the site
-- Better handling of empty notice lists on many pages
-- Major improvements to the Twitter friend-sync offline processing
-- subscribers, subscriptions, groups are listed on the Personal page.
-- "Invite" link restored to main menu
-- Better memory handling in FOAF output
-- Fix for SUP support (FriendFeed)
-- Correct and intelligent redirect HTTP status codes
-- Fix DB collations for search and sort
-- Better H1s and Titles using user full names
-- Fixes to make the linkback plugin operational
-- Better indication that a notice is being published by Ajax (spinner)
-- Better and unified Atom output
-- Hiding "register" and "join now" messages when site is closed
-- ping, twitter and facebook queuehandlers working better
-- Updated RPM spec
+This is a minor bug-fix and feature release since version 0.7.3,
+released Apr 4 2009. Notable changes this version:
+
+- Improved handling of UTF-8 characters. The new code is *not* backwards
+  compatible by default; see "Upgrading" below for instructions on
+  converting existing databases to the correct character set.
+- Unroll joins for large queries. This greatly enhanced database
+  performance -- up to 50x for some queries -- at the expense of making
+  an extra DB hit for some queries.
+- Added an optional plugin to use WikiHashtags
+  (http://hashtags.wikia.com/) for the sidebar on hashtag pages.
+- Optimized Twitter friend synchronization.
+- Better error handling for Ajax posting of notices, including
+  HTTP errors and timeouts.
+- Experimental Comet plugin -- supports the cometd and the Bayeux
+  protocol. Using this plugin, you can show "real time" updates on the
+  public and tag pages. However, server configuration is complex.
+- If queues are enabled, update inboxes and memcached off-line. Speeds
+  up posting considerably.
+- Correctly shorten links posted through XMPP.
+- <link> elements for pagination, supported by some browsers like Opera.
+- Corrected date format in search API.
+- Made the public XRDS file work correctly.
 
 Prerequisites
 =============
@@ -197,9 +197,9 @@ especially if you've previously installed PHP/MySQL packages.
 1. Unpack the tarball you downloaded on your Web server. Usually a
    command like this will work:
 
-          tar zxf laconica-0.7.3.tar.gz
+          tar zxf laconica-0.7.4.tar.gz
 
-   ...which will make a laconica-0.7.3 subdirectory in your current
+   ...which will make a laconica-0.7.4 subdirectory in your current
    directory. (If you don't have shell access on your Web server, you
    may have to unpack the tarball on your local computer and FTP the
    files to the server.)
@@ -207,7 +207,7 @@ especially if you've previously installed PHP/MySQL packages.
 2. Move the tarball to a directory of your choosing in your Web root
    directory. Usually something like this will work:
 
-          mv laconica-0.7.3 /var/www/mublog
+          mv laconica-0.7.4 /var/www/mublog
 
    This will make your Laconica instance available in the mublog path of
    your server, like "http://example.net/mublog". "microblog" or
@@ -704,7 +704,7 @@ with this situation.
 If you've been using Laconica 0.6, 0.5 or lower, or if you've been
 tracking the "git" version of the software, you will probably want
 to upgrade and keep your existing data. There is no automated upgrade
-procedure in Laconica 0.7.3. Try these step-by-step instructions; read
+procedure in Laconica 0.7.4. Try these step-by-step instructions; read
 to the end first before trying them.
 
 0. Download Laconica and set up all the prerequisites as if you were
@@ -1239,7 +1239,7 @@ repository (see below), and you get a compilation error ("unexpected
 T_STRING") in the browser, check to see that you don't have any
 conflicts in your code.
 
-If you upgraded to Laconica 0.7.3 without reading the "Notice inboxes"
+If you upgraded to Laconica 0.7.4 without reading the "Notice inboxes"
 section above, and all your users' 'Personal' tabs are empty, read the
 "Notice inboxes" section above.
 
@@ -1330,6 +1330,8 @@ if anyone's been overlooked in error.
 * Ken Sedgwick
 * Brian Hendrickson
 * Tobias Diekershoff
+* Dan Moore
+* Fil
 
 Thanks also to the developers of our upstream library code and to the
 thousands of people who have tried out Identi.ca, installed Laconi.ca,
index b25ba99f30cfcf44b56f6a477856b9c1c82e675c..b8da852b536d469682f6fcee277894247696695e 100644 (file)
@@ -144,8 +144,8 @@ class ApiAction extends Action
         }
 
         if (in_array($fullname, $bareauth)) {
-            # bareauth: only needs auth if without an argument
-            if ($this->api_arg) {
+            # bareauth: only needs auth if without an argument or query param specifying user
+            if ($this->api_arg || $this->arg('id') || is_numeric($this->arg('user_id')) || $this->arg('screen_name')) {
                 return false;
             } else {
                 return true;
index 5774b85378d9e14025ef9a1c229083a48c13e200..66476e6777620db4ba679da33cf4e44f319b706e 100644 (file)
@@ -33,8 +33,6 @@ if (!defined('LACONICA')) {
 
 require_once INSTALLDIR.'/lib/accountsettingsaction.php';
 
-
-
 class DesignsettingsAction extends AccountSettingsAction
 {
     /**
@@ -100,28 +98,18 @@ class DesignsettingsAction extends AccountSettingsAction
         $this->element('legend', null, _('Change colours'));
         $this->elementStart('ul', 'form_data');
 
-        //This is a JSON object in the DB field. Here for testing. Remove later.
-        $userSwatch = '{"body":{"background-color":"#F0F2F5"},
-                        "#content":{"background-color":"#FFFFFF"},
-                        "#aside_primary":{"background-color":"#CEE1E9"},
-                        "html body":{"color":"#000000"},
-                        "a":{"color":"#002E6E"}}';
+        $design = $user->getDesign();
 
-        //Default theme swatch -- Where should this be stored?
-        $defaultSwatch = array('body' => array('background-color' => '#F0F2F5'),
-                               '#content' => array('background-color' => '#FFFFFF'),
-                               '#aside_primary' => array('background-color' => '#CEE1E9'),
-                               'html body' => array('color' => '#000000'),
-                               'a' => array('color' => '#002E6E'));
-
-        $userSwatch = ($userSwatch) ? json_decode($userSwatch, true) : $defaultSwatch;
+        if (empty($design)) {
+            $design = $this->defaultDesign();
+        }
 
-        $s = 0;
         $labelSwatch = array('Background',
                              'Content',
                              'Sidebar',
                              'Text',
                              'Links');
+
         foreach($userSwatch as $propertyvalue => $value) {
             $foo = array_values($value);
             $this->elementStart('li');
@@ -147,14 +135,14 @@ class DesignsettingsAction extends AccountSettingsAction
                                       'title' => _('Reset back to default')));
         $this->submit('save', _('Save'), 'submit form_action-secondary', 'save', _('Save design'));
 
-/*TODO: Check submitted form values: 
-json_encode(form values)
-if submitted Swatch == DefaultSwatch, don't store in DB.
-else store in BD
-*/
+        /*TODO: Check submitted form values:
+         json_encode(form values)
+         if submitted Swatch == DefaultSwatch, don't store in DB.
+         else store in BD
+         */
+
         $this->elementEnd('fieldset');
         $this->elementEnd('form');
-
     }
 
     /**
@@ -168,63 +156,10 @@ else store in BD
 
     function handlePost()
     {
-    /*
-        // CSRF protection
-
-        $token = $this->trimmed('token');
-        if (!$token || $token != common_session_token()) {
-            $this->showForm(_('There was a problem with your session token. '.
-                               'Try again, please.'));
-            return;
-        }
-
-        $user = common_current_user();
-        assert(!is_null($user)); // should already be checked
-
-        // FIXME: scrub input
-
-        $newpassword = $this->arg('newpassword');
-        $confirm     = $this->arg('confirm');
-
-        # Some validation
-
-        if (strlen($newpassword) < 6) {
-            $this->showForm(_('Password must be 6 or more characters.'));
-            return;
-        } else if (0 != strcmp($newpassword, $confirm)) {
-            $this->showForm(_('Passwords don\'t match.'));
-            return;
-        }
-
-        if ($user->password) {
-            $oldpassword = $this->arg('oldpassword');
-
-            if (!common_check_user($user->nickname, $oldpassword)) {
-                $this->showForm(_('Incorrect old password'));
-                return;
-            }
-        }
-
-        $original = clone($user);
-
-        $user->password = common_munge_password($newpassword, $user->id);
-
-        $val = $user->validate();
-        if ($val !== true) {
-            $this->showForm(_('Error saving user; invalid.'));
-            return;
-        }
-
-        if (!$user->update($original)) {
-            $this->serverError(_('Can\'t save new password.'));
-            return;
-        }
-
-        $this->showForm(_('Password saved.'), true);
-        */
+        // TODO: implement this
+        return;
     }
 
-
     /**
      * Add the Farbtastic stylesheet
      *
index 7e52cdbcc6af6d20b88be1944f038a306d999c0c..c793f582491035d752ae1b696f4a2bed8764ebd5 100644 (file)
@@ -19,7 +19,7 @@
 
 if (!defined('LACONICA')) { exit(1); }
 
-class InviteAction extends Action
+class InviteAction extends CurrentUserDesignAction
 {
     var $mode = null;
     var $error = null;
index dfb520d64974ae5c104e8f9e8c5b7caeab400e09..5d84f6428cfac4aec8bf75e807107801f7974118 100644 (file)
@@ -45,9 +45,8 @@ require_once INSTALLDIR.'/lib/feedlist.php';
  * @link     http://laconi.ca/
  */
 
-class RepliesAction extends Action
+class RepliesAction extends OwnerDesignAction
 {
-    var $user = null;
     var $page = null;
 
     /**
index eed62a2ab308f79dfdb318ee61cbebf338367d72..839f083dd49444c54c32b15f5f444534b11333a5 100644 (file)
@@ -45,10 +45,8 @@ require_once INSTALLDIR.'/lib/feedlist.php';
  * @link     http://laconi.ca/
  */
 
-class ShowfavoritesAction extends Action
+class ShowfavoritesAction extends OwnerDesignAction
 {
-    /** User we're getting the faves of */
-    var $user = null;
     /** Page of the faves we're on */
     var $page = null;
 
@@ -161,7 +159,6 @@ class ShowfavoritesAction extends Action
                                      $this->page, 'showfavorites', array('nickname' => $this->user->nickname));
     }
 
-
     /**
      * show the personal group nav
      *
index 34c8a8e9464c68e21c47a45ca52467b6b8b3b3d5..b0d973a991cad8e61cfa2d8ce632013e89f3201b 100644 (file)
@@ -122,7 +122,7 @@ class ShownoticeAction extends Action
 
     function lastModified()
     {
-        return max(strtotime($this->notice->created),
+        return max(strtotime($this->notice->modified),
                    strtotime($this->profile->modified),
                    ($this->avatar) ? strtotime($this->avatar->modified) : 0);
     }
index 68a18cb57b43c87d7a9e4e6090f5b5b1df019a9a..8b956f897a397606434d2a2d1040a71c31b937c8 100644 (file)
@@ -98,9 +98,31 @@ class TwitapiaccountAction extends TwitterapiAction
         $this->serverError(_('API method under construction.'), $code=501);
     }
 
+    // We don't have a rate limit, but some clients check this method.
+    // It always returns the same thing: 100 hit left.
     function rate_limit_status($args, $apidata)
     {
         parent::handle($args);
-        $this->serverError(_('API method under construction.'), $code=501);
+
+        $type = $apidata['content-type'];
+        $this->init_document($type);
+
+        if ($apidata['content-type'] == 'xml') {
+            $this->elementStart('hash');
+            $this->element('remaining-hits', array('type' => 'integer'), 100);
+            $this->element('hourly-limit', array('type' => 'integer'), 100);
+            $this->element('reset-time', array('type' => 'datetime'), null);
+            $this->element('reset_time_in_seconds', array('type' => 'integer'), 0);
+            $this->elementEnd('hash');
+        } elseif ($apidata['content-type'] == 'json') {
+
+            $out = array('reset_time_in_seconds' => 0,
+                         'remaining_hits' => 100,
+                         'hourly_limit' => 100,
+                         'reset_time' => '');
+            print json_encode($out);
+        }
+
+        $this->end_document($type);
     }
 }
index 7101db8df585354462150a1eae3e98364ca2b96a..d2dbdb619b180044a0388cd592382944eec4018a 100644 (file)
@@ -43,7 +43,7 @@ class Twitapidirect_messagesAction extends TwitterapiAction
         $count = $this->arg('count');
         $since = $this->arg('since');
         $since_id = $this->arg('since_id');
-        $before_id = $this->arg('before_id');
+        $max_id = $this->arg('max_id');
 
         $page = $this->arg('page');
 
@@ -74,8 +74,8 @@ class Twitapidirect_messagesAction extends TwitterapiAction
             $link = $server . $user->nickname . '/outbox';
         }
 
-        if ($before_id) {
-            $message->whereAdd("id < $before_id");
+        if ($max_id) {
+            $message->whereAdd("id <= $max_id");
         }
 
         if ($since_id) {
index 3abeba36727d2f8ae4ee6708f8d3aab95d00a64d..1fbde6639f3bf32b4be7131e8a28554e1acdcb5a 100644 (file)
@@ -45,22 +45,21 @@ class TwitapistatusesAction extends TwitterapiAction
 
         $page = $this->arg('page');
         $since_id = $this->arg('since_id');
-        $before_id = $this->arg('before_id');
+        $max_id = $this->arg('max_id');
 
-        // NOTE: page, since_id, and before_id are extensions to Twitter API -- TB
         if (!$page) {
             $page = 1;
         }
         if (!$since_id) {
             $since_id = 0;
         }
-        if (!$before_id) {
-            $before_id = 0;
+        if (!$max_id) {
+            $max_id = 0;
         }
 
         $since = strtotime($this->arg('since'));
 
-        $notice = Notice::publicStream((($page-1)*$MAX_PUBSTATUSES), $MAX_PUBSTATUSES, $since_id, $before_id, $since);
+        $notice = Notice::publicStream((($page-1)*$MAX_PUBSTATUSES), $MAX_PUBSTATUSES, $since_id, $max_id, $since);
 
         if ($notice) {
 
@@ -97,7 +96,7 @@ class TwitapistatusesAction extends TwitterapiAction
         $since_id = $this->arg('since_id');
         $count = $this->arg('count');
         $page = $this->arg('page');
-        $before_id = $this->arg('before_id');
+        $max_id = $this->arg('max_id');
 
         if (!$page) {
             $page = 1;
@@ -111,9 +110,8 @@ class TwitapistatusesAction extends TwitterapiAction
             $since_id = 0;
         }
 
-        // NOTE: before_id is an extension to Twitter API -- TB
-        if (!$before_id) {
-            $before_id = 0;
+        if (!$max_id) {
+            $max_id = 0;
         }
 
         $since = strtotime($this->arg('since'));
@@ -133,7 +131,7 @@ class TwitapistatusesAction extends TwitterapiAction
         $link = common_local_url('all', array('nickname' => $user->nickname));
         $subtitle = sprintf(_('Updates from %1$s and friends on %2$s!'), $user->nickname, $sitename);
 
-        $notice = $user->noticesWithFriends(($page-1)*20, $count, $since_id, $before_id, $since);
+        $notice = $user->noticesWithFriends(($page-1)*20, $count, $since_id, $max_id, $since);
 
         switch($apidata['content-type']) {
          case 'xml':
@@ -184,7 +182,7 @@ class TwitapistatusesAction extends TwitterapiAction
         $since = $this->arg('since');
         $since_id = $this->arg('since_id');
         $page = $this->arg('page');
-        $before_id = $this->arg('before_id');
+        $max_id = $this->arg('max_id');
 
         if (!$page) {
             $page = 1;
@@ -198,9 +196,8 @@ class TwitapistatusesAction extends TwitterapiAction
             $since_id = 0;
         }
 
-        // NOTE: before_id is an extensions to Twitter API -- TB
-        if (!$before_id) {
-            $before_id = 0;
+        if (!$max_id) {
+            $max_id = 0;
         }
 
         $since = strtotime($this->arg('since'));
@@ -220,7 +217,7 @@ class TwitapistatusesAction extends TwitterapiAction
 
         # XXX: since
 
-        $notice = $user->getNotices((($page-1)*20), $count, $since_id, $before_id, $since);
+        $notice = $user->getNotices((($page-1)*20), $count, $since_id, $max_id, $since);
 
         switch($apidata['content-type']) {
          case 'xml':
@@ -353,7 +350,7 @@ class TwitapistatusesAction extends TwitterapiAction
         $count = $this->arg('count');
         $page = $this->arg('page');
         $since_id = $this->arg('since_id');
-        $before_id = $this->arg('before_id');
+        $max_id = $this->arg('max_id');
 
         $user = $this->get_user($apidata['api_arg'], $apidata);
         $this->auth_user = $apidata['user'];
@@ -380,15 +377,14 @@ class TwitapistatusesAction extends TwitterapiAction
             $since_id = 0;
         }
 
-        // NOTE: before_id is an extension to Twitter API -- TB
-        if (!$before_id) {
-            $before_id = 0;
+        if (!$max_id) {
+            $max_id = 0;
         }
 
         $since = strtotime($this->arg('since'));
 
         $notice = $user->getReplies((($page-1)*20),
-            $count, $since_id, $before_id, $since);
+            $count, $since_id, $max_id, $since);
         $notices = array();
 
         while ($notice->fetch()) {
index 1542cfb33e3d064130093272e014ac4fa6262d81..b90bbfa985c9a6a381a5397647b54518225e6768 100644 (file)
@@ -25,110 +25,61 @@ class TwitapiusersAction extends TwitterapiAction
 {
 
     function show($args, $apidata)
-    {        
+    {
         parent::handle($args);
 
-        if (!in_array($apidata['content-type'], array('xml', 'json'))) {            
+        if (!in_array($apidata['content-type'], array('xml', 'json'))) {
             $this->clientError(_('API method not found!'), $code = 404);
             return;
         }
-                
-               $user = null;
-               $email = $this->arg('email');
-               $user_id = $this->arg('user_id');
-
-               if ($email) {
-                       $user = User::staticGet('email', $email);
-               } elseif ($user_id) {
-                       $user = $this->get_user($user_id);  
-               } elseif (isset($apidata['api_arg'])) {
-                       $user = $this->get_user($apidata['api_arg']);
-           } elseif (isset($apidata['user'])) {
-               $user = $apidata['user'];
-           }
-       
-               if (!$user) {               
-                       // XXX: Twitter returns a random(?) user instead of throwing and err! -- Zach
-                       $this->client_error(_('Not found.'), 404, $apidata['content-type']);
-                       return;
-               }
-
-               $profile = $user->getProfile();
-
-               if (!$profile) {
-                       common_server_error(_('User has no profile.'));
-                       return;
-               }
-
-               $twitter_user = $this->twitter_user_array($profile, true);
-
-               // Add in extended user fields offered up by this method
-               $twitter_user['created_at'] = $this->date_twitter($profile->created);
-
-               $subbed = DB_DataObject::factory('subscription');
-               $subbed->subscriber = $profile->id;
-               $subbed_count = (int) $subbed->count() - 1;
-
-               $notices = DB_DataObject::factory('notice');
-               $notices->profile_id = $profile->id;
-               $notice_count = (int) $notices->count();
-
-               $twitter_user['friends_count'] = (is_int($subbed_count)) ? $subbed_count : 0;
-               $twitter_user['statuses_count'] = (is_int($notice_count)) ? $notice_count : 0;
-
-               // Other fields Twitter sends...
-               $twitter_user['profile_background_color'] = '';
-               $twitter_user['profile_background_image_url'] = '';
-               $twitter_user['profile_text_color'] = '';
-               $twitter_user['profile_link_color'] = '';
-               $twitter_user['profile_sidebar_fill_color'] = '';
-               $twitter_user['profile_sidebar_border_color'] = '';
-               $twitter_user['profile_background_tile'] = false;
-
-               $faves = DB_DataObject::factory('fave');
-               $faves->user_id = $user->id;
-               $faves_count = (int) $faves->count();
-               $twitter_user['favourites_count'] = $faves_count;
-
-               $timezone = 'UTC';
-
-               if ($user->timezone) {
-                       $timezone = $user->timezone;
-               }
-
-               $t = new DateTime;
-               $t->setTimezone(new DateTimeZone($timezone));
-               $twitter_user['utc_offset'] = $t->format('Z');
-               $twitter_user['time_zone'] = $timezone;
-
-               if (isset($apidata['user'])) {
-
-                       $twitter_user['following'] = $apidata['user']->isSubscribed($profile);
-            
-                       // Notifications on?
-                       $sub = Subscription::pkeyGet(array('subscriber' =>
-                               $apidata['user']->id, 'subscribed' => $profile->id));
-            
-                       if ($sub) {
-                               $twitter_user['notifications'] = ($sub->jabber || $sub->sms);
-                       }
-               }
-        
-               if ($apidata['content-type'] == 'xml') {
-                       $this->init_document('xml');
-                       $this->show_twitter_xml_user($twitter_user);
-                       $this->end_document('xml');
-               } elseif ($apidata['content-type'] == 'json') {
-                       $this->init_document('json');
-                       $this->show_json_objects($twitter_user);
-                       $this->end_document('json');
-               } else {
-                   
-                   // This is in case 'show' was called via /account/verify_credentials
-                   // without a format (xml or json).
+
+        $user = null;
+        $email = $this->arg('email');
+        $user_id = $this->arg('user_id');
+
+        // XXX: email field deprecated in Twitter's API
+
+        // XXX: Also: need to add screen_name param
+
+        if ($email) {
+            $user = User::staticGet('email', $email);
+        } elseif ($user_id) {
+            $user = $this->get_user($user_id);
+        } elseif (isset($apidata['api_arg'])) {
+            $user = $this->get_user($apidata['api_arg']);
+        } elseif (isset($apidata['user'])) {
+            $user = $apidata['user'];
+        }
+
+        if (!$user) {
+            $this->client_error(_('Not found.'), 404, $apidata['content-type']);
+            return;
+        }
+
+        $profile = $user->getProfile();
+
+        if (!$profile) {
+            common_server_error(_('User has no profile.'));
+            return;
+        }
+
+        $twitter_user = $this->twitter_user_array($profile, true);
+
+        if ($apidata['content-type'] == 'xml') {
+            $this->init_document('xml');
+            $this->show_twitter_xml_user($twitter_user);
+            $this->end_document('xml');
+        } elseif ($apidata['content-type'] == 'json') {
+            $this->init_document('json');
+            $this->show_json_objects($twitter_user);
+            $this->end_document('json');
+        } else {
+
+            // This is in case 'show' was called via /account/verify_credentials
+            // without a format (xml or json).
             header('Content-Type: text/html; charset=utf-8');
             print 'Authorized';
         }
 
-       }
+    }
 }
index e3088dcbd83d6e26dc51110ecd633f626e78becc..7ead6e6e49172e4941035e0a4c5d433c210f9026 100644 (file)
@@ -46,9 +46,8 @@ require_once INSTALLDIR.'/lib/grouplist.php';
  * @link     http://laconi.ca/
  */
 
-class UsergroupsAction extends Action
+class UsergroupsAction extends OwnerDesignAction
 {
-    var $user = null;
     var $page = null;
     var $profile = null;
 
diff --git a/classes/Design.php b/classes/Design.php
new file mode 100644 (file)
index 0000000..bb1e917
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+/*
+ * Laconica - the distributed open-source microblogging tool
+ * Copyright (C) 2009, Control Yourself, 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('LACONICA')) { exit(1); }
+
+/**
+ * Table Definition for design
+ */
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Design extends Memcached_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'design';                          // table name
+    public $id;                              // int(4)  primary_key not_null
+    public $backgroundcolor;                 // int(4)
+    public $contentcolor;                    // int(4)
+    public $sidebarcolor;                    // int(4)
+    public $textcolor;                       // int(4)
+    public $linkcolor;                       // int(4)
+    public $backgroundimage;                 // varchar(255)
+
+    /* Static get */
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Design',$k,$v); }
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+
+    function showCSS($out)
+    {
+        $out->element('stylesheet', array('type' => 'text/css'),
+                      'body { background-color: #' . dechex($this->backgroundcolor) . '} '."\n".
+                      '#content { background-color #' . dechex($this->contentcolor) . '} '."\n".
+                      '#aside_primary { background-color #'. dechex($this->sidebarcolor) .'} '."\n".
+                      'html body { color: #'. dechex($this->textcolor) .'} '."\n".
+                      'a { color: #' . dechex($this->linkcolor) . '} '."\n");
+    }
+}
index 915b4572ffed5dd11e533a664c55e4d687d725b3..572334ce4f8cf17e8811cf0eab51cd77ed922549 100644 (file)
@@ -46,7 +46,7 @@ class Fave extends Memcached_DataObject
         return $ids;
     }
 
-    function _streamDirect($user_id, $offset, $limit, $since_id, $before_id, $since)
+    function _streamDirect($user_id, $offset, $limit, $since_id, $max_id, $since)
     {
         $fav = new Fave();
 
@@ -59,8 +59,8 @@ class Fave extends Memcached_DataObject
             $fav->whereAdd('notice_id > ' . $since_id);
         }
 
-        if ($before_id != 0) {
-            $fav->whereAdd('notice_id < ' . $before_id);
+        if ($max_id != 0) {
+            $fav->whereAdd('notice_id <= ' . $max_id);
         }
 
         if (!is_null($since)) {
index 52ad4100fcec32cc3a823172b269556fdab32a06..33ac70dd045643a5a57ff87e58d383a0bc52bd2b 100644 (file)
@@ -239,8 +239,14 @@ class Memcached_DataObject extends DB_DataObject
         $result = parent::_connect();
         if (!$exists) {
             $DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5];
-            if (common_config('db', 'utf8')) {
-                $DB->query('SET NAMES "utf8"');
+            if (common_config('db', 'type') == 'mysql' &&
+                common_config('db', 'utf8')) {
+                $conn = $DB->connection;
+                if ($DB instanceof DB_mysqli) {
+                    mysqli_set_charset($conn, 'utf8');
+                } else if ($DB instanceof DB_mysql) {
+                    mysql_set_charset('utf8', $conn);
+                }
             }
         }
         return $result;
index 0b1017e126d95e0da7dd79c102a19e2474ed333c..895b5d2c71c8a3720587ec57a16abb62f0adf7cc 100644 (file)
@@ -124,6 +124,8 @@ class Notice extends Memcached_DataObject
 
         $profile = Profile::staticGet($profile_id);
 
+        $final =  common_shorten_links($content);
+
         if (!$profile) {
             common_log(LOG_ERR, 'Problem saving notice. Unknown user.');
             return _('Problem saving notice. Unknown user.');
@@ -134,7 +136,7 @@ class Notice extends Memcached_DataObject
             return _('Too many notices too fast; take a breather and post again in a few minutes.');
         }
 
-        if (common_config('site', 'dupelimit') > 0 && !Notice::checkDupes($profile_id, $content)) {
+        if (common_config('site', 'dupelimit') > 0 && !Notice::checkDupes($profile_id, $final)) {
             common_log(LOG_WARNING, 'Dupe posting by profile #' . $profile_id . '; throttled.');
                        return _('Too many duplicate messages too quickly; take a breather and post again in a few minutes.');
         }
@@ -165,8 +167,8 @@ class Notice extends Memcached_DataObject
 
                $notice->reply_to = $reply_to;
                $notice->created = common_sql_now();
-               $notice->content = $content;
-               $notice->rendered = common_render_content($content, $notice);
+               $notice->content = $final;
+               $notice->rendered = common_render_content($final, $notice);
                $notice->source = $source;
                $notice->uri = $uri;
 
@@ -202,13 +204,9 @@ class Notice extends Memcached_DataObject
 
             $notice->saveReplies();
             $notice->saveTags();
-            $notice->saveGroups();
 
-            if (common_config('queue', 'enabled')) {
-                $notice->addToAuthorInbox();
-            } else {
-                $notice->addToInboxes();
-            }
+            $notice->addToInboxes();
+            $notice->saveGroups();
 
             $notice->query('COMMIT');
 
@@ -218,13 +216,7 @@ class Notice extends Memcached_DataObject
         # Clear the cache for subscribed users, so they'll update at next request
         # XXX: someone clever could prepend instead of clearing the cache
 
-        if (common_config('memcached', 'enabled')) {
-            if (common_config('queue', 'enabled')) {
-                $notice->blowAuthorCaches();
-            } else {
-                $notice->blowCaches();
-            }
-        }
+        $notice->blowCaches();
 
         return $notice;
     }
@@ -309,17 +301,6 @@ class Notice extends Memcached_DataObject
         $this->blowGroupCache($blowLast);
     }
 
-    function blowAuthorCaches($blowLast=false)
-    {
-        // Clear the user's cache
-        $cache = common_memcache();
-        if (!empty($cache)) {
-            $cache->delete(common_cache_key('notice_inbox:by_user:'.$this->profile_id));
-        }
-        $this->blowNoticeCache($blowLast);
-        $this->blowPublicCache($blowLast);
-    }
-
     function blowGroupCache($blowLast=false)
     {
         $cache = common_memcache();
@@ -455,22 +436,22 @@ class Notice extends Memcached_DataObject
     # XXX: too many args; we need to move to named params or even a separate
     # class for notice streams
 
-    static function getStream($qry, $cachekey, $offset=0, $limit=20, $since_id=0, $before_id=0, $order=null, $since=null) {
+    static function getStream($qry, $cachekey, $offset=0, $limit=20, $since_id=0, $max_id=0, $order=null, $since=null) {
 
         if (common_config('memcached', 'enabled')) {
 
-            # Skip the cache if this is a since, since_id or before_id qry
-            if ($since_id > 0 || $before_id > 0 || $since) {
-                return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since);
+            # Skip the cache if this is a since, since_id or max_id qry
+            if ($since_id > 0 || $max_id > 0 || $since) {
+                return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $max_id, $order, $since);
             } else {
                 return Notice::getCachedStream($qry, $cachekey, $offset, $limit, $order);
             }
         }
 
-        return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since);
+        return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $max_id, $order, $since);
     }
 
-    static function getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since) {
+    static function getStreamDirect($qry, $offset, $limit, $since_id, $max_id, $order, $since) {
 
         $needAnd = false;
         $needWhere = true;
@@ -492,7 +473,7 @@ class Notice extends Memcached_DataObject
             $qry .= ' notice.id > ' . $since_id;
         }
 
-        if ($before_id > 0) {
+        if ($max_id > 0) {
 
             if ($needWhere) {
                 $qry .= ' WHERE ';
@@ -501,7 +482,7 @@ class Notice extends Memcached_DataObject
                 $qry .= ' AND ';
             }
 
-            $qry .= ' notice.id < ' . $before_id;
+            $qry .= ' notice.id <= ' . $max_id;
         }
 
         if ($since) {
@@ -659,17 +640,17 @@ class Notice extends Memcached_DataObject
         }
     }
 
-    function publicStream($offset=0, $limit=20, $since_id=0, $before_id=0, $since=null)
+    function publicStream($offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
     {
         $ids = Notice::stream(array('Notice', '_publicStreamDirect'),
                               array(),
                               'public',
-                              $offset, $limit, $since_id, $before_id, $since);
+                              $offset, $limit, $since_id, $max_id, $since);
 
         return Notice::getStreamByIds($ids);
     }
 
-    function _publicStreamDirect($offset=0, $limit=20, $since_id=0, $before_id=0, $since=null)
+    function _publicStreamDirect($offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
     {
         $notice = new Notice();
 
@@ -693,8 +674,8 @@ class Notice extends Memcached_DataObject
             $notice->whereAdd('id > ' . $since_id);
         }
 
-        if ($before_id != 0) {
-            $notice->whereAdd('id < ' . $before_id);
+        if ($max_id != 0) {
+            $notice->whereAdd('id <= ' . $max_id);
         }
 
         if (!is_null($since)) {
@@ -738,33 +719,6 @@ class Notice extends Memcached_DataObject
         return;
     }
 
-    function addToAuthorInbox()
-    {
-        $enabled = common_config('inboxes', 'enabled');
-
-        if ($enabled === true || $enabled === 'transitional') {
-            $user = User::staticGet('id', $this->profile_id);
-            if (empty($user)) {
-                return;
-            }
-            $inbox = new Notice_inbox();
-            $UT = common_config('db','type')=='pgsql'?'"user"':'user';
-            $qry = 'INSERT INTO notice_inbox (user_id, notice_id, created) ' .
-              "SELECT $UT.id, " . $this->id . ", '" . $this->created . "' " .
-              "FROM $UT " .
-              "WHERE $UT.id = " . $this->profile_id . ' ' .
-              'AND NOT EXISTS (SELECT user_id, notice_id ' .
-              'FROM notice_inbox ' .
-              "WHERE user_id = " . $this->profile_id . ' '.
-              'AND notice_id = ' . $this->id . ' )';
-            if ($enabled === 'transitional') {
-                $qry .= " AND $UT.inboxed = 1";
-            }
-            $inbox->query($qry);
-        }
-        return;
-    }
-
     function saveGroups()
     {
         $enabled = common_config('inboxes', 'enabled');
@@ -1036,15 +990,15 @@ class Notice extends Memcached_DataObject
         }
     }
 
-    function stream($fn, $args, $cachekey, $offset=0, $limit=20, $since_id=0, $before_id=0, $since=null, $tag=null)
+    function stream($fn, $args, $cachekey, $offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
     {
         $cache = common_memcache();
 
         if (empty($cache) ||
-            $since_id != 0 || $before_id != 0 || !is_null($since) ||
+            $since_id != 0 || $max_id != 0 || (!is_null($since) && $since > 0) ||
             ($offset + $limit) > NOTICE_CACHE_WINDOW) {
             return call_user_func_array($fn, array_merge($args, array($offset, $limit, $since_id,
-                                                                      $before_id, $since, $tag)));
+                                                                      $max_id, $since)));
         }
 
         $idkey = common_cache_key($cachekey);
index dec14b0d18897cf0fcb40ddf8e44305929ca5103..8a27e174785101b9b52b6e3bf9211b3867e00ab2 100644 (file)
@@ -43,15 +43,15 @@ class Notice_inbox extends Memcached_DataObject
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
 
-    function stream($user_id, $offset, $limit, $since_id, $before_id, $since)
+    function stream($user_id, $offset, $limit, $since_id, $max_id, $since)
     {
         return Notice::stream(array('Notice_inbox', '_streamDirect'),
                               array($user_id),
                               'notice_inbox:by_user:'.$user_id,
-                              $offset, $limit, $since_id, $before_id, $since);
+                              $offset, $limit, $since_id, $max_id, $since);
     }
 
-    function _streamDirect($user_id, $offset, $limit, $since_id, $before_id, $since)
+    function _streamDirect($user_id, $offset, $limit, $since_id, $max_id, $since)
     {
         $inbox = new Notice_inbox();
 
@@ -61,8 +61,8 @@ class Notice_inbox extends Memcached_DataObject
             $inbox->whereAdd('notice_id > ' . $since_id);
         }
 
-        if ($before_id != 0) {
-            $inbox->whereAdd('notice_id < ' . $before_id);
+        if ($max_id != 0) {
+            $inbox->whereAdd('notice_id <= ' . $max_id);
         }
 
         if (!is_null($since)) {
index e5b7722430b99be54a6fe2927b13275f86f44df0..758a6659473ec491bd625758ac697090e74aa9b8 100644 (file)
@@ -46,7 +46,7 @@ class Notice_tag extends Memcached_DataObject
         return Notice::getStreamByIds($ids);
     }
 
-    function _streamDirect($tag, $offset, $limit, $since_id, $before_id, $since)
+    function _streamDirect($tag, $offset, $limit, $since_id, $max_id, $since)
     {
         $nt = new Notice_tag();
 
@@ -59,8 +59,8 @@ class Notice_tag extends Memcached_DataObject
             $nt->whereAdd('notice_id > ' . $since_id);
         }
 
-        if ($before_id != 0) {
-            $nt->whereAdd('notice_id < ' . $before_id);
+        if ($max_id != 0) {
+            $nt->whereAdd('notice_id < ' . $max_id);
         }
 
         if (!is_null($since)) {
index afc0ea4f74557b29824ef266a54be6fd59d81202..4a459b9740e4055b5a62c9349ba689b9dc9598fd 100644 (file)
@@ -170,7 +170,7 @@ class Profile extends Memcached_DataObject
         $ids = Notice::stream(array($this, '_streamDirect'),
                               array(),
                               'profile:notice_ids:' . $this->id,
-                              $offset, $limit, $since_id, $before_id, $since);
+                              $offset, $limit, $since_id, $max_id, $since);
 
         return Notice::getStreamByIds($ids);
     }
@@ -225,8 +225,8 @@ class Profile extends Memcached_DataObject
             $notice->whereAdd('id > ' . $since_id);
         }
 
-        if ($before_id != 0) {
-            $notice->whereAdd('id < ' . $before_id);
+        if ($max_id != 0) {
+            $notice->whereAdd('id <= ' . $max_id);
         }
 
         if (!is_null($since)) {
index 4439053b444934f05fb69fa9b4d5b396700f532e..49b1e05e517e1bd34bb7645cead3148990f27c0b 100644 (file)
@@ -22,16 +22,16 @@ class Reply extends Memcached_DataObject
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
 
-    function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
+    function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0, $since=null)
     {
         $ids = Notice::stream(array('Reply', '_streamDirect'),
                               array($user_id),
                               'reply:stream:' . $user_id,
-                              $offset, $limit, $since_id, $before_id, $since);
+                              $offset, $limit, $since_id, $max_id, $since);
         return $ids;
     }
 
-    function _streamDirect($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
+    function _streamDirect($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0, $since=null)
     {
         $reply = new Reply();
         $reply->profile_id = $user_id;
@@ -40,8 +40,8 @@ class Reply extends Memcached_DataObject
             $reply->whereAdd('notice_id > ' . $since_id);
         }
 
-        if ($before_id != 0) {
-            $reply->whereAdd('notice_id < ' . $before_id);
+        if ($max_id != 0) {
+            $reply->whereAdd('notice_id < ' . $max_id);
         }
 
         if (!is_null($since)) {
index ea8ba40817ac74c3901c9f8f519deb677f616339..c7eede94e19e81c4f6231744f23c085e104f37e7 100644 (file)
@@ -62,14 +62,13 @@ class User extends Memcached_DataObject
     public $autosubscribe;                   // tinyint(1)
     public $urlshorteningservice;            // varchar(50)   default_ur1.ca
     public $inboxed;                         // tinyint(1)
+    public $design_id;                       // int(4)
+    public $viewdesigns;                     // tinyint(1)   default_1
     public $created;                         // datetime()   not_null
     public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
     /* Static get */
-    function staticGet($k,$v=NULL)
-    {
-        return Memcached_DataObject::staticGet('User',$k,$v);
-    }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('User',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
@@ -403,7 +402,6 @@ class User extends Memcached_DataObject
     function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
     {
         $ids = Reply::stream($this->id, $offset, $limit, $since_id, $before_id, $since);
-        common_debug("Ids = " . implode(',', $ids));
         return Notice::getStreamByIds($ids);
     }
 
@@ -685,4 +683,9 @@ class User extends Memcached_DataObject
 
         return ($cnt > 0);
     }
+
+    function getDesign()
+    {
+        return Design::staticGet('id', $this->design_id);
+    }
 }
index 7cc31e7026d0aeb693d481c1a7c820b3807f17dc..a135015baca3a1f2d67601ebb9ae6f03d4c5dd0f 100644 (file)
@@ -58,7 +58,7 @@ class User_group extends Memcached_DataObject
         return Notice::getStreamByIds($ids);
     }
 
-    function _streamDirect($offset, $limit, $since_id, $before_id, $since)
+    function _streamDirect($offset, $limit, $since_id, $max_id, $since)
     {
         $inbox = new Group_inbox();
 
@@ -71,8 +71,8 @@ class User_group extends Memcached_DataObject
             $inbox->whereAdd('notice_id > ' . $since_id);
         }
 
-        if ($before_id != 0) {
-            $inbox->whereAdd('notice_id < ' . $before_id);
+        if ($max_id != 0) {
+            $inbox->whereAdd('notice_id <= ' . $max_id);
         }
 
         if (!is_null($since)) {
old mode 100644 (file)
new mode 100755 (executable)
index 92bbb35..07aa016
@@ -1,3 +1,4 @@
+
 [avatar]
 profile_id = 129
 original = 17
@@ -37,6 +38,18 @@ modified = 384
 [consumer__keys]
 consumer_key = K
 
+[design]
+id = 129
+backgroundcolor = 1
+contentcolor = 1
+sidebarcolor = 1
+textcolor = 1
+linkcolor = 1
+backgroundimage = 2
+
+[design__keys]
+id = N
+
 [fave]
 notice_id = 129
 user_id = 129
@@ -411,6 +424,8 @@ uri = 2
 autosubscribe = 17
 urlshorteningservice = 2
 inboxed = 17
+design_id = 1
+viewdesigns = 17
 created = 142
 modified = 384
 
index a11e31692548449c4cabfd0629b589961ea577d4..2ca084e48df19b368c23596cb82fd1602732226e 100644 (file)
@@ -41,6 +41,7 @@ create table sms_carrier (
 /* local users */
 
 create table user (
+
     id integer primary key comment 'foreign key to profile table' references profile (id),
     nickname varchar(64) unique key comment 'nickname or username, duped in profile',
     password varchar(255) comment 'salted password, can be null for OpenID users',
@@ -69,6 +70,9 @@ create table user (
     autosubscribe tinyint default 0 comment 'automatically subscribe to users who subscribe to us',
     urlshorteningservice varchar(50) default 'ur1.ca' comment 'service to use for auto-shortening URLs',
     inboxed tinyint default 0 comment 'has an inbox been created for this user?',
+    design_id integer comment 'id of a design' references design(id),
+    viewdesigns tinyint default 1 comment 'whether to view user-provided designs',
+
     created datetime not null comment 'date this record was created',
     modified timestamp comment 'date this record was modified',
 
@@ -482,3 +486,13 @@ create table file_to_post (
 
     unique(file_id, post_id)
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table design (
+    id integer primary key auto_increment comment 'design ID',
+    backgroundcolor integer comment 'main background color',
+    contentcolor integer comment 'content area background color',
+    sidebarcolor integer comment 'sidebar background color',
+    textcolor integer comment 'text color',
+    linkcolor integer comment 'link color',
+    backgroundimage varchar(255) comment 'background image, if any'
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
index d5a280b82cedd18f9ba0df4107b7277c18a4394c..983ea915023ded6aeaae99a9049cfde4ec29b05e 100644 (file)
@@ -2,7 +2,9 @@ INSERT INTO notice_source
     (code, name, url, created)
 VALUES
     ('adium', 'Adium', 'http://www.adiumx.com/', now()),
+    ('Afficheur', 'Afficheur', 'http://afficheur.sourceforge.jp/', now()),
     ('AgentSolo.com','AgentSolo.com','http://www.agentsolo.com/', now()),
+    ('anyio', 'Any.IO', 'http://any.io/', now()),
     ('betwittered','BeTwittered','http://www.32hours.com/betwitteredinfo/', now()),
     ('bti','bti','http://gregkh.github.com/bti/', now()),
     ('cliqset', 'Cliqset', 'http://www.cliqset.com/', now()),
@@ -11,6 +13,7 @@ VALUES
     ('eventbox','EventBox','http://thecosmicmachine.com/eventbox/ ', now()),
     ('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()),
     ('gravity', 'Gravity', 'http://mobileways.de/gravity', now()),
     ('Gwibber','Gwibber','http://launchpad.net/gwibber', now()),
     ('HelloTxt','HelloTxt','http://hellotxt.com/', now()),
index 6a69d26518170eed18f04a7a76ebdb15b4bbd9ce..55fb7c0896301dbab2c91842813a0eb6dab6150a 100644 (file)
@@ -224,6 +224,16 @@ class Action extends HTMLOutputter // lawsuit
                                'href="'.theme_path('css/ie.css', null).'?version='.LACONICA_VERSION.'" /><![endif]');
                 Event::handle('EndShowUAStyles', array($this));
             }
+            if (Event::handle('StartShowDesign', array($this))) {
+                $design = $this->getDesign();
+                if (!empty($design)) {
+                    $cur = common_current_user();
+                    if (empty($cur) || $cur->viewdesigns) {
+                        $design->showCSS($this);
+                    }
+                }
+                Event::handle('EndShowDesign', array($this, $design));
+            }
             Event::handle('EndShowStyles', array($this));
         }
     }
@@ -248,7 +258,6 @@ class Action extends HTMLOutputter // lawsuit
                                                'src' => common_path('js/jquery.joverlay.min.js')),
                                ' ');
 
-
                 Event::handle('EndShowJQueryScripts', array($this));
             }
             if (Event::handle('StartShowLaconicaScripts', array($this))) {
@@ -1095,4 +1104,19 @@ class Action extends HTMLOutputter // lawsuit
                                          'title' => _('Previous')));
         }
     }
+
+    /**
+     * A design for this action
+     *
+     * A design (colors and background) for the current page. May be
+     * the user's design, or a group's design, or a site design.
+     *
+     * @return Design a design object to use
+     */
+
+    function getDesign()
+    {
+        // XXX: return site design by default
+        return null;
+    }
 }
index 45e4fa319687761ee9dc31d3b617b7cfc18a2e5e..e1726df2876e8f24fd19f855e85a39ea0a7836f4 100644 (file)
@@ -46,7 +46,6 @@ if (!defined('LACONICA')) {
  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link     http://laconi.ca/
  * @see      Notice
- * @see      StreamAction
  * @see      NoticeListItem
  * @see      ProfileNoticeList
  */
diff --git a/lib/currentuserdesignaction.php b/lib/currentuserdesignaction.php
new file mode 100644 (file)
index 0000000..2975256
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Base class for actions that use the current user's design
+ *
+ * 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  Action
+ * @package   Laconica
+ * @author    Evan Prodromou <evan@controlyourself.ca>
+ * @copyright 2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Base class for actions that use the current user's design
+ *
+ * Some pages (settings in particular) use the current user's chosen
+ * design. This superclass returns that design.
+ *
+ * @category Action
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://laconi.ca/
+ *
+ */
+
+class CurrentUserDesignAction extends Action
+{
+    /**
+     * A design for this action
+     *
+     * if the user attribute has been set, returns that user's
+     * design.
+     *
+     * @return Design a design object to use
+     */
+
+    function getDesign()
+    {
+        $cur = common_current_user();
+
+        if (empty($cur)) {
+            return null;
+        }
+
+        return $cur->getDesign();
+    }
+}
index 8fa11a7562752f217edb65bb206f797b5732c40a..498c828514b5f30b57b90ce4817a2d1984e1b587 100644 (file)
@@ -27,10 +27,9 @@ require_once INSTALLDIR.'/lib/profilelist.php';
 
 define('AVATARS_PER_PAGE', 80);
 
-class GalleryAction extends Action
+class GalleryAction extends OwnerDesignAction
 {
     var $profile = null;
-    var $user = null;
     var $page = null;
     var $tag = null;
 
index 27a1d99dcbbd93e80b43e7d815ee27ba3091fe31..4e1f1dbb1d71ccee1d9407c591ae0c135de637c2 100644 (file)
@@ -335,6 +335,7 @@ function mail_broadcast_notice_sms($notice)
                  "FROM $UT JOIN subscription " .
                  "ON $UT.id = subscription.subscriber " .
                  'WHERE subscription.subscribed = ' . $notice->profile_id . ' ' .
+                 'AND subscription.subscribed != subscription.subscriber ' .
                  "AND $UT.smsemail IS NOT null " .
                  "AND $UT.smsnotify = 1 " .
                  'AND subscription.sms = 1 ');
index 01bbf5721a4450426f2b53a0d731979fa76c1b02..766510a47b94318d4d0eb78a8021a584c9fefd84 100644 (file)
@@ -47,11 +47,11 @@ define('MESSAGES_PER_PAGE', 20);
  * @see      OutboxAction
  */
 
-class MailboxAction extends PersonalAction
+class MailboxAction extends CurrentUserDesignAction
 {
     var $page = null;
 
-    function prepare($args) 
+    function prepare($args)
     {
         parent::prepare($args);
 
@@ -265,12 +265,12 @@ class MailboxAction extends PersonalAction
      * Returns either the name (and link) of the API client that posted the notice,
      * or one of other other channels.
      *
-     * @param string $source the source of the message 
+     * @param string $source the source of the message
      *
      * @return void
      */
 
-    function showSource($source) 
+    function showSource($source)
     {
         $source_name = _($source);
         switch ($source) {
index 9ace341d8018b632103583bf88641ed1896ed806..c00942af4998226b3bdee957b29f849486005b1f 100644 (file)
@@ -50,7 +50,6 @@ require_once INSTALLDIR.'/lib/attachmentlist.php';
  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link     http://laconi.ca/
  * @see      Notice
- * @see      StreamAction
  * @see      NoticeListItem
  * @see      ProfileNoticeList
  */
index e8e1acc4139b6aa59fe916f1938e6817986ffa19..40cb847dfa8434e96f6ca0a388aa7069e867b8f0 100644 (file)
@@ -159,13 +159,9 @@ function omb_post_notice($notice, $remote_profile, $subscription)
 
 function omb_post_notice_keys($notice, $postnoticeurl, $tk, $secret)
 {
-
-    common_debug('Posting notice ' . $notice->id . ' to ' . $postnoticeurl, __FILE__);
-
     $user = User::staticGet('id', $notice->profile_id);
 
     if (!$user) {
-        common_debug('Failed to get user for notice ' . $notice->id . ', profile = ' . $notice->profile_id, __FILE__);
         return false;
     }
 
@@ -208,8 +204,6 @@ function omb_post_notice_keys($notice, $postnoticeurl, $tk, $secret)
                              $req->to_postdata(),
                              array('User-Agent: Laconica/' . LACONICA_VERSION));
 
-    common_debug('Got HTTP result "'.print_r($result,true).'"', __FILE__);
-
     if ($result->status == 403) { # not authorized, don't send again
         common_debug('403 result, deleting subscription', __FILE__);
         # FIXME: figure out how to delete this
@@ -286,14 +280,10 @@ function omb_update_profile($profile, $remote_profile, $subscription)
 
     $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
 
-    common_debug('request URL = '.$req->get_normalized_http_url(), __FILE__);
-    common_debug('postdata = '.$req->to_postdata(), __FILE__);
     $result = $fetcher->post($req->get_normalized_http_url(),
                              $req->to_postdata(),
                              array('User-Agent: Laconica/' . LACONICA_VERSION));
 
-    common_debug('Got HTTP result "'.print_r($result,true).'"', __FILE__);
-
     if (empty($result) || !$result) {
         common_debug("Unable to contact " . $req->get_normalized_http_url());
     } else if ($result->status == 403) { # not authorized, don't send again
diff --git a/lib/ownerdesignaction.php b/lib/ownerdesignaction.php
new file mode 100644 (file)
index 0000000..c47633b
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Base class for actions that use the page owner's design
+ *
+ * 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  Action
+ * @package   Laconica
+ * @author    Evan Prodromou <evan@controlyourself.ca>
+ * @copyright 2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Base class for actions that use the page owner's design
+ *
+ * Some pages have a clear "owner" -- like the profile page, subscriptions
+ * pages, etc. This superclass uses that owner's chosen design for the page
+ * design.
+ *
+ * @category Action
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://laconi.ca/
+ *
+ */
+
+class OwnerDesignAction extends Action {
+
+    /** The user for this page. */
+
+    var $user = null;
+
+    /**
+     * A design for this action
+     *
+     * if the user attribute has been set, returns that user's
+     * design.
+     *
+     * @return Design a design object to use
+     */
+
+    function getDesign()
+    {
+        if (empty($this->user)) {
+            return null;
+        }
+
+        return $this->user->getDesign();
+    }
+}
diff --git a/lib/personal.php b/lib/personal.php
deleted file mode 100644 (file)
index f927323..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-/**
- * Laconica, the distributed open-source microblogging tool
- *
- * User profile page
- *
- * 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  Personal
- * @package   Laconica
- * @author    Evan Prodromou <evan@controlyourself.ca>
- * @author    Sarven Capadisli <csarven@controlyourself.ca>
- * @copyright 2008-2009 Control Yourself, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://laconi.ca/
- */
-
-if (!defined('LACONICA')) {
-    exit(1);
-}
-
-/**
- * Base class for user profile page
- *
- * @category Personal
- * @package  Laconica
- * @author   Evan Prodromou <evan@controlyourself.ca>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://laconi.ca/
- */
-
-class PersonalAction extends Action
-{
-
-    var $user = null;
-
-    function isReadOnly($args)
-    {
-         return true;
-    }
-
-    function handle($args)
-    {
-        parent::handle($args);
-    }
-
-}
index a3437ff4ddbec567d630c3f25e4985aa8b3f73a8..a14d3846e66bfd08440b5a5e30ff8dcc9d59137c 100644 (file)
@@ -47,9 +47,8 @@ require_once INSTALLDIR.'/lib/groupminilist.php';
  * @link     http://laconi.ca/
  */
 
-class ProfileAction extends Action
+class ProfileAction extends OwnerDesignAction
 {
-    var $user    = null;
     var $page    = null;
     var $profile = null;
     var $tag     = null;
index db20c580436b46e0ae4bc361d65013d2234d43d2..17d3a2f64dcb1853190ee0064b6a224d01342be0 100644 (file)
@@ -43,7 +43,7 @@ if (!defined('LACONICA')) {
  * @see      Widget
  */
 
-class SettingsAction extends Action
+class SettingsAction extends CurrentUserDesignAction
 {
     /**
      * A message for the user.
diff --git a/lib/stream.php b/lib/stream.php
deleted file mode 100644 (file)
index 0cb9e0b..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-/*
- * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, 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('LACONICA')) { exit(1); }
-
-require_once(INSTALLDIR.'/lib/personal.php');
-require_once(INSTALLDIR.'/lib/noticelist.php');
-
-class StreamAction extends PersonalAction
-{
-    function show_notice_list($notice)
-    {
-        $nl = new NoticeList($notice);
-        return $nl->show();
-    }
-}
index caf8c071639212a4ec8c7eb19dc37509fddd197e..ca8b03cdcd79f6230df08db5bce0495932cd8773 100644 (file)
@@ -54,7 +54,7 @@ class TwitterapiAction extends Action
     /**
      * Overrides XMLOutputter::element to write booleans as strings (true|false).
      * See that method's documentation for more info.
-     * 
+     *
      * @param string $tag     Element type or tagname
      * @param array  $attrs   Array of element attributes, as
      *                        key-value pairs
@@ -70,24 +70,85 @@ class TwitterapiAction extends Action
 
         return parent::element($tag, $attrs, $content);
     }
-    
+
     function twitter_user_array($profile, $get_notice=false)
     {
-
         $twitter_user = array();
 
+        $twitter_user['id'] = intval($profile->id);
         $twitter_user['name'] = $profile->getBestName();
-        $twitter_user['followers_count'] = $this->count_subscriptions($profile);
         $twitter_user['screen_name'] = $profile->nickname;
-        $twitter_user['description'] = ($profile->bio) ? $profile->bio : null;
         $twitter_user['location'] = ($profile->location) ? $profile->location : null;
-        $twitter_user['id'] = intval($profile->id);
+        $twitter_user['description'] = ($profile->bio) ? $profile->bio : null;
 
         $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
+        $twitter_user['profile_image_url'] = ($avatar) ? $avatar->displayUrl() :
+            Avatar::defaultImage(AVATAR_STREAM_SIZE);
 
-        $twitter_user['profile_image_url'] = ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_STREAM_SIZE);
-        $twitter_user['protected'] = false; # not supported by Laconica yet
         $twitter_user['url'] = ($profile->homepage) ? $profile->homepage : null;
+        $twitter_user['protected'] = false; # not supported by Laconica yet
+        $twitter_user['followers_count'] = $this->count_subscriptions($profile);
+
+        // To be supported soon...
+        $twitter_user['profile_background_color'] = '';
+        $twitter_user['profile_text_color'] = '';
+        $twitter_user['profile_link_color'] = '';
+        $twitter_user['profile_sidebar_fill_color'] = '';
+        $twitter_user['profile_sidebar_border_color'] = '';
+
+        $subbed = DB_DataObject::factory('subscription');
+        $subbed->subscriber = $profile->id;
+        $subbed_count = (int) $subbed->count() - 1;
+        $twitter_user['friends_count'] = (is_int($subbed_count)) ? $subbed_count : 0;
+
+        $twitter_user['created_at'] = $this->date_twitter($profile->created);
+
+        $faves = DB_DataObject::factory('fave');
+        $faves->user_id = $user->id;
+        $faves_count = (int) $faves->count();
+        $twitter_user['favourites_count'] = $faves_count; // British spelling!
+
+        // Need to pull up the user for some of this
+        $user = User::staticGet($profile->id);
+
+        $timezone = 'UTC';
+
+        if ($user->timezone) {
+            $timezone = $user->timezone;
+        }
+
+        $t = new DateTime;
+        $t->setTimezone(new DateTimeZone($timezone));
+
+        $twitter_user['utc_offset'] = $t->format('Z');
+        $twitter_user['time_zone'] = $timezone;
+
+        // To be supported some day, perhaps
+        $twitter_user['profile_background_image_url'] = '';
+        $twitter_user['profile_background_tile'] = false;
+
+        $notices = DB_DataObject::factory('notice');
+        $notices->profile_id = $profile->id;
+        $notice_count = (int) $notices->count();
+
+        $twitter_user['statuses_count'] = (is_int($notice_count)) ? $notice_count : 0;
+
+        // Is the requesting user following this user?
+        $twitter_user['following'] = false;
+        $twitter_user['notifications'] = false;
+
+        if (isset($apidata['user'])) {
+
+            $twitter_user['following'] = $apidata['user']->isSubscribed($profile);
+
+            // Notifications on?
+            $sub = Subscription::pkeyGet(array('subscriber' =>
+                $apidata['user']->id, 'subscribed' => $profile->id));
+
+            if ($sub) {
+                $twitter_user['notifications'] = ($sub->jabber || $sub->sms);
+            }
+        }
 
         if ($get_notice) {
             $notice = $profile->getCurrentNotice();
@@ -612,7 +673,27 @@ class TwitterapiAction extends Action
     function get_user($id, $apidata=null)
     {
         if (!$id) {
-            return $apidata['user'];
+            
+            // Twitter supports these other ways of passing the user ID
+            if (is_numeric($this->arg('id'))) {
+                return User::staticGet($this->arg('id'));
+            } else if ($this->arg('id')) {
+                $nickname = common_canonical_nickname($this->arg('id'));
+                return User::staticGet('nickname', $nickname);
+            } else if ($this->arg('user_id')) {
+                // This is to ensure that a non-numeric user_id still 
+                // overrides screen_name even if it doesn't get used
+                if (is_numeric($this->arg('user_id'))) {
+                    return User::staticGet('id', $this->arg('user_id'));
+                }
+            } else if ($this->arg('screen_name')) {
+                $nickname = common_canonical_nickname($this->arg('screen_name'));
+                return User::staticGet('nickname', $nickname);
+            } else {
+                // Fall back to trying the currently authenticated user
+                return $apidata['user'];
+            }
+            
         } else if (is_numeric($id)) {
             return User::staticGet($id);
         } else {
index ab12723098b1b706354257b9f9b4ceba925f15ec..9872d97c4895f8f66cbae97ae4bf565cf817623d 100644 (file)
@@ -900,6 +900,34 @@ function common_enqueue_notice($notice)
     return $result;
 }
 
+function common_post_inbox_transports()
+{
+    $transports = array('omb', 'sms');
+
+    if (common_config('xmpp', 'enabled')) {
+        $transports = array_merge($transports, array('jabber', 'public'));
+    }
+
+    return $transports;
+}
+
+function common_enqueue_notice_transport($notice, $transport)
+{
+    $qi = new Queue_item();
+    $qi->notice_id = $notice->id;
+    $qi->transport = $transport;
+    $qi->created = $notice->created;
+    $result = $qi->insert();
+    if (!$result) {
+        $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
+        common_log(LOG_ERR, 'DB error inserting queue item: ' . $last_error->message);
+        throw new ServerException('DB error inserting queue item: ' . $last_error->message);
+>>>>>>> 0.7.x:lib/util.php
+    }
+    common_log(LOG_DEBUG, 'complete queueing notice ID = ' . $notice->id . ' for ' . $transport);
+    return true;
+}
+
 function common_real_broadcast($notice, $remote=false)
 {
     $success = true;
index e5021ff343e5896a0c44f9926cad66792fca572e..1693760914e32e96c6227e4715fd3c669728aead 100644 (file)
@@ -35,107 +35,334 @@ define('LACONICA', true);
 require_once(INSTALLDIR . '/lib/common.php');
 require_once('DB.php');
 
-function fixup_utf8($id) {
+class UTF8FixerUpper
+{
+    var $dbl = null;
+    var $dbu = null;
+    var $args = array();
+
+    function __construct($args)
+    {
+        $this->args = $args;
+
+        if (array_key_exists('max_date', $args)) {
+            $this->max_date = strftime('%Y-%m-%d %H:%M:%S', strtotime($args['max_date']));
+        } else {
+            $this->max_date = strftime('%Y-%m-%d %H:%M:%S', time());
+        }
 
-    $dbl = doConnect('latin1');
+        $this->dbl = $this->doConnect('latin1');
 
-    if (empty($dbl)) {
-        return;
-    }
+        if (empty($this->dbl)) {
+            return;
+        }
 
-    $dbu = doConnect('utf8');
+        $this->dbu = $this->doConnect('utf8');
 
-    if (empty($dbu)) {
-        return;
+        if (empty($this->dbu)) {
+            return;
+        }
     }
 
-    // Do a separate DB connection
+    function doConnect($charset)
+    {
+        $db = DB::connect(common_config('db', 'database'),
+                          array('persistent' => false));
 
-    $sth = $dbu->prepare("UPDATE notice SET content = UNHEX(?), rendered = UNHEX(?) WHERE id = ?");
+        if (PEAR::isError($db)) {
+            echo "ERROR: " . $db->getMessage() . "\n";
+            return NULL;
+        }
 
-    if (PEAR::isError($sth)) {
-        echo "ERROR: " . $sth->getMessage() . "\n";
-        return;
-    }
+        $conn = $db->connection;
 
-    $sql = 'SELECT id, content, rendered FROM notice ' .
-      'WHERE LENGTH(content) != CHAR_LENGTH(content)';
+        $succ = mysqli_set_charset($conn, $charset);
 
-    if (!empty($id)) {
-        $sql .= ' AND id < ' . $id;
-    }
+        if (!$succ) {
+            echo "ERROR: couldn't set charset\n";
+            $db->disconnect();
+            return NULL;
+        }
 
-    $sql .= ' ORDER BY id DESC';
+        $result = $db->autoCommit(true);
 
-    $rn = $dbl->query($sql);
+        if (PEAR::isError($result)) {
+            echo "ERROR: " . $result->getMessage() . "\n";
+            $db->disconnect();
+            return NULL;
+        }
 
-    if (PEAR::isError($rn)) {
-        echo "ERROR: " . $rn->getMessage() . "\n";
-        return;
+        return $db;
     }
 
-    echo "Number of rows: " . $rn->numRows() . "\n";
+    function fixup()
+    {
+        $this->fixupNotices($this->args['max_notice'],
+                            $this->args['min_notice']);
+        $this->fixupProfiles();
+        $this->fixupGroups();
+        $this->fixupMessages();
+    }
 
-    $notice = array();
+    function fixupNotices($max_id, $min_id) {
 
-    while (DB_OK == $rn->fetchInto($notice)) {
+        // Do a separate DB connection
 
-        $id = ($notice[0])+0;
-        $content = bin2hex($notice[1]);
-        $rendered = bin2hex($notice[2]);
+        $sth = $this->dbu->prepare("UPDATE notice SET content = UNHEX(?), rendered = UNHEX(?) WHERE id = ?");
 
-        echo "$id...";
+        if (PEAR::isError($sth)) {
+            echo "ERROR: " . $sth->getMessage() . "\n";
+            return;
+        }
 
-        $result =& $dbu->execute($sth, array($content, $rendered, $id));
+        $sql = 'SELECT id, content, rendered FROM notice ' .
+          'WHERE LENGTH(content) != CHAR_LENGTH(content) '.
+          'AND modified < "'.$this->max_date.'" ';
 
-        if (PEAR::isError($result)) {
-            echo "ERROR: " . $result->getMessage() . "\n";
-            continue;
+        if (!empty($max_id)) {
+            $sql .= ' AND id <= ' . $max_id;
+        }
+
+        if (!empty($min_id)) {
+            $sql .= ' AND id >= ' . $min_id;
         }
 
-        $cnt = $dbu->affectedRows();
+        $sql .= ' ORDER BY id DESC';
 
-        if ($cnt != 1) {
-            echo "ERROR: 0 rows affected\n";
-            continue;
+        $rn = $this->dbl->query($sql);
+
+        if (PEAR::isError($rn)) {
+            echo "ERROR: " . $rn->getMessage() . "\n";
+            return;
         }
 
-        $notice = Notice::staticGet('id', $id);
-        $notice->decache();
+        echo "Number of rows: " . $rn->numRows() . "\n";
 
-        echo "OK\n";
-    }
-}
+        $notice = array();
 
-function doConnect($charset)
-{
-    $db = DB::connect(common_config('db', 'database'),
-                      array('persistent' => false));
+        while (DB_OK == $rn->fetchInto($notice)) {
+
+            $id = ($notice[0])+0;
+            $content = bin2hex($notice[1]);
+            $rendered = bin2hex($notice[2]);
 
-    if (PEAR::isError($db)) {
-        echo "ERROR: " . $db->getMessage() . "\n";
-        return NULL;
+            echo "$id...";
+
+            $result =& $this->dbu->execute($sth, array($content, $rendered, $id));
+
+            if (PEAR::isError($result)) {
+                echo "ERROR: " . $result->getMessage() . "\n";
+                continue;
+            }
+
+            $cnt = $this->dbu->affectedRows();
+
+            if ($cnt != 1) {
+                echo "ERROR: 0 rows affected\n";
+                continue;
+            }
+
+            $notice = Notice::staticGet('id', $id);
+            $notice->decache();
+            $notice->free();
+
+            echo "OK\n";
+        }
     }
 
-    $result = $db->query("SET NAMES $charset");
+    function fixupProfiles()
+    {
+        // Do a separate DB connection
+
+        $sth = $this->dbu->prepare("UPDATE profile SET ".
+                                   "fullname = UNHEX(?),".
+                                   "location = UNHEX(?), ".
+                                   "bio = UNHEX(?) ".
+                                   "WHERE id = ?");
+
+        if (PEAR::isError($sth)) {
+            echo "ERROR: " . $sth->getMessage() . "\n";
+            return;
+        }
+
+        $sql = 'SELECT id, fullname, location, bio FROM profile ' .
+          'WHERE (LENGTH(fullname) != CHAR_LENGTH(fullname) '.
+          'OR LENGTH(location) != CHAR_LENGTH(location) '.
+          'OR LENGTH(bio) != CHAR_LENGTH(bio)) '.
+          'AND modified < "'.$this->max_date.'" '.
+          ' ORDER BY modified DESC';
+
+        $rn = $this->dbl->query($sql);
+
+        if (PEAR::isError($rn)) {
+            echo "ERROR: " . $rn->getMessage() . "\n";
+            return;
+        }
+
+        echo "Number of rows: " . $rn->numRows() . "\n";
+
+        $profile = array();
+
+        while (DB_OK == $rn->fetchInto($profile)) {
+
+            $id = ($profile[0])+0;
+            $fullname = bin2hex($profile[1]);
+            $location = bin2hex($profile[2]);
+            $bio = bin2hex($profile[3]);
+
+            echo "$id...";
+
+            $result =& $this->dbu->execute($sth, array($fullname, $location, $bio, $id));
+
+            if (PEAR::isError($result)) {
+                echo "ERROR: " . $result->getMessage() . "\n";
+                continue;
+            }
+
+            $cnt = $this->dbu->affectedRows();
 
-    if (PEAR::isError($result)) {
-        echo "ERROR: " . $result->getMessage() . "\n";
-        $db->disconnect();
-        return NULL;
+            if ($cnt != 1) {
+                echo "ERROR: 0 rows affected\n";
+                continue;
+            }
+
+            $profile = Profile::staticGet('id', $id);
+            $profile->decache();
+            $profile->free();
+
+            echo "OK\n";
+        }
     }
 
-    $result = $db->autoCommit(true);
+    function fixupGroups()
+    {
+        // Do a separate DB connection
+
+        $sth = $this->dbu->prepare("UPDATE user_group SET ".
+                                   "fullname = UNHEX(?),".
+                                   "location = UNHEX(?), ".
+                                   "description = UNHEX(?) ".
+                                   "WHERE id = ?");
+
+        if (PEAR::isError($sth)) {
+            echo "ERROR: " . $sth->getMessage() . "\n";
+            return;
+        }
+
+        $sql = 'SELECT id, fullname, location, description FROM user_group ' .
+          'WHERE LENGTH(fullname) != CHAR_LENGTH(fullname) '.
+          'OR LENGTH(location) != CHAR_LENGTH(location) '.
+          'OR LENGTH(description) != CHAR_LENGTH(description) ';
+          'AND modified < "'.$this->max_date.'" '.
+          'ORDER BY modified DESC';
+
+        $rn = $this->dbl->query($sql);
+
+        if (PEAR::isError($rn)) {
+            echo "ERROR: " . $rn->getMessage() . "\n";
+            return;
+        }
+
+        echo "Number of rows: " . $rn->numRows() . "\n";
 
-    if (PEAR::isError($result)) {
-        echo "ERROR: " . $result->getMessage() . "\n";
-        $db->disconnect();
-        return NULL;
+        $user_group = array();
+
+        while (DB_OK == $rn->fetchInto($user_group)) {
+
+            $id = ($user_group[0])+0;
+            $fullname = bin2hex($user_group[1]);
+            $location = bin2hex($user_group[2]);
+            $description = bin2hex($user_group[3]);
+
+            echo "$id...";
+
+            $result =& $this->dbu->execute($sth, array($fullname, $location, $description, $id));
+
+            if (PEAR::isError($result)) {
+                echo "ERROR: " . $result->getMessage() . "\n";
+                continue;
+            }
+
+            $cnt = $this->dbu->affectedRows();
+
+            if ($cnt != 1) {
+                echo "ERROR: 0 rows affected\n";
+                continue;
+            }
+
+            $user_group = User_group::staticGet('id', $id);
+            $user_group->decache();
+            $user_group->free();
+
+            echo "OK\n";
+        }
     }
 
-    return $db;
+    function fixupMessages() {
+
+        // Do a separate DB connection
+
+        $sth = $this->dbu->prepare("UPDATE message SET content = UNHEX(?), rendered = UNHEX(?) WHERE id = ?");
+
+        if (PEAR::isError($sth)) {
+            echo "ERROR: " . $sth->getMessage() . "\n";
+            return;
+        }
+
+        $sql = 'SELECT id, content, rendered FROM message ' .
+          'WHERE LENGTH(content) != CHAR_LENGTH(content) '.
+          'AND modified < "'.$this->max_date.'" '.
+          'ORDER BY id DESC';
+
+        $rn = $this->dbl->query($sql);
+
+        if (PEAR::isError($rn)) {
+            echo "ERROR: " . $rn->getMessage() . "\n";
+            return;
+        }
+
+        echo "Number of rows: " . $rn->numRows() . "\n";
+
+        $message = array();
+
+        while (DB_OK == $rn->fetchInto($message)) {
+
+            $id = ($message[0])+0;
+            $content = bin2hex($message[1]);
+            $rendered = bin2hex($message[2]);
+
+            echo "$id...";
+
+            $result =& $this->dbu->execute($sth, array($content, $rendered, $id));
+
+            if (PEAR::isError($result)) {
+                echo "ERROR: " . $result->getMessage() . "\n";
+                continue;
+            }
+
+            $cnt = $this->dbu->affectedRows();
+
+            if ($cnt != 1) {
+                echo "ERROR: 0 rows affected\n";
+                continue;
+            }
+
+            $message = Message::staticGet('id', $id);
+            $message->decache();
+            $message->free();
+
+            echo "OK\n";
+        }
+    }
 }
 
-$id = ($argc > 1) ? $argv[1] : null;
+$max_date = ($argc > 1) ? $argv[1] : null;
+$max_id = ($argc > 2) ? $argv[2] : null;
+$min_id = ($argc > 3) ? $argv[3] : null;
+
+$fixer = new UTF8FixerUpper(array('max_date' => $max_date,
+                                  'max_notice' => $max_id,
+                                  'min_notice' => $min_id));
+
+$fixer->fixup();
 
-fixup_utf8($id);
index a10233e69fcc2ce453a969118eccd867c37c63f7..4e49f9bd4bc776e1ef7b5928dd3deab39972ac89 100755 (executable)
@@ -25,7 +25,6 @@
  * daemon names.
  */
 
-
 # Abort if called from a web server
 if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
     print "This script must be run from the command line\n";
@@ -51,5 +50,4 @@ echo "ombqueuehandler.php ";
 echo "twitterqueuehandler.php ";
 echo "facebookqueuehandler.php ";
 echo "pingqueuehandler.php ";
-echo "inboxqueuehandler.php ";
 echo "smsqueuehandler.php ";
diff --git a/scripts/inboxqueuehandler.php b/scripts/inboxqueuehandler.php
deleted file mode 100755 (executable)
index 73d31e8..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/usr/bin/env php
-<?php
-/*
- * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008,2009 Control Yourself, 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/>.
- */
-
-// Abort if called from a web server
-
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
-
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
-
-require_once(INSTALLDIR . '/lib/common.php');
-require_once(INSTALLDIR . '/lib/queuehandler.php');
-
-set_error_handler('common_error_handler');
-
-class InboxQueueHandler extends QueueHandler
-{
-    function transport()
-    {
-        return 'inbox';
-    }
-
-       function start() {
-               $this->log(LOG_INFO, "INITIALIZE");
-               return true;
-       }
-
-    function handle_notice($notice)
-    {
-        $this->log(LOG_INFO, "Distributing notice to inboxes for $notice->id");
-        $notice->addToInboxes();
-        $notice->blowSubsCache();
-        return true;
-    }
-
-       function finish() {
-       }
-}
-
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
-
-$id = ($argc > 1) ? $argv[1] : null;
-
-$handler = new InboxQueueHandler($id);
-
-$handler->runOnce();
diff --git a/scripts/memcachedqueuehandler.php b/scripts/memcachedqueuehandler.php
deleted file mode 100755 (executable)
index 185b781..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/env php
-<?php
-/*
- * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008,2009 Control Yourself, 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/>.
- */
-
-// Abort if called from a web server
-
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
-
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
-
-require_once(INSTALLDIR . '/lib/common.php');
-require_once(INSTALLDIR . '/lib/queuehandler.php');
-
-set_error_handler('common_error_handler');
-
-class MemcachedQueueHandler extends QueueHandler
-{
-    function transport()
-    {
-        return 'memcache';
-    }
-
-       function start() {
-               $this->log(LOG_INFO, "INITIALIZE");
-               return true;
-       }
-
-    function handle_notice($notice)
-    {
-        // XXX: fork here
-        $this->log(LOG_INFO, "Blowing memcached for $notice->id");
-        $notice->blowCaches();
-        return true;
-    }
-
-       function finish() {
-       }
-
-}
-
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
-
-$id = ($argc > 1) ? $argv[1] : null;
-
-$handler = new MemcachedQueueHandler($id);
-
-$handler->runOnce();
index 764037e8ff0bb4f06d7d6041d0f06437ab22d748..2134b4ab00f2af90e95750a5a24fb9153d97a917 100755 (executable)
@@ -24,8 +24,7 @@ SDIR=`dirname $0`
 DIR=`php $SDIR/getpiddir.php`
 
 for f in jabberhandler ombhandler publichandler smshandler pinghandler \
-        xmppconfirmhandler xmppdaemon twitterhandler facebookhandler \
-         memcachehandler inboxhandler twitterstatusfetcher; do
+        xmppconfirmhandler xmppdaemon twitterhandler facebookhandler; do
 
        FILES="$DIR/$f.*.pid"
        for ff in "$FILES" ; do