]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch 'tos' into 0.8.x
authorEvan Prodromou <evan@controlyourself.ca>
Tue, 7 Jul 2009 15:34:49 +0000 (11:34 -0400)
committerEvan Prodromou <evan@controlyourself.ca>
Tue, 7 Jul 2009 15:34:49 +0000 (11:34 -0400)
138 files changed:
EVENTS.txt
README
actions/api.php
actions/attachment.php
actions/attachment_ajax.php
actions/attachment_thumbnail.php
actions/conversation.php
actions/disfavor.php
actions/favor.php
actions/file.php
actions/groupdesignsettings.php
actions/groupmembers.php
actions/grouprss.php
actions/groups.php
actions/invite.php
actions/newnotice.php
actions/noticesearchrss.php
actions/othersettings.php
actions/peoplesearch.php
actions/public.php
actions/showfavorites.php
actions/showgroup.php
actions/shownotice.php
actions/subscriptions.php
actions/sup.php
actions/twitapifavorites.php
actions/twitapifriendships.php
actions/twitapisearchatom.php
actions/twitapisearchjson.php
actions/twitapistatuses.php
actions/twitapiusers.php
actions/userdesignsettings.php
classes/Design.php
classes/Fave.php
classes/File.php
classes/File_oembed.php
classes/File_redirection.php
classes/File_thumbnail.php
classes/File_to_post.php
classes/Foreign_user.php
classes/Group_inbox.php
classes/Memcached_DataObject.php
classes/Notice.php
classes/Notice_inbox.php
classes/Profile.php
classes/Session.php [new file with mode: 0644]
classes/Status_network.php
classes/User.php
classes/User_group.php
classes/laconica.ini [changed mode: 0755->0644]
config.php.sample
db/074to080.sql [new file with mode: 0644]
db/innodb.sql [new file with mode: 0644]
db/laconica.sql
extlib/Console/Getopt.php [new file with mode: 0644]
extlib/Mail/mimeDecode.php [new file with mode: 0644]
extlib/System/Command.php [new file with mode: 0644]
extlib/XMPPHP/BOSH.php
extlib/XMPPHP/XMLStream.php
extlib/XMPPHP/XMPP.php
index.php
install.php
js/jquery.joverlay.js [new file with mode: 0644]
js/jquery.joverlay.min.js
js/userdesign.go.js
js/util.js
lib/Shorturl_api.php
lib/action.php
lib/attachmentlist.php
lib/common.php
lib/currentuserdesignaction.php
lib/daemon.php
lib/designsettings.php
lib/facebookaction.php
lib/groupdesignaction.php
lib/groupeditform.php
lib/imagefile.php
lib/jsonsearchresultslist.php
lib/language.php
lib/noticelist.php
lib/ownerdesignaction.php
lib/peoplesearchresults.php [deleted file]
lib/profileaction.php
lib/profilelist.php
lib/profileminilist.php
lib/queuehandler.php
lib/router.php
lib/rssaction.php
lib/search_engines.php
lib/searchaction.php
lib/subgroupnav.php
lib/theme.php
lib/twitterapi.php
lib/util.php
lib/xmppqueuehandler.php
plugins/FBConnect/FBConnectAuth.php
plugins/FBConnect/FBConnectPlugin.css
plugins/FBConnect/FBConnectPlugin.php
scripts/allsites.php [new file with mode: 0755]
scripts/commandline.inc [new file with mode: 0644]
scripts/decache.php
scripts/delete_status_network.sh [new file with mode: 0755]
scripts/enjitqueuehandler.php
scripts/facebookqueuehandler.php
scripts/fixup_conversations.php [new file with mode: 0755]
scripts/fixup_utf8.php
scripts/getpiddir.php
scripts/getvaliddaemons.php
scripts/inbox_users.php
scripts/jabberqueuehandler.php
scripts/maildaemon.php
scripts/ombqueuehandler.php
scripts/pingqueuehandler.php
scripts/publicqueuehandler.php
scripts/reportsnapshot.php
scripts/setpassword.php
scripts/setup.cfg.sample
scripts/setup_status_network.sh
scripts/showcache.php [new file with mode: 0644]
scripts/sitemap.php
scripts/smsqueuehandler.php
scripts/startdaemons.sh
scripts/stopdaemons.sh
scripts/synctwitterfriends.php
scripts/triminboxes.php
scripts/twitterqueuehandler.php
scripts/twitterstatusfetcher.php
scripts/uncache_users.php
scripts/xmppconfirmhandler.php
scripts/xmppdaemon.php
sphinx.conf.sample
theme/base/css/display.css
theme/base/css/ie.css
theme/base/css/ie6.css
theme/default/css/display.css
theme/default/css/ie.css
theme/identica/css/display.css
theme/identica/css/ie.css

index 8e917f11de864605b5e561caf1b797f33a3d0e96..2c43469d4642c3e8a9edc5f1bd5f6da2fd33831c 100644 (file)
@@ -1,5 +1,4 @@
-InitializePlugin: a chance to initialize a plugin in a complete
-                 environment
+InitializePlugin: a chance to initialize a plugin in a complete environment
 
 CleanupPlugin: a chance to cleanup a plugin at the end of a program
 
@@ -117,3 +116,9 @@ EndLogout: After logging out
 
 ArgsInitialized: After the argument array has been initialized
 - $args: associative array of arguments, can be modified
+
+StartAddressData: Allows the site owner to provide additional information about themselves for contact (e.g., tagline, email, location)
+- $action: the current action
+
+EndAddressData: At the end of <address>
+- $action: the current action
diff --git a/README b/README
index 5aa7270eece7c2bf6ea98823c223e9c62b4e6668..0f1b5a43b485e17445599fea76b126e96503acdb 100644 (file)
--- a/README
+++ b/README
@@ -180,6 +180,7 @@ and the URLs are listed here for your convenience.
 - PEAR HTTP_Request is an oEmbed dependency.
 - PEAR Validate is an oEmbed dependency.
 - PEAR Net_URL2 is an oEmbed dependency.
+- Console_GetOpt for parsing command-line options.
 
 A design goal of Laconica is that the basic Web functionality should
 work on even the most restrictive commercial hosting services.
@@ -905,6 +906,9 @@ sslserver: use an alternate server name for SSL URLs, like
            parameters correctly so that both the SSL server and the
            "normal" server can access the session cookie and
            preferably other cookies as well.
+shorturllength: Length of URL at which URLs in a message exceeding 140
+                characters will be sent to the user's chosen
+                shortening service.
 
 db
 --
@@ -1039,9 +1043,16 @@ theme
 -----
 
 server: Like avatars, you can speed up page loading by pointing the
-       theme file lookup to another server (virtual or real). The
-       theme server's root path should map to the Laconica "theme"
-       subdirectory. Defaults to NULL.
+       theme file lookup to another server (virtual or real).
+       Defaults to NULL, meaning to use the site server.
+dir:    Directory where theme files are stored. Used to determine
+       whether to show parts of a theme file. Defaults to the theme
+       subdirectory of the install directory.
+path:  Path part of theme URLs, before the theme name. Relative to the
+       theme server. It may make sense to change this path when upgrading,
+       (using version numbers as the path) to make sure that all files are
+       reloaded by caching clients or proxies. Defaults to null,
+       which means to use the site path + '/theme'.
 
 xmpp
 ----
@@ -1073,6 +1084,13 @@ debug: if turned on, this will make the XMPP library blurt out all of
 public: an array of JIDs to send _all_ notices to. This is useful for
        participating in third-party search and archiving services.
 
+invite
+------
+
+For configuring invites.
+
+enabled: Whether to allow users to send invites. Default true.
+
 tag
 ---
 
@@ -1224,6 +1242,10 @@ supported: an array of mime types you accept to store and distribute,
            setup your server to properly reckognize the types you want to
            support.
 uploads:   false to disable uploading files with notices (true by default).
+filecommand: The required MIME_Type library may need to use the 'file'
+            command. It tries the one in the Web server's path, but if
+            you're having problems with uploads, try setting this to the
+            correct value. Note: 'file' must accept '-b' and '-i' options.
 
 For quotas, be sure you've set the upload_max_filesize and post_max_size
 in php.ini to be large enough to handle your upload. In httpd.conf
@@ -1247,7 +1269,6 @@ Options for group functionality.
 maxaliases: maximum number of aliases a group can have. Default 3. Set
             to 0 or less to prevent aliases in a group.
 
-
 oohembed
 --------
 
@@ -1255,6 +1276,30 @@ oEmbed endpoint for multimedia attachments (links in posts).
 
 endpoint: oohembed endpoint using http://oohembed.com/ software.
 
+search
+------
+
+Some stuff for search.
+
+type: type of search. Ignored if PostgreSQL or Sphinx are enabled. Can either
+      be 'fulltext' (default) or 'like'. The former is faster and more efficient
+      but requires the lame old MyISAM engine for MySQL. The latter
+      will work with InnoDB but could be miserably slow on large
+      systems. We'll probably add another type sometime in the future,
+      with our own indexing system (maybe like MediaWiki's).
+
+sessions
+--------
+
+Session handling.
+
+handle: boolean. Whether we should register our own PHP session-handling
+       code (using the database and memcache if enabled). Defaults to false.
+       Setting this to true makes some sense on large or multi-server
+       sites, but it probably won't hurt for smaller ones, either.
+debug: whether to output debugging info for session storage. Can help
+       with weird session bugs, sometimes. Default false.
+
 Troubleshooting
 ===============
 
index 1fe5875ad6b586d6ec2fe7f2938ceab0110797dd..18c3b68d4b336e2e6da9b66708f998dc11e9efa2 100644 (file)
@@ -67,20 +67,22 @@ class ApiAction extends Action
                     $this->process_command();
                 } else {
                     # basic authentication failed
-                    common_log(LOG_WARNING, "Failed API auth attempt, nickname: $nickname.");
+                    list($proxy, $ip) = common_client_ip();
+
+                    common_log(LOG_WARNING, "Failed API auth attempt, nickname = $nickname, proxy = $proxy, ip = $ip.");
                     $this->show_basic_auth_error();
                 }
             }
         } else {
 
-                       # Caller might give us a username even if not required
-                       if (isset($_SERVER['PHP_AUTH_USER'])) {
-                               $user = User::staticGet('nickname', $_SERVER['PHP_AUTH_USER']);
-                               if ($user) {
-                                       $this->user = $user;
-                               }
-                               # Twitter doesn't throw an error if the user isn't found
-                       }
+            // Caller might give us a username even if not required
+            if (isset($_SERVER['PHP_AUTH_USER'])) {
+                $user = User::staticGet('nickname', $_SERVER['PHP_AUTH_USER']);
+                if ($user) {
+                    $this->user = $user;
+                }
+                # Twitter doesn't throw an error if the user isn't found
+            }
 
             $this->process_command();
         }
@@ -115,7 +117,7 @@ class ApiAction extends Action
         }
     }
 
-    # Whitelist of API methods that don't need authentication
+    // Whitelist of API methods that don't need authentication
     function requires_auth()
     {
         static $noauth = array( 'statuses/public_timeline',
@@ -133,28 +135,61 @@ class ApiAction extends Action
                                  'statuses/replies',
                                  'statuses/mentions',
                                  'statuses/followers',
-                                 'favorites/favorites');
+                                 'favorites/favorites',
+                                 'friendships/show');
 
         $fullname = "$this->api_action/$this->api_method";
 
         // If the site is "private", all API methods except laconica/config
         // need authentication
+
         if (common_config('site', 'private')) {
             return $fullname != 'laconica/config' || false;
         }
 
+        // bareauth: only needs auth if without an argument or query param specifying user
+
         if (in_array($fullname, $bareauth)) {
-            # 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')) {
+
+            // Special case: friendships/show only needs auth if source_id or
+            // source_screen_name is not specified as a param
+
+            if ($fullname == 'friendships/show') {
+
+                $source_id          = $this->arg('source_id');
+                $source_screen_name = $this->arg('source_screen_name');
+
+                if (empty($source_id) && empty($source_screen_name)) {
+                    return true;
+                }
+
                 return false;
-            } else {
+            }
+
+            // if all of these are empty, auth is required
+
+            $id          = $this->arg('id');
+            $user_id     = $this->arg('user_id');
+            $screen_name = $this->arg('screen_name');
+
+            if (empty($this->api_arg) &&
+                empty($id)            &&
+                empty($user_id)       &&
+                empty($screen_name)) {
                 return true;
+            } else {
+                return false;
             }
+
         } else if (in_array($fullname, $noauth)) {
-            # noauth: never needs auth
+
+            // noauth: never needs auth
+
             return false;
         } else {
-            # everybody else needs auth
+
+            // everybody else needs auth
+
             return true;
         }
     }
index e4dc0e054e27be9a238ad842eb823a8575e8d989..ee4cd9640d971d1b0b19bbe1cdbfa6b1ee5be67b 100644 (file)
@@ -111,7 +111,16 @@ class AttachmentAction extends Action
     function handle($args)
     {
         parent::handle($args);
-        $this->showPage();
+
+        if (empty($this->attachment->filename)) {
+
+            // if it's not a local file, gtfo
+
+            common_redirect($this->attachment->url, 303);
+
+        } else {
+            $this->showPage();
+        }
     }
 
     /**
index 5d6773010f87668ad9f51fe6b01aa8580bb29d10..4caa159f3a456918ecb6f3923944fed796711cd9 100644 (file)
@@ -58,6 +58,11 @@ class Attachment_ajaxAction extends AttachmentAction
         }
     }
 
+    function handle($args)
+    {
+        $this->showPage();
+    }
+
     /**
      * Show core.
      *
index b4070e747b78ff6dd58ea3c7876a0140f793ae80..248d16e38c04f629e08bb8e1520b8bdda44ae52e 100644 (file)
@@ -45,6 +45,12 @@ require_once INSTALLDIR.'/actions/attachment.php';
 
 class Attachment_thumbnailAction extends AttachmentAction
 {
+
+    function handle($args)
+    {
+        $this->showPage();
+    }
+
     /**
      * Show page, a template method.
      *
@@ -74,45 +80,5 @@ class Attachment_thumbnailAction extends AttachmentAction
         $this->element('img', array('src' => $file_thumbnail->url, 'alt' => 'Thumbnail'));
     }
 
-    /**
-     * Last-modified date for page
-     *
-     * When was the content of this page last modified? Based on notice,
-     * profile, avatar.
-     *
-     * @return int last-modified date as unix timestamp
-     */
-/*
-    function lastModified()
-    {
-        return max(strtotime($this->notice->created),
-                   strtotime($this->profile->modified),
-                   ($this->avatar) ? strtotime($this->avatar->modified) : 0);
-    }
-*/
-
-    /**
-     * An entity tag for this page
-     *
-     * Shows the ETag for the page, based on the notice ID and timestamps
-     * for the notice, profile, and avatar. It's weak, since we change
-     * the date text "one hour ago", etc.
-     *
-     * @return string etag
-     */
-/*
-    function etag()
-    {
-        $avtime = ($this->avatar) ?
-          strtotime($this->avatar->modified) : 0;
-
-        return 'W/"' . implode(':', array($this->arg('action'),
-                                          common_language(),
-                                          $this->notice->id,
-                                          strtotime($this->notice->created),
-                                          strtotime($this->profile->modified),
-                                          $avtime)) . '"';
-    }
-*/
 }
 
index d3fc5b6a9c31d124056619d6a2a0d5c771f8ae10..0eb0d86d65e0e61a974fb95d672b0756cdc24d67 100644 (file)
@@ -31,6 +31,9 @@ if (!defined('LACONICA')) {
     exit(1);
 }
 
+// XXX: not sure how to do paging yet,
+// so set a 60-notice limit
+
 require_once INSTALLDIR.'/lib/noticelist.php';
 
 /**
@@ -63,6 +66,7 @@ class ConversationAction extends Action
         if (empty($this->id)) {
             return false;
         }
+        $this->id = $this->id+0;
         $this->page = $this->trimmed('page');
         if (empty($this->page)) {
             $this->page = 1;
@@ -106,27 +110,12 @@ class ConversationAction extends Action
 
     function showContent()
     {
-        // FIXME this needs to be a tree, not a list
-
-        $qry = 'SELECT * FROM notice WHERE conversation = %s ';
-
-        $offset = ($this->page-1) * NOTICES_PER_PAGE;
-        $limit  = NOTICES_PER_PAGE + 1;
-
-        $txt = sprintf($qry, $this->id);
-
-        $notices = Notice::getStream($txt,
-                                     'notice:conversation:'.$this->id,
-                                     $offset, $limit);
+        $notices = Notice::conversationStream($this->id, null, null);
 
         $ct = new ConversationTree($notices, $this);
 
         $cnt = $ct->show();
-
-        $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
-                          $this->page, 'conversation', array('id' => $this->id));
     }
-
 }
 
 /**
@@ -154,8 +143,25 @@ class ConversationTree extends NoticeList
 
     function show()
     {
-        $cnt = 0;
+        $cnt = $this->_buildTree();
+
+        $this->out->elementStart('div', array('id' =>'notices_primary'));
+        $this->out->element('h2', null, _('Notices'));
+        $this->out->elementStart('ol', array('class' => 'notices xoxo'));
 
+        if (array_key_exists('root', $this->tree)) {
+            $rootid = $this->tree['root'][0];
+            $this->showNoticePlus($rootid);
+        }
+
+        $this->out->elementEnd('ol');
+        $this->out->elementEnd('div');
+
+        return $cnt;
+    }
+
+    function _buildTree()
+    {
         $this->tree  = array();
         $this->table = array();
 
@@ -177,18 +183,6 @@ class ConversationTree extends NoticeList
             }
         }
 
-        $this->out->elementStart('div', array('id' =>'notices_primary'));
-        $this->out->element('h2', null, _('Notices'));
-        $this->out->elementStart('ol', array('class' => 'notices xoxo'));
-
-        if (array_key_exists('root', $this->tree)) {
-            $rootid = $this->tree['root'][0];
-            $this->showNoticePlus($rootid);
-        }
-
-        $this->out->elementEnd('ol');
-        $this->out->elementEnd('div');
-
         return $cnt;
     }
 
index 740f7de9333707458259037dc25aba62b883b04d..02e01d6e006bd3747beca0bfdfb694fd530309d0 100644 (file)
@@ -75,7 +75,7 @@ class DisfavorAction extends Action
             return;
         }
         $fave            = new Fave();
-        $fave->user_id   = $this->id;
+        $fave->user_id   = $user->id;
         $fave->notice_id = $notice->id;
         if (!$fave->find(true)) {
             $this->clientError(_('This notice is not a favorite!'));
index ec86b17e699a6bab1a5edd6a99942a58f91fa7f6..fe51e34a272c8d73ddfd9244e2fda14d28a9fe10 100644 (file)
@@ -12,8 +12,6 @@
  * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
  * @link     http://laconi.ca/
  *
-
-/*
  * Laconica - a distributed open-source microblogging tool
  * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
index bb245c4a778abd73145db2b3006e061008a1fee9..8310e48df1565431f1ba23f5a4455af455c73c1c 100644 (file)
@@ -21,20 +21,52 @@ if (!defined('LACONICA')) { exit(1); }
 
 require_once(INSTALLDIR.'/actions/shownotice.php');
 
-class FileAction extends ShowNoticeAction
+class FileAction extends Action
 {
-    function showPage() {
-        $source_url = common_local_url('file', array('notice' => $this->notice->id));
-        $query = "select file_redirection.url as url from file join file_redirection on file.id = file_redirection.file_id where file.url = '$source_url'";
-        $file = new File_redirection;
-        $file->query($query);
-        $file->fetch();
-        if (empty($file->url)) {
-            die('nothing attached here');
-        } else {
-            header("Location: {$file->url}");
-            die();
+    var $id = null;
+    var $filerec = null;
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+        $this->id = $this->trimmed('notice');
+        if (empty($this->id)) {
+            $this->clientError(_('No notice id'));
+        }
+        $notice = Notice::staticGet('id', $this->id);
+        if (empty($notice)) {
+            $this->clientError(_('No notice'));
+        }
+        $atts = $notice->attachments();
+        if (empty($atts)) {
+            $this->clientError(_('No attachments'));
         }
+        foreach ($atts as $att) {
+            if (!empty($att->filename)) {
+                $this->filerec = $att;
+                break;
+            }
+        }
+        if (empty($this->filerec)) {
+            $this->clientError(_('No uploaded attachments'));
+        }
+        return true;
     }
+
+    function handle() {
+        common_redirect($this->filerec->url);
+    }
+
+    /**
+     * Is this action read-only?
+     *
+     * @return boolean true
+     */
+
+    function isReadOnly($args)
+    {
+        return true;
+    }
+
 }
 
index 7270bc8f7ecf7023c3accff952548953f3b3a61d..bb01243c6e7e8eb599563153a0a06a765d7c078c 100644 (file)
@@ -34,19 +34,37 @@ if (!defined('LACONICA')) {
 
 require_once INSTALLDIR . '/lib/designsettings.php';
 
+/**
+ * Set a group's design
+ *
+ * Saves a design for a given group
+ *
+ * @category Settings
+ * @package  Laconica
+ * @author   Zach Copley <zach@controlyourself.ca>
+ * @author   Sarven Capadisli <csarven@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 GroupDesignSettingsAction extends DesignSettingsAction
 {
     var $group = null;
 
     /**
-     * Prepare to run
+     * Sets the right action for the form, and passes request args into
+     * the base action
+     *
+     * @param array $args misc. arguments
+     *
+     * @return boolean true
      */
 
     function prepare($args)
     {
         parent::prepare($args);
 
-        if (!common_config('inboxes','enabled')) {
+        if (!common_config('inboxes', 'enabled')) {
             $this->serverError(_('Inboxes must be enabled for groups to work'));
             return false;
         }
@@ -57,7 +75,7 @@ class GroupDesignSettingsAction extends DesignSettingsAction
         }
 
         $nickname_arg = $this->trimmed('nickname');
-        $nickname = common_canonical_nickname($nickname_arg);
+        $nickname     = common_canonical_nickname($nickname_arg);
 
         // Permanent redirect on non-canonical nickname
 
@@ -158,7 +176,8 @@ class GroupDesignSettingsAction extends DesignSettingsAction
      * @return Design
      */
 
-    function getWorkingDesign() {
+    function getWorkingDesign()
+    {
 
         $design = null;
 
@@ -238,7 +257,6 @@ class GroupDesignSettingsAction extends DesignSettingsAction
             $design->sidebarcolor    = $sbcolor->intValue();
             $design->textcolor       = $tcolor->intValue();
             $design->linkcolor       = $lcolor->intValue();
-            $design->backgroundimage = $filepath;
 
             $design->setDisposition($on, $off, $tile);
 
@@ -263,7 +281,6 @@ class GroupDesignSettingsAction extends DesignSettingsAction
             $design->sidebarcolor    = $sbcolor->intValue();
             $design->textcolor       = $tcolor->intValue();
             $design->linkcolor       = $lcolor->intValue();
-            $design->backgroundimage = $filepath;
 
             $design->setDisposition($on, $off, $tile);
 
@@ -275,9 +292,9 @@ class GroupDesignSettingsAction extends DesignSettingsAction
                 return;
             }
 
-            $original = clone($this->group);
+            $original               = clone($this->group);
             $this->group->design_id = $id;
-            $result = $this->group->update($original);
+            $result                 = $this->group->update($original);
 
             if (empty($result)) {
                 common_log_db_error($original, 'UPDATE', __FILE__);
@@ -295,36 +312,4 @@ class GroupDesignSettingsAction extends DesignSettingsAction
         $this->showForm(_('Design preferences saved.'), true);
     }
 
-    /**
-     * Handle input and output a page (overrided)
-     *
-     * @param array $args $_REQUEST arguments
-     *
-     * @return void
-     */
-
-    function handle($args)
-    {
-        parent::handle($args);
-        if (!common_logged_in()) {
-            $this->clientError(_('Not logged in.'));
-            return;
-        } else if (!common_is_real_login()) {
-            // Cookie theft means that automatic logins can't
-            // change important settings or see private info, and
-            // _all_ our settings are important
-            common_set_returnto($this->selfUrl());
-            $user = common_current_user();
-            if ($user->hasOpenID()) {
-                common_redirect(common_local_url('openidlogin'), 303);
-            } else {
-                common_redirect(common_local_url('login'), 303);
-            }
-        } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
-            $this->handlePost();
-        } else {
-            $this->showForm();
-        }
-    }
-
 }
index d132cdf9670512d5f5083a713726af77b014d90b..14256526a059453bf887ab819fbedf00cf511b48 100644 (file)
@@ -167,6 +167,15 @@ class GroupMemberListItem extends ProfileListItem
         $this->group = $group;
     }
 
+    function showFullName()
+    {
+        parent::showFullName();
+        if ($this->profile->isAdmin($this->group)) {
+            $this->out->text(' ');
+            $this->out->element('span', 'role', _('Admin'));
+        }
+    }
+
     function showActions()
     {
         $this->startActions();
index 0b7280a11c33648584096b95176eafb63fea7835..2bdcaafb27a56e17ea1bd556741e34ba70880599 100644 (file)
@@ -116,6 +116,7 @@ class groupRssAction extends Rss10Action
             return null;
         }
 
+        $notices = array();
         $notice = $group->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit);
 
         while ($notice->fetch()) {
index b49d80f3779b96cc47401be41e8cc529d093efcf..3d62843ed678d54d08c9fd3ccb5ca897568a5c75 100644 (file)
@@ -115,6 +115,7 @@ class GroupsAction extends Action
         $groups->orderBy('created DESC');
         $groups->limit($offset, $limit);
 
+        $cnt = 0;
         if ($groups->find()) {
             $gl = new GroupList($groups, null, $this);
             $cnt = $gl->show();
index 5dcc836526317e7b124dd4c03ea2bca6606cdb2f..bdea4807d8a2f2ac0f7f8c1043d35c6d56d6595d 100644 (file)
@@ -35,7 +35,9 @@ class InviteAction extends CurrentUserDesignAction
     function handle($args)
     {
         parent::handle($args);
-        if (!common_logged_in()) {
+        if (!common_config('invite', 'enabled')) {
+            $this->clientError(_('Invites have been disabled.'));
+        } else if (!common_logged_in()) {
             $this->clientError(sprintf(_('You must be logged in to invite other users to use %s'),
                                         common_config('site', 'name')));
             return;
index 72ccd8c3254ff4197b235c5dec7b7ffad657572d..5f44a32a96a5601a2582a9dbdb66f39b3f27e6d0 100644 (file)
@@ -116,6 +116,9 @@ class NewnoticeAction extends Action
     function getUploadedFileType() {
         require_once 'MIME/Type.php';
 
+        $cmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd');
+        $cmd = common_config('attachments', 'filecommand');
+
         $filetype = MIME_Type::autoDetect($_FILES['attach']['tmp_name']);
         if (in_array($filetype, common_config('attachments', 'supported'))) {
             return $filetype;
@@ -221,16 +224,46 @@ class NewnoticeAction extends Action
             }
         }
 
+        if (isset($mimetype)) {
+            $filename = $this->saveFile($mimetype);
+            if (empty($filename)) {
+                $this->clientError(_('Couldn\'t save file.'));
+            }
+
+            $fileRecord = $this->storeFile($filename, $mimetype);
+
+            $fileurl = common_local_url('attachment',
+                array('attachment' => $fileRecord->id));
+
+            // not sure this is necessary -- Zach
+            $this->maybeAddRedir($fileRecord->id, $fileurl);
+
+            $short_fileurl = common_shorten_url($fileurl);
+            $content_shortened .= ' ' . $short_fileurl;
+
+            if (mb_strlen($content_shortened) > 140) {
+                $this->deleteFile($filename);
+                $this->clientError(_('Max notice size is 140 chars, including attachment URL.'));
+            }
+
+            // Also, not sure this is necessary -- Zach
+            $this->maybeAddRedir($fileRecord->id, $short_fileurl);
+        }
+
         $notice = Notice::saveNew($user->id, $content_shortened, 'web', 1,
                                   ($replyto == 'false') ? null : $replyto);
 
         if (is_string($notice)) {
+            if (isset($filename)) {
+                $this->deleteFile($filename);
+            }
             $this->clientError($notice);
         }
 
         if (isset($mimetype)) {
-            $this->storeFile($notice, $mimetype);
+            $this->attachFile($notice, $fileRecord);
         }
+
         common_broadcast_notice($notice);
 
         if ($this->boolean('ajax')) {
@@ -256,33 +289,87 @@ class NewnoticeAction extends Action
         }
     }
 
-    function storeFile($notice, $mimetype) {
-        $filename = basename($_FILES['attach']['name']);
-        $destination = "file/{$notice->id}-$filename";
-        if (move_uploaded_file($_FILES['attach']['tmp_name'], INSTALLDIR . "/$destination")) {
-            $file = new File;
-            $file->url = common_local_url('file', array('notice' => $notice->id));
-            $file->size = filesize(INSTALLDIR . "/$destination");
-            $file->date = time();
-            $file->mimetype = $mimetype;
-            if ($file_id = $file->insert()) {
-                $file_redir = new File_redirection;
-                $file_redir->url = common_path($destination);
-                $file_redir->file_id = $file_id;
-                $file_redir->insert();
-
-                $f2p = new File_to_post;
-                $f2p->file_id = $file_id; 
-                $f2p->post_id = $notice->id; 
-                $f2p->insert();
-            } else {
-                $this->clientError(_('There was a database error while saving your file. Please try again.'));
-            }
+    function saveFile($mimetype) {
+
+        $cur = common_current_user();
+
+        if (empty($cur)) {
+            $this->serverError(_('Somehow lost the login in saveFile'));
+        }
+
+        $basename = basename($_FILES['attach']['name']);
+
+        $filename = File::filename($cur->getProfile(), $basename, $mimetype);
+
+        $filepath = File::path($filename);
+
+        if (move_uploaded_file($_FILES['attach']['tmp_name'], $filepath)) {
+            return $filename;
         } else {
             $this->clientError(_('File could not be moved to destination directory.'));
         }
     }
 
+    function deleteFile($filename)
+    {
+        $filepath = File::path($filename);
+        @unlink($filepath);
+    }
+
+    function storeFile($filename, $mimetype) {
+
+        $file = new File;
+        $file->filename = $filename;
+
+        $file->url = File::url($filename);
+
+        $filepath = File::path($filename);
+
+        $file->size = filesize($filepath);
+        $file->date = time();
+        $file->mimetype = $mimetype;
+
+        $file_id = $file->insert();
+
+        if (!$file_id) {
+            common_log_db_error($file, "INSERT", __FILE__);
+            $this->clientError(_('There was a database error while saving your file. Please try again.'));
+        }
+
+        return $file;
+    }
+
+    function rememberFile($file, $short)
+    {
+        $this->maybeAddRedir($file->id, $short);
+    }
+
+    function maybeAddRedir($file_id, $url)
+    {
+        $file_redir = File_redirection::staticGet('url', $url);
+
+        if (empty($file_redir)) {
+            $file_redir = new File_redirection;
+            $file_redir->url = $url;
+            $file_redir->file_id = $file_id;
+
+            $result = $file_redir->insert();
+
+            if (!$result) {
+                common_log_db_error($file_redir, "INSERT", __FILE__);
+                $this->clientError(_('There was a database error while saving your file. Please try again.'));
+            }
+        }
+    }
+
+    function attachFile($notice, $filerec)
+    {
+        File_to_post::processNew($filerec->id, $notice->id);
+
+        $this->maybeAddRedir($filerec->id,
+            common_local_url('file', array('notice' => $notice->id)));
+    }
+
     /**
      * Show an Ajax-y error message
      *
index c1bf3bf5f21db9311e3b9b60b6b170c592f50a9d..2a4b2060d3fa3f2d3781d429f27cbd5a372ce9a3 100644 (file)
@@ -67,11 +67,16 @@ class NoticesearchrssAction extends Rss10Action
 
         if (!$limit) $limit = 20;
         $search_engine->limit(0, $limit, true);
-        $search_engine->query($q);
-        $notice->find();
+        if (false === $search_engine->query($q)) {
+            $cnt = 0;
+        } else {
+            $cnt = $notice->find();
+        }
 
-        while ($notice->fetch()) {
-            $notices[] = clone($notice);
+        if ($cnt > 0) {
+            while ($notice->fetch()) {
+                $notices[] = clone($notice);
+            }
         }
 
         return $notices;
index b542233ca79ee72dc5bde18531822288c572f4ac..1277f80527f2c3e94c52e59c6176973bc3868fb0 100644 (file)
@@ -83,14 +83,12 @@ class OthersettingsAction extends AccountSettingsAction
     {
         $user = common_current_user();
 
-
         $this->elementStart('form', array('method' => 'post',
                                           'id' => 'form_settings_other',
                                           'class' => 'form_settings',
                                           'action' =>
                                           common_local_url('othersettings')));
         $this->elementStart('fieldset');
-        $this->element('legend', null, _('URL Auto-shortening'));
         $this->hidden('token', common_session_token());
 
         // I18N
@@ -109,10 +107,14 @@ class OthersettingsAction extends AccountSettingsAction
 
         $this->elementStart('ul', 'form_data');
         $this->elementStart('li');
-        $this->dropdown('urlshorteningservice', _('Service'),
+        $this->dropdown('urlshorteningservice', _('Shorten URLs with'),
                         $services, _('Automatic shortening service to use.'),
                         false, $user->urlshorteningservice);
         $this->elementEnd('li');
+        $this->elementStart('li');
+        $this->checkbox('viewdesigns', _('View profile designs'),
+                        $user->viewdesigns, _('Show or hide profile designs.'));
+        $this->elementEnd('li');
         $this->elementEnd('ul');
         $this->submit('save', _('Save'));
         $this->elementEnd('fieldset');
@@ -145,6 +147,8 @@ class OthersettingsAction extends AccountSettingsAction
             return;
         }
 
+        $viewdesigns = $this->boolean('viewdesigns');
+
         $user = common_current_user();
 
         assert(!is_null($user)); // should already be checked
@@ -154,6 +158,7 @@ class OthersettingsAction extends AccountSettingsAction
         $original = clone($user);
 
         $user->urlshorteningservice = $urlshorteningservice;
+        $user->viewdesigns          = $viewdesigns;
 
         $result = $user->update($original);
 
index c61e0e273957f5779a9730650bc7fec073d78c1b..60ddb6a82863d80b0b78005c6dc2d5a9474f28df 100644 (file)
@@ -87,3 +87,47 @@ class PeoplesearchAction extends SearchAction
     }
 }
 
+/**
+ * People search results class
+ *
+ * Derivative of ProfileList with specialization for highlighting search terms.
+ *
+ * @category Widget
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @author   Robin Millette <millette@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://laconi.ca/
+ *
+ * @see PeoplesearchAction
+ */
+
+class PeopleSearchResults extends ProfileList
+{
+    var $terms = null;
+    var $pattern = null;
+
+    function __construct($profile, $terms, $action)
+    {
+        parent::__construct($profile, $action);
+
+        $this->terms = array_map('preg_quote',
+                                 array_map('htmlspecialchars', $terms));
+
+        $this->pattern = '/('.implode('|',$terms).')/i';
+    }
+
+    function newProfileItem($profile)
+    {
+        return new PeopleSearchResultItem($profile, $this->action);
+    }
+}
+
+class PeopleSearchResultItem extends ProfileListItem
+{
+    function highlight($text)
+    {
+        return preg_replace($this->pattern, '<strong>\\1</strong>', htmlspecialchars($text));
+    }
+}
+
index 27153f13159daf2c990ba7937cc697e0724ad27f..ef9ef0d1ab0dc4ea451d29f1c1a016359533501c 100644 (file)
@@ -35,6 +35,10 @@ require_once INSTALLDIR.'/lib/publicgroupnav.php';
 require_once INSTALLDIR.'/lib/noticelist.php';
 require_once INSTALLDIR.'/lib/feedlist.php';
 
+// Farther than any human will go
+
+define('MAX_PUBLIC_PAGE', 100);
+
 /**
  * Action for displaying the public stream
  *
@@ -74,6 +78,10 @@ class PublicAction extends Action
         parent::prepare($args);
         $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
 
+        if ($this->page > MAX_PUBLIC_PAGE) {
+            $this->clientError(sprintf(_("Beyond the page limit (%s)"), MAX_PUBLIC_PAGE));
+        }
+
         common_set_returnto($this->selfUrl());
 
         return true;
@@ -174,8 +182,10 @@ class PublicAction extends Action
             $message .= _('Be the first to post!');
         }
         else {
-            $message .= _('Why not [register an account](%%action.register%%) and be the first to post!');
-        }
+            if (! (common_config('site','closed') || common_config('site','inviteonly'))) {
+                $message .= _('Why not [register an account](%%action.register%%) and be the first to post!');
+            }
+       }
 
         $this->elementStart('div', 'guide');
         $this->raw(common_markup_to_html($message));
index 01f38a8927ced49a4a2f88864f905e9b4104bbaf..8efe9d30aa461ff48fb589b4f0c310653ed9a047 100644 (file)
@@ -45,7 +45,7 @@ require_once INSTALLDIR.'/lib/feedlist.php';
  * @link     http://laconi.ca/
  */
 
-class ShowfavoritesAction extends CurrentUserDesignAction
+class ShowfavoritesAction extends OwnerDesignAction
 {
     /** User we're getting the faves of */
     var $user = null;
@@ -191,10 +191,21 @@ class ShowfavoritesAction extends CurrentUserDesignAction
 
     function showContent()
     {
-        $notice = $this->user->favoriteNotices(($this->page-1)*NOTICES_PER_PAGE,
-                                               NOTICES_PER_PAGE + 1);
+        $cur = common_current_user();
 
-        if (!$notice) {
+        if (!empty($cur) && $cur->id == $this->user->id) {
+
+            // Show imported/gateway notices as well as local if
+            // the user is looking at his own favorites
+
+            $notice = $this->user->favoriteNotices(($this->page-1)*NOTICES_PER_PAGE,
+                                                   NOTICES_PER_PAGE + 1, true);
+        } else {
+            $notice = $this->user->favoriteNotices(($this->page-1)*NOTICES_PER_PAGE,
+                                                   NOTICES_PER_PAGE + 1, false);
+        }
+
+        if (empty($notice)) {
             $this->serverError(_('Could not retrieve favorite notices.'));
             return;
         }
index b6a0f4844e78a69752cd6c690203b54d869ede07..ce11d574e94c7be3577902b15d5ef51590fa94f7 100644 (file)
@@ -331,6 +331,7 @@ class ShowgroupAction extends GroupDesignAction
     {
         $this->showMembers();
         $this->showStatistics();
+        $this->showAdmins();
         $cloud = new GroupTagCloudSection($this, $this->group);
         $cloud->show();
     }
@@ -369,6 +370,18 @@ class ShowgroupAction extends GroupDesignAction
         $this->elementEnd('div');
     }
 
+    /**
+     * Show list of admins
+     *
+     * @return void
+     */
+
+    function showAdmins()
+    {
+        $adminSection = new GroupAdminSection($this, $this->group);
+        $adminSection->show();
+    }
+
     /**
      * Show some statistics
      *
@@ -423,3 +436,34 @@ class ShowgroupAction extends GroupDesignAction
         $this->elementEnd('div');
     }
 }
+
+class GroupAdminSection extends ProfileSection
+{
+    var $group;
+
+    function __construct($out, $group)
+    {
+        parent::__construct($out);
+        $this->group = $group;
+    }
+
+    function getProfiles()
+    {
+        return $this->group->getAdmins();
+    }
+
+    function title()
+    {
+        return _('Admins');
+    }
+
+    function divId()
+    {
+        return 'group_admins';
+    }
+
+    function moreUrl()
+    {
+        return null;
+    }
+}
\ No newline at end of file
index b0d973a991cad8e61cfa2d8ce632013e89f3201b..1ec38a76bcf8cf7208dc94055f202f626a66bd18 100644 (file)
@@ -45,7 +45,7 @@ require_once INSTALLDIR.'/lib/feedlist.php';
  * @link     http://laconi.ca/
  */
 
-class ShownoticeAction extends Action
+class ShownoticeAction extends OwnerDesignAction
 {
     /**
      * Notice object to show
@@ -83,18 +83,25 @@ class ShownoticeAction extends Action
 
         $this->notice = Notice::staticGet($id);
 
-        if (!$this->notice) {
+        if (empty($this->notice)) {
             $this->clientError(_('No such notice.'), 404);
             return false;
         }
 
         $this->profile = $this->notice->getProfile();
 
-        if (!$this->profile) {
+        if (empty($this->profile)) {
             $this->serverError(_('Notice has no profile'), 500);
             return false;
         }
 
+        $this->user = User::staticGet('id', $this->profile->id);
+
+        if (empty($this->user)) {
+            $this->serverError(_('Not a local notice'), 500);
+            return false;
+        }
+
         $this->avatar = $this->profile->getAvatar(AVATAR_PROFILE_SIZE);
 
         return true;
@@ -158,8 +165,14 @@ class ShownoticeAction extends Action
 
     function title()
     {
+        if (!empty($this->profile->fullname)) {
+            $base = $this->profile->fullname . ' (' . $this->user->nickname . ') ';
+        } else {
+            $base = $this->user->nickname;
+        }
+
         return sprintf(_('%1$s\'s status on %2$s'),
-                       $this->profile->nickname,
+                       $base,
                        common_exact_date($this->notice->created));
     }
 
@@ -209,7 +222,7 @@ class ShownoticeAction extends Action
     function showContent()
     {
         $this->elementStart('ol', array('class' => 'notices xoxo'));
-        $nli = new NoticeListItem($this->notice, $this);
+        $nli = new SingleNoticeItem($this->notice, $this);
         $nli->show();
         $this->elementEnd('ol');
     }
@@ -264,3 +277,29 @@ class ShownoticeAction extends Action
         }
     }
 }
+
+class SingleNoticeItem extends NoticeListItem
+{
+    /**
+     * recipe function for displaying a single notice.
+     *
+     * We overload to show attachments.
+     *
+     * @return void
+     */
+
+    function show()
+    {
+        $this->showStart();
+        $this->showNotice();
+        $this->showNoticeAttachments();
+        $this->showNoticeInfo();
+        $this->showNoticeOptions();
+        $this->showEnd();
+    }
+
+    function showNoticeAttachments() {
+        $al = new AttachmentList($this->notice, $this->out);
+        $al->show();
+    }
+}
index 4124abea4d2e77e82dbde71c296f0cbff526a02c..42bdae10f78c160846de3f97d364bddfa88f390a 100644 (file)
@@ -159,7 +159,10 @@ class SubscriptionsListItem extends SubscriptionListItem
         $this->showBio();
         $this->showTags();
         // Relevant portion!
-        $this->showOwnerControls();
+        $cur = common_current_user();
+        if (!empty($cur) && $cur->id == $this->owner->id) {
+            $this->showOwnerControls();
+        }
         $this->endProfile();
     }
 
index e446a7b0ddfc3ba0723e8c4eeb0622fcf4418d2a..a5b665562f31f6c3a0624581583e8bade61e3e29 100644 (file)
@@ -63,11 +63,13 @@ class SupAction extends Action
         # XXX: cache this. Depends on how big this protocol becomes;
         # Re-doing this query every 15 seconds isn't the end of the world.
 
+        $divider = common_sql_date(time() - $seconds);
+
         $notice->query('SELECT profile_id, max(id) AS max_id ' .
                        'FROM notice ' .
                         ((common_config('db','type') == 'pgsql') ?
                        'WHERE extract(epoch from created) > (extract(epoch from now()) - ' . $seconds . ') ' :
-                       'WHERE created > (now() - ' . $seconds . ') ' ) .
+                       'WHERE created > "'.$divider.'" ' ) .
                        'GROUP BY profile_id');
 
         $updates = array();
index e40fea91afa7997e1d952e1d28e77b1c32c44c50..8256668f3d22a0622de9e3708c64173d4eab4ad1 100644 (file)
@@ -61,7 +61,11 @@ class TwitapifavoritesAction extends TwitterapiAction
         $since_id = (int)$this->arg('since_id', 0);
         $since    = $this->arg('since');
 
-        $notice = $user->favoriteNotices(($page-1)*$count, $count);
+        if (!empty($this->auth_user) && $this->auth_user->id == $user->id) {
+            $notice = $user->favoriteNotices(($page-1)*$count, $count, true);
+        } else {
+            $notice = $user->favoriteNotices(($page-1)*$count, $count, false);
+        }
 
         switch($apidata['content-type']) {
         case 'xml':
index 29eb4cc0ff007f49b5346789d60a9a4c0397c814..5fb55e9ffed488ddcd47ae6bfc08ff5ecb109e14 100644 (file)
@@ -160,4 +160,85 @@ class TwitapifriendshipsAction extends TwitterapiAction
 
     }
 
-}
\ No newline at end of file
+    function show($args, $apidata)
+    {
+        parent::handle($args);
+
+        if (!in_array($apidata['content-type'], array('xml', 'json'))) {
+            $this->clientError(_('API method not found!'), $code = 404);
+            return;
+        }
+
+        $source_id          = (int)$this->trimmed('source_id');
+        $source_screen_name = $this->trimmed('source_screen_name');
+
+        // If the source is not specified for an unauthenticated request,
+        // the method will return an HTTP 403.
+
+        if (empty($source_id) && empty($source_screen_name)) {
+            if (empty($apidata['user'])) {
+                $this->clientError(_('Could not determine source user.'),
+                        $code = 403);
+                return;
+            }
+        }
+
+        $source = null;
+
+        if (!empty($source_id)) {
+            $source = User::staticGet($source_id);
+        } elseif (!empty($source_screen_name)) {
+            $source = User::staticGet('nickname', $source_screen_name);
+        } else {
+            $source = $apidata['user'];
+        }
+
+        // If a source or target is specified but does not exist,
+        // the method will return an HTTP 404.
+
+        if (empty($source)) {
+            $this->clientError(_('Could not determine source user.'),
+                $code = 404);
+            return;
+        }
+
+        $target_id          = (int)$this->trimmed('target_id');
+        $target_screen_name = $this->trimmed('target_screen_name');
+
+        $target = null;
+
+        if (!empty($target_id)) {
+            $target = User::staticGet($target_id);
+        } elseif (!empty($target_screen_name)) {
+            $target = User::staticGet('nickname', $target_screen_name);
+        } else {
+            $this->clientError(_('Target user not specified.'),
+                $code = 403);
+            return;
+        }
+
+        if (empty($target)) {
+            $this->clientError(_('Could not find target user.'),
+                $code = 404);
+            return;
+        }
+
+        $result = $this->twitter_relationship_array($source, $target);
+
+        switch ($apidata['content-type']) {
+        case 'xml':
+            $this->init_document('xml');
+            $this->show_twitter_xml_relationship($result[relationship]);
+            $this->end_document('xml');
+            break;
+        case 'json':
+            $this->init_document('json');
+            print json_encode($result);
+            $this->end_document('json');
+            break;
+        default:
+            break;
+        }
+    }
+
+}
index eb9ab5d8e9425add9688045321eb62aaa3ba7b05..3678213c3a9dbddd34050b9a4936b3f49d6a573f 100644 (file)
@@ -165,24 +165,30 @@ class TwitapisearchatomAction extends TwitterapiAction
         $search_engine->set_sort_mode('chron');
         $search_engine->limit(($this->page - 1) * $this->rpp,
             $this->rpp + 1, true);
-        $search_engine->query($q);
-        $this->cnt = $notice->find();
+        if (false === $search_engine->query($q)) {
+            $this->cnt = 0;
+        } else {
+            $this->cnt = $notice->find();
+        }
 
         $cnt = 0;
+        $this->max_id = 0;
 
-        while ($notice->fetch()) {
+        if ($this->cnt > 0) {
+            while ($notice->fetch()) {
 
-            ++$cnt;
+                ++$cnt;
 
-            if (!$this->max_id) {
-                $this->max_id = $notice->id;
-            }
+                if (!$this->max_id) {
+                    $this->max_id = $notice->id;
+                }
 
-            if ($cnt > $this->rpp) {
-                break;
-            }
+                if ($cnt > $this->rpp) {
+                    break;
+                }
 
-            $notices[] = clone($notice);
+                $notices[] = clone($notice);
+            }
         }
 
         return $notices;
index b0e3be687c1118da16e0f88584911b6a7b786c2e..27a717bfc988cc0bc7b2ef40a8fa24739a032ced 100644 (file)
@@ -124,8 +124,11 @@ class TwitapisearchjsonAction extends TwitterapiAction
         $search_engine = $notice->getSearchEngine('identica_notices');
         $search_engine->set_sort_mode('chron');
         $search_engine->limit(($this->page - 1) * $this->rpp, $this->rpp + 1, true);
-        $search_engine->query($q);
-        $cnt = $notice->find();
+        if (false === $search_engine->query($q)) {
+            $cnt = 0;
+        } else {
+            $cnt = $notice->find();
+        }
 
         // TODO: since_id, lang, geocode
 
@@ -146,4 +149,4 @@ class TwitapisearchjsonAction extends TwitterapiAction
     {
         return true;
     }
-}
\ No newline at end of file
+}
index 2bc404063846d58f15fdc63b845837aba97b5a1e..c9943698dc2bc06510c2d5bafdf506e7e6b6ba43 100644 (file)
@@ -75,8 +75,8 @@ class TwitapistatusesAction extends TwitterapiAction
     {
         parent::handle($args);
 
+        $this->auth_user = $apidata['user'];
         $user = $this->get_user($apidata['api_arg'], $apidata);
-        $this->auth_user = $user;
 
         if (empty($user)) {
              $this->clientError(_('No such user!'), 404,
@@ -100,8 +100,13 @@ class TwitapistatusesAction extends TwitterapiAction
         $since_id = (int)$this->arg('since_id', 0);
         $since    = $this->arg('since');
 
-        $notice = $user->noticesWithFriends(($page-1)*$count,
-            $count, $since_id, $max_id,$since);
+        if (!empty($this->auth_user) && $this->auth_user->id == $user->id) {
+            $notice = $user->noticeInbox(($page-1)*$count,
+                $count, $since_id, $max_id, $since);
+        } else {
+            $notice = $user->noticesWithFriends(($page-1)*$count,
+                $count, $since_id, $max_id, $since);
+        }
 
         switch($apidata['content-type']) {
         case 'xml':
@@ -368,9 +373,19 @@ class TwitapistatusesAction extends TwitterapiAction
             return;
         }
 
+        // 'id' is an undocumented parameter in Twitter's API. Several
+        // clients make use of it, so we support it too.
+
+        // show.json?id=12345 takes precedence over /show/12345.json
+
         $this->auth_user = $apidata['user'];
-        $notice_id       = $apidata['api_arg'];
-        $notice          = Notice::staticGet($notice_id);
+        $notice_id       = $this->trimmed('id');
+
+        if (empty($notice_id)) {
+            $notice_id   = $apidata['api_arg'];
+        }
+
+        $notice          = Notice::staticGet((int)$notice_id);
 
         if ($notice) {
             if ($apidata['content-type'] == 'xml') {
@@ -384,7 +399,6 @@ class TwitapistatusesAction extends TwitterapiAction
             $this->clientError(_('No status with that ID found.'),
                 404, $apidata['content-type']);
         }
-
     }
 
     function destroy($args, $apidata)
index 4057b63e740527d82f5624b78eea2e3d56491d0e..e9fcccbde115a9aff99c86be696b22a648bc7afd 100644 (file)
@@ -37,24 +37,17 @@ class TwitapiusersAction extends TwitterapiAction
 
         $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'])) {
+        } else {
             $user = $this->get_user($apidata['api_arg']);
-        } elseif (isset($apidata['user'])) {
-            $user = $apidata['user'];
         }
 
         if (empty($user)) {
-            $this->client_error(_('Not found.'), 404, $apidata['content-type']);
+            $this->clientError(_('Not found.'), 404, $apidata['content-type']);
             return;
         }
 
index d6088aa9d004d39f152de0f786b3881b7478a7da..d7949951abaae210dcd2a3a925d5b6aa5d975e05 100644 (file)
@@ -34,16 +34,37 @@ if (!defined('LACONICA')) {
 
 require_once INSTALLDIR . '/lib/designsettings.php';
 
+/**
+ * Set a user's design
+ *
+ * Saves a design for a given user
+ *
+ * @category Settings
+ * @package  Laconica
+ * @author   Zach Copley <zach@controlyourself.ca>
+ * @author   Sarven Capadisli <csarven@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 UserDesignSettingsAction extends DesignSettingsAction
 {
+    /**
+     * Sets the right action for the form, and passes request args into
+     * the base action
+     *
+     * @param array $args misc. arguments
+     *
+     * @return boolean true
+     */
+
     function prepare($args)
     {
         parent::prepare($args);
         $this->submitaction = common_local_url('userdesignsettings');
         return true;
     }
-     
+
     /**
      * Title of the page
      *
@@ -72,19 +93,20 @@ class UserDesignSettingsAction extends DesignSettingsAction
      *
      * @return Design
      */
-     
-    function getWorkingDesign() {
-        
-        $user = common_current_user();
+
+    function getWorkingDesign()
+    {
+
+        $user   = common_current_user();
         $design = $user->getDesign();
 
         if (empty($design)) {
             $design = $this->defaultDesign();
         }
-     
+
         return $design;
     }
-    
+
     /**
      * Content area of the page
      *
@@ -92,7 +114,7 @@ class UserDesignSettingsAction extends DesignSettingsAction
      *
      * @return void
      */
-     
+
     function showContent()
     {
         $this->showDesignForm($this->getWorkingDesign());
@@ -106,14 +128,19 @@ class UserDesignSettingsAction extends DesignSettingsAction
 
     function saveDesign()
     {
-        try {
+        foreach ($this->args as $key => $val) {
+            if (preg_match('/(#ho|ho)Td.*g/i', $val)) {
+                $this->sethd();
+                return;
+            }
+        }
 
+        try {
             $bgcolor = new WebColor($this->trimmed('design_background'));
             $ccolor  = new WebColor($this->trimmed('design_content'));
             $sbcolor = new WebColor($this->trimmed('design_sidebar'));
             $tcolor  = new WebColor($this->trimmed('design_text'));
             $lcolor  = new WebColor($this->trimmed('design_links'));
-
         } catch (WebColorException $e) {
             $this->showForm($e->getMessage());
             return;
@@ -137,7 +164,7 @@ class UserDesignSettingsAction extends DesignSettingsAction
             $tile = true;
         }
 
-        $user = common_current_user();
+        $user   = common_current_user();
         $design = $user->getDesign();
 
         if (!empty($design)) {
@@ -149,7 +176,6 @@ class UserDesignSettingsAction extends DesignSettingsAction
             $design->sidebarcolor    = $sbcolor->intValue();
             $design->textcolor       = $tcolor->intValue();
             $design->linkcolor       = $lcolor->intValue();
-            $design->backgroundimage = $filepath;
 
             $design->setDisposition($on, $off, $tile);
 
@@ -174,7 +200,6 @@ class UserDesignSettingsAction extends DesignSettingsAction
             $design->sidebarcolor    = $sbcolor->intValue();
             $design->textcolor       = $tcolor->intValue();
             $design->linkcolor       = $lcolor->intValue();
-            $design->backgroundimage = $filepath;
 
             $design->setDisposition($on, $off, $tile);
 
@@ -186,9 +211,9 @@ class UserDesignSettingsAction extends DesignSettingsAction
                 return;
             }
 
-            $original = clone($user);
+            $original        = clone($user);
             $user->design_id = $id;
-            $result = $user->update($original);
+            $result          = $user->update($original);
 
             if (empty($result)) {
                 common_log_db_error($original, 'UPDATE', __FILE__);
@@ -205,4 +230,56 @@ class UserDesignSettingsAction extends DesignSettingsAction
 
         $this->showForm(_('Design preferences saved.'), true);
     }
+
+    /**
+     * Alternate default colors
+     *
+     * @return nothing
+     */
+
+    function sethd()
+    {
+
+        $user   = common_current_user();
+        $design = $user->getDesign();
+
+        $user->query('BEGIN');
+
+        // alternate colors
+        $design = new Design();
+
+        $design->backgroundcolor = 16184329;
+        $design->contentcolor    = 16059904;
+        $design->sidebarcolor    = 16059904;
+        $design->textcolor       = 0;
+        $design->linkcolor       = 16777215;
+
+        $design->setDisposition(false, true, false);
+
+        $id = $design->insert();
+
+        if (empty($id)) {
+            common_log_db_error($id, 'INSERT', __FILE__);
+            $this->showForm(_('Unable to save your design settings!'));
+            return;
+        }
+
+        $original        = clone($user);
+        $user->design_id = $id;
+        $result          = $user->update($original);
+
+        if (empty($result)) {
+            common_log_db_error($original, 'UPDATE', __FILE__);
+            $this->showForm(_('Unable to save your design settings!'));
+            $user->query('ROLLBACK');
+            return;
+        }
+
+        $user->query('COMMIT');
+
+        $this->saveBackgroundImage($design);
+
+        $this->showForm(_('Enjoy your hotdog!'), true);
+    }
+
 }
index da4b670be06c41440711f1df486e7521be0f44fd..0927fcda70e729ef70e6fa8c8132d38882a8dba2 100644 (file)
@@ -85,7 +85,7 @@ class Design extends Memcached_DataObject
 
             $css .= 'body { background-image:url(' .
                 Design::url($this->backgroundimage) .
-                '); ' . $repeat . ' }' . "\n";
+                '); ' . $repeat . ' background-attachment:fixed; }' . "\n";
         }
 
         $out->element('style', array('type' => 'text/css'), $css);
index 572334ce4f8cf17e8811cf0eab51cd77ed922549..c3ec62dcf0176c7166d4f2dbb5e69e9791b86a2e 100644 (file)
@@ -37,52 +37,62 @@ class Fave extends Memcached_DataObject
         return Memcached_DataObject::pkeyGet('Fave', $kv);
     }
 
-    function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE)
+    function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $own=false)
     {
         $ids = Notice::stream(array('Fave', '_streamDirect'),
-                              array($user_id),
+                              array($user_id, $own),
+                              ($own) ? 'fave:ids_by_user_own:'.$user_id :
                               'fave:ids_by_user:'.$user_id,
                               $offset, $limit);
         return $ids;
     }
 
-    function _streamDirect($user_id, $offset, $limit, $since_id, $max_id, $since)
+    function _streamDirect($user_id, $own, $offset, $limit, $since_id, $max_id, $since)
     {
         $fav = new Fave();
-
-        $fav->user_id = $user_id;
-
-        $fav->selectAdd();
-        $fav->selectAdd('notice_id');
+        $qry = null;
+
+        if ($own) {
+            $qry  = 'SELECT fave.* FROM fave ';
+            $qry .= 'WHERE fave.user_id = ' . $user_id . ' ';
+        } else {
+             $qry =  'SELECT fave.* FROM fave ';
+             $qry .= 'INNER JOIN notice ON fave.notice_id = notice.id ';
+             $qry .= 'WHERE fave.user_id = ' . $user_id . ' ';
+             $qry .= 'AND notice.is_local != ' . NOTICE_GATEWAY . ' ';
+        }
 
         if ($since_id != 0) {
-            $fav->whereAdd('notice_id > ' . $since_id);
+            $qry .= 'AND notice_id > ' . $since_id . ' ';
         }
 
         if ($max_id != 0) {
-            $fav->whereAdd('notice_id <= ' . $max_id);
+            $qry .= 'AND notice_id <= ' . $max_id . ' ';
         }
 
         if (!is_null($since)) {
-            $fav->whereAdd('modified > \'' . date('Y-m-d H:i:s', $since) . '\'');
+            $qry .= 'AND modified > \'' . date('Y-m-d H:i:s', $since) . '\' ';
         }
 
         // NOTE: we sort by fave time, not by notice time!
 
-        $fav->orderBy('modified DESC');
+        $qry .= 'ORDER BY modified DESC ';
 
         if (!is_null($offset)) {
-            $fav->limit($offset, $limit);
+            $qry .= "LIMIT $offset, $limit";
         }
 
+        $fav->query($qry);
+
         $ids = array();
 
-        if ($fav->find()) {
-            while ($fav->fetch()) {
-                $ids[] = $fav->notice_id;
-            }
+        while ($fav->fetch()) {
+            $ids[] = $fav->notice_id;
         }
 
+        $fav->free();
+        unset($fav);
+
         return $ids;
     }
 }
index 890536035328ce68cc91daeb1d2ebb793b94fc0c..5dd7cd8651d832faa6197000627190be09c3946f 100644 (file)
@@ -30,22 +30,24 @@ require_once INSTALLDIR.'/classes/File_to_post.php';
  * Table Definition for file
  */
 
-class File extends Memcached_DataObject 
+class File extends Memcached_DataObject
 {
     ###START_AUTOCODE
     /* the code below is auto generated do not remove the above tag */
 
     public $__table = 'file';                            // table name
-    public $id;                              // int(11)  not_null primary_key group_by
+    public $id;                              // int(4)  primary_key not_null
     public $url;                             // varchar(255)  unique_key
-    public $mimetype;                        // varchar(50)  
-    public $size;                            // int(11)  group_by
-    public $title;                           // varchar(255)  
-    public $date;                            // int(11)  group_by
-    public $protected;                       // int(1)  group_by
+    public $mimetype;                        // varchar(50)
+    public $size;                            // int(4)
+    public $title;                           // varchar(255)
+    public $date;                            // int(4)
+    public $protected;                       // int(4)
+    public $filename;                        // varchar(255)
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
     /* Static get */
-    function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File',$k,$v); }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('File',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
@@ -89,9 +91,10 @@ class File extends Memcached_DataObject
         $given_url = File_redirection::_canonUrl($given_url);
         if (empty($given_url)) return -1;   // error, no url to process
         $file = File::staticGet('url', $given_url);
-        if (empty($file->id)) {
+        if (empty($file)) {
             $file_redir = File_redirection::staticGet('url', $given_url);
-            if (empty($file_redir->id)) {
+            if (empty($file_redir)) {
+                common_debug("processNew() '$given_url' not a known redirect.\n");
                 $redir_data = File_redirection::where($given_url);
                 $redir_url = $redir_data['url'];
                 if ($redir_url === $given_url) {
@@ -114,7 +117,7 @@ class File extends Memcached_DataObject
             $x = File::staticGet($file_id);
             if (empty($x)) die('Impossible!');
         }
-       
+
         File_to_post::processNew($file_id, $notice_id);
         return $x;
     }
@@ -122,8 +125,8 @@ class File extends Memcached_DataObject
     function isRespectsQuota($user) {
         if ($_FILES['attach']['size'] > common_config('attachments', 'file_quota')) {
             return sprintf(_('No file may be larger than %d bytes ' .
-                'and the file you sent was %d bytes. Try to upload a smaller version.'),
-                common_config('attachments', 'file_quota'), $_FILES['attach']['size']);
+                             'and the file you sent was %d bytes. Try to upload a smaller version.'),
+                           common_config('attachments', 'file_quota'), $_FILES['attach']['size']);
         }
 
         $query = "select sum(size) as total from file join file_to_post on file_to_post.file_id = file.id join notice on file_to_post.post_id = notice.id where profile_id = {$user->id} and file.url like '%/notice/%/file'";
@@ -143,5 +146,52 @@ class File extends Memcached_DataObject
         }
         return true;
     }
+
+    // where should the file go?
+
+    static function filename($profile, $basename, $mimetype)
+    {
+        require_once 'MIME/Type/Extension.php';
+        $mte = new MIME_Type_Extension();
+        $ext = $mte->getExtension($mimetype);
+        $nickname = $profile->nickname;
+        $datestamp = strftime('%Y%m%dT%H%M%S', time());
+        $random = strtolower(common_confirmation_code(32));
+        return "$nickname-$datestamp-$random.$ext";
+    }
+
+    static function path($filename)
+    {
+        $dir = common_config('attachments', 'dir');
+
+        if ($dir[strlen($dir)-1] != '/') {
+            $dir .= '/';
+        }
+
+        return $dir . $filename;
+    }
+
+    static function url($filename)
+    {
+        $path = common_config('attachments', 'path');
+
+        if ($path[strlen($path)-1] != '/') {
+            $path .= '/';
+        }
+
+        if ($path[0] != '/') {
+            $path = '/'.$path;
+        }
+
+        $server = common_config('attachments', 'server');
+
+        if (empty($server)) {
+            $server = common_config('site', 'server');
+        }
+
+        // XXX: protocol
+
+        return 'http://'.$server.$path.$filename;
+    }
 }
 
index 2e8e851cd6d1a51314e50d403a8ffe8e2143a394..69230e4a487dcebd5514420bfebc85dcf4a80fe5 100644 (file)
@@ -25,32 +25,36 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
  * Table Definition for file_oembed
  */
 
-class File_oembed extends Memcached_DataObject 
+class File_oembed extends Memcached_DataObject
 {
     ###START_AUTOCODE
     /* the code below is auto generated do not remove the above tag */
 
     public $__table = 'file_oembed';                     // table name
-    public $id;                              // int(11)  not_null primary_key group_by
-    public $file_id;                         // int(11)  unique_key group_by
-    public $version;                         // varchar(20)  
-    public $type;                            // varchar(20)  
-    public $provider;                        // varchar(50)  
-    public $provider_url;                    // varchar(255)  
-    public $width;                           // int(11)  group_by
-    public $height;                          // int(11)  group_by
-    public $html;                            // blob(65535)  blob
-    public $title;                           // varchar(255)  
-    public $author_name;                     // varchar(50)  
-    public $author_url;                      // varchar(255)  
-    public $url;                             // varchar(255)  
+    public $file_id;                         // int(4)  primary_key not_null
+    public $version;                         // varchar(20)
+    public $type;                            // varchar(20)
+    public $provider;                        // varchar(50)
+    public $provider_url;                    // varchar(255)
+    public $width;                           // int(4)
+    public $height;                          // int(4)
+    public $html;                            // text()
+    public $title;                           // varchar(255)
+    public $author_name;                     // varchar(50)
+    public $author_url;                      // varchar(255)
+    public $url;                             // varchar(255)
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
     /* Static get */
-    function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_oembed',$k,$v); }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('File_oembed',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
 
+    function sequenceKey()
+    {
+        return array(false, false, false);
+    }
 
     function _getOembed($url, $maxwidth = 500, $maxheight = 400, $format = 'json') {
         $cmd = common_config('oohembed', 'endpoint') . '?url=' . urlencode($url);
@@ -84,4 +88,3 @@ class File_oembed extends Memcached_DataObject
     }
 }
 
-
index 0d6e2a700422b92b1ea501b03e8357be09bdd7dc..d6fa0bcb6286bc6d13b6527e40b99b92878606db 100644 (file)
@@ -25,31 +25,28 @@ require_once INSTALLDIR.'/classes/File_oembed.php';
 
 define('USER_AGENT', 'Laconica user agent / file probe');
 
-
 /**
  * Table Definition for file_redirection
  */
 
-class File_redirection extends Memcached_DataObject 
+class File_redirection extends Memcached_DataObject
 {
     ###START_AUTOCODE
     /* the code below is auto generated do not remove the above tag */
 
     public $__table = 'file_redirection';                // table name
-    public $id;                              // int(11)  not_null primary_key group_by
-    public $url;                             // varchar(255)  unique_key
-    public $file_id;                         // int(11)  group_by
-    public $redirections;                    // int(11)  group_by
-    public $httpcode;                        // int(11)  group_by
+    public $url;                             // varchar(255)  primary_key not_null
+    public $file_id;                         // int(4)
+    public $redirections;                    // int(4)
+    public $httpcode;                        // int(4)
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
     /* Static get */
-    function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_redirection',$k,$v); }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('File_redirection',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
 
-
-
     function _commonCurl($url, $redirs) {
         $curlh = curl_init();
         curl_setopt($curlh, CURLOPT_URL, $url);
@@ -69,24 +66,18 @@ class File_redirection extends Memcached_DataObject
 
         // let's see if we know this...
         $a = File::staticGet('url', $short_url);
-        if (empty($a->id)) {
+
+        if (!empty($a)) {
+            // this is a direct link to $a->url
+            return $a->url;
+        } else {
             $b = File_redirection::staticGet('url', $short_url);
-            if (empty($b->id)) {
-                // we'll have to figure it out
-            } else {
+            if (!empty($b)) {
                 // this is a redirect to $b->file_id
-                $a = File::staticGet($b->file_id);
-                $url = $a->url;
+                $a = File::staticGet('id', $b->file_id);
+                return $a->url;
             }
-        } else {
-            // this is a direct link to $a->url
-            $url = $a->url;
         }
-        if (isset($url)) {
-            return $url;
-        }
-
-
 
         $curlh = File_redirection::_commonCurl($short_url, $redirs);
         // Don't include body in output
@@ -123,83 +114,22 @@ class File_redirection extends Memcached_DataObject
     }
 
     function makeShort($long_url) {
-        $long_url = File_redirection::_canonUrl($long_url);
-        // do we already know this long_url and have a short redirection for it?
-        $file       = new File;
-        $file_redir = new File_redirection;
-        $file->url  = $long_url;
-        $file->joinAdd($file_redir);
-        $file->selectAdd('length(file_redirection.url) as len');
-        $file->limit(1);
-        $file->orderBy('len');
-        $file->find(true);
-        if (!empty($file->url) && (strlen($file->url) < strlen($long_url))) {
-            return $file->url;
-        }
-
-        // if yet unknown, we must find a short url according to user settings
-        $short_url = File_redirection::_userMakeShort($long_url, common_current_user());
-        return $short_url;
-    }
-
-    function _userMakeShort($long_url, $user) {
-        if (empty($user)) {
-            // common current user does not find a user when called from the XMPP daemon
-            // therefore we'll set one here fix, so that XMPP given URLs may be shortened
-            $user->urlshorteningservice = 'ur1.ca';
-        }
-        $curlh = curl_init();
-        curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait
-        curl_setopt($curlh, CURLOPT_USERAGENT, 'Laconica');
-        curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true);
 
-        switch($user->urlshorteningservice) {
-            case 'ur1.ca':
-                require_once INSTALLDIR.'/lib/Shorturl_api.php';
-                $short_url_service = new LilUrl;
-                $short_url = $short_url_service->shorten($long_url);
-                break;
+        $canon = File_redirection::_canonUrl($long_url);
 
-            case '2tu.us':
-                $short_url_service = new TightUrl;
-                require_once INSTALLDIR.'/lib/Shorturl_api.php';
-                $short_url = $short_url_service->shorten($long_url);
-                break;
+        $short_url = File_redirection::_userMakeShort($canon);
 
-            case 'ptiturl.com':
-                require_once INSTALLDIR.'/lib/Shorturl_api.php';
-                $short_url_service = new PtitUrl;
-                $short_url = $short_url_service->shorten($long_url);
-                break;
-
-            case 'bit.ly':
-                curl_setopt($curlh, CURLOPT_URL, 'http://bit.ly/api?method=shorten&long_url='.urlencode($long_url));
-                $short_url = current(json_decode(curl_exec($curlh))->results)->hashUrl;
-                break;
-
-            case 'is.gd':
-                curl_setopt($curlh, CURLOPT_URL, 'http://is.gd/api.php?longurl='.urlencode($long_url));
-                $short_url = curl_exec($curlh);
-                break;
-            case 'snipr.com':
-                curl_setopt($curlh, CURLOPT_URL, 'http://snipr.com/site/snip?r=simple&link='.urlencode($long_url));
-                $short_url = curl_exec($curlh);
-                break;
-            case 'metamark.net':
-                curl_setopt($curlh, CURLOPT_URL, 'http://metamark.net/api/rest/simple?long_url='.urlencode($long_url));
-                $short_url = curl_exec($curlh);
-                break;
-            case 'tinyurl.com':
-                curl_setopt($curlh, CURLOPT_URL, 'http://tinyurl.com/api-create.php?url='.urlencode($long_url));
-                $short_url = curl_exec($curlh);
-                break;
-            default:
-                $short_url = false;
+        // Did we get one? Is it shorter?
+        if (!empty($short_url) && mb_strlen($short_url) < mb_strlen($long_url)) {
+            return $short_url;
+        } else {
+            return $long_url;
         }
+    }
 
-        curl_close($curlh);
-
-        if ($short_url) {
+    function _userMakeShort($long_url) {
+        $short_url = common_shorten_url($long_url);
+        if (!empty($short_url) && $short_url != $long_url) {
             $short_url = (string)$short_url;
             // store it
             $file = File::staticGet('url', $long_url);
@@ -222,7 +152,7 @@ class File_redirection extends Memcached_DataObject
             }
             return $short_url;
         }
-        return $long_url;
+        return null;
     }
 
     function _canonUrl($in_url, $default_scheme = 'http://') {
index 2908549da610fab31c94840dfd977860277431b1..44b92a2fadd005c4be6282a100a55f0ee12200fc 100644 (file)
@@ -25,24 +25,29 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
  * Table Definition for file_thumbnail
  */
 
-class File_thumbnail extends Memcached_DataObject 
+class File_thumbnail extends Memcached_DataObject
 {
     ###START_AUTOCODE
     /* the code below is auto generated do not remove the above tag */
 
     public $__table = 'file_thumbnail';                  // table name
-    public $id;                              // int(11)  not_null primary_key group_by
-    public $file_id;                         // int(11)  unique_key group_by
+    public $file_id;                         // int(4)  primary_key not_null
     public $url;                             // varchar(255)  unique_key
-    public $width;                           // int(11)  group_by
-    public $height;                          // int(11)  group_by
+    public $width;                           // int(4)
+    public $height;                          // int(4)
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
     /* Static get */
-    function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_thumbnail',$k,$v); }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('File_thumbnail',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
 
+    function sequenceKey()
+    {
+        return array(false, false, false);
+    }
+
     function saveNew($data, $file_id) {
         $tn = new File_thumbnail;
         $tn->file_id = $file_id;
index 9362faaaed8977986abd3174d284dd7d13ac094f..d35febb77873af9503cdbe74460882d543e2a15f 100644 (file)
@@ -25,18 +25,18 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
  * Table Definition for file_to_post
  */
 
-class File_to_post extends Memcached_DataObject 
+class File_to_post extends Memcached_DataObject
 {
     ###START_AUTOCODE
     /* the code below is auto generated do not remove the above tag */
 
     public $__table = 'file_to_post';                    // table name
-    public $id;                              // int(11)  not_null primary_key group_by
-    public $file_id;                         // int(11)  multiple_key group_by
-    public $post_id;                         // int(11)  group_by
+    public $file_id;                         // int(4)  primary_key not_null
+    public $post_id;                         // int(4)  primary_key not_null
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
     /* Static get */
-    function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_to_post',$k,$v); }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('File_to_post',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
@@ -44,17 +44,27 @@ class File_to_post extends Memcached_DataObject
     function processNew($file_id, $notice_id) {
         static $seen = array();
         if (empty($seen[$notice_id]) || !in_array($file_id, $seen[$notice_id])) {
-            $f2p = new File_to_post;
-            $f2p->file_id = $file_id;
-            $f2p->post_id = $notice_id;
-            $f2p->insert();
+
+            $f2p = File_to_post::pkeyGet(array('post_id' => $notice_id,
+                                               'file_id' => $file_id));
+            if (empty($f2p)) {
+                $f2p = new File_to_post;
+                $f2p->file_id = $file_id;
+                $f2p->post_id = $notice_id;
+                $f2p->insert();
+            }
+
             if (empty($seen[$notice_id])) {
                 $seen[$notice_id] = array($file_id);
             } else {
                 $seen[$notice_id][] = $file_id;
             }
         }
+    }
 
+    function &pkeyGet($kv)
+    {
+        return Memcached_DataObject::pkeyGet('File_to_post', $kv);
     }
 }
 
index 61727abe5ed5924d6a8a7bb41eb721a2d672cefc..8b3e03dfb33f3d7f5c1e39cdf2d04160c72d07de 100644 (file)
@@ -4,42 +4,41 @@
  */
 require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
 
-class Foreign_user extends Memcached_DataObject 
+class Foreign_user extends Memcached_DataObject
 {
     ###START_AUTOCODE
     /* the code below is auto generated do not remove the above tag */
 
     public $__table = 'foreign_user';                    // table name
-    public $id;                              // int(4)  primary_key not_null
+    public $id;                              // bigint(8)  primary_key not_null
     public $service;                         // int(4)  primary_key not_null
     public $uri;                             // varchar(255)  unique_key not_null
-    public $nickname;                        // varchar(255)  
+    public $nickname;                        // varchar(255)
     public $created;                         // datetime()   not_null
     public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
     /* Static get */
-    function staticGet($k,$v=null)
-    { return Memcached_DataObject::staticGet('Foreign_user',$k,$v); }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Foreign_user',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
-    
+
     // XXX:  This only returns a 1->1 single obj mapping.  Change?  Or make
     // a getForeignUsers() that returns more than one? --Zach
-    static function getForeignUser($id, $service) {        
+    static function getForeignUser($id, $service) {
         $fuser = new Foreign_user();
         $fuser->whereAdd("service = $service");
         $fuser->whereAdd("id = $id");
         $fuser->limit(1);
-        
+
         if ($fuser->find()) {
             $fuser->fetch();
             return $fuser;
         }
-        
-        return null;        
+
+        return null;
     }
-    
+
     function updateKeys(&$orig)
     {
         $parts = array();
@@ -68,5 +67,4 @@ class Foreign_user extends Memcached_DataObject
         return $result;
     }
 
-    
 }
index b80ba42729d8b84cfb0a9186584721406d6355d7..1af7439f7f749e668824773aa6363012e77e33f2 100644 (file)
@@ -14,8 +14,14 @@ class Group_inbox extends Memcached_DataObject
     public $created;                         // datetime()   not_null
 
     /* Static get */
+
     function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Group_inbox',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
+
+    function &pkeyGet($kv)
+    {
+        return Memcached_DataObject::pkeyGet('Group_inbox', $kv);
+    }
 }
index 2d5a73554c7210fda91d50c338cc8ac0db322346..f7cbb9d5b6c375b50f9c7dbc46b3a8eb2496e80b 100644 (file)
@@ -193,7 +193,14 @@ class Memcached_DataObject extends DB_DataObject
                 // unable to connect to sphinx' search daemon
                 if (!$connected) {
                     if ('mysql' === common_config('db', 'type')) {
-                        $search_engine = new MySQLSearch($this, $table);
+                        $type = common_config('search', 'type');
+                        if ($type == 'like') {
+                            $search_engine = new MySQLLikeSearch($this, $table);
+                        } else if ($type == 'fulltext') {
+                            $search_engine = new MySQLSearch($this, $table);
+                        } else {
+                            throw new ServerException('Unknown search type: ' . $type);
+                        }
                     } else {
                         $search_engine = new PGSearch($this, $table);
                     }
index b6bbf66cacd376f3e8f136df4b16280c570aee26..8a018068aee6289580217df68b627936985442ab 100644 (file)
@@ -34,6 +34,8 @@ define('NOTICE_REMOTE_OMB', 0);
 define('NOTICE_LOCAL_NONPUBLIC', -1);
 define('NOTICE_GATEWAY', -2);
 
+define('MAX_BOXCARS', 128);
+
 class Notice extends Memcached_DataObject
 {
     ###START_AUTOCODE
@@ -221,7 +223,7 @@ class Notice extends Memcached_DataObject
             $notice->saveTags();
 
             $notice->addToInboxes();
-            $notice->saveGroups();
+
             $notice->saveUrls();
             $orig2 = clone($notice);
                $notice->rendered = common_render_content($final, $notice);
@@ -331,6 +333,20 @@ class Notice extends Memcached_DataObject
         return $n_attachments;
     }
 
+    function attachments() {
+        // XXX: cache this
+        $att = array();
+        $f2p = new File_to_post;
+        $f2p->post_id = $this->id;
+        if ($f2p->find()) {
+            while ($f2p->fetch()) {
+                $f = File::staticGet($f2p->file_id);
+                $att[] = clone($f);
+            }
+        }
+        return $att;
+    }
+
     function blowCaches($blowLast=false)
     {
         $this->blowSubsCache($blowLast);
@@ -339,6 +355,19 @@ class Notice extends Memcached_DataObject
         $this->blowPublicCache($blowLast);
         $this->blowTagCache($blowLast);
         $this->blowGroupCache($blowLast);
+        $this->blowConversationCache($blowLast);
+    }
+
+    function blowConversationCache($blowLast=false)
+    {
+        $cache = common_memcache();
+        if ($cache) {
+            $ck = common_cache_key('notice:conversation_ids:'.$this->conversation);
+            $cache->delete($ck);
+            if ($blowLast) {
+                $cache->delete($ck.';last');
+            }
+        }
     }
 
     function blowGroupCache($blowLast=false)
@@ -471,8 +500,10 @@ class Notice extends Memcached_DataObject
             if ($fave->find()) {
                 while ($fave->fetch()) {
                     $cache->delete(common_cache_key('fave:ids_by_user:'.$fave->user_id));
+                    $cache->delete(common_cache_key('fave:by_user_own:'.$fave->user_id));
                     if ($blowLast) {
                         $cache->delete(common_cache_key('fave:ids_by_user:'.$fave->user_id.';last'));
+                        $cache->delete(common_cache_key('fave:by_user_own:'.$fave->user_id.';last'));
                     }
                 }
             }
@@ -675,7 +706,10 @@ class Notice extends Memcached_DataObject
         if (!empty($cache)) {
             $notices = array();
             foreach ($ids as $id) {
-                $notices[] = Notice::staticGet('id', $id);
+                $n = Notice::staticGet('id', $id);
+                if (!empty($n)) {
+                    $notices[] = $n;
+                }
             }
             return new ArrayWrapper($notices);
         } else {
@@ -744,34 +778,148 @@ class Notice extends Memcached_DataObject
         return $ids;
     }
 
+    function conversationStream($id, $offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
+    {
+        $ids = Notice::stream(array('Notice', '_conversationStreamDirect'),
+                              array($id),
+                              'notice:conversation_ids:'.$id,
+                              $offset, $limit, $since_id, $max_id, $since);
+
+        return Notice::getStreamByIds($ids);
+    }
+
+    function _conversationStreamDirect($id, $offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
+    {
+        $notice = new Notice();
+
+        $notice->selectAdd(); // clears it
+        $notice->selectAdd('id');
+
+        $notice->conversation = $id;
+
+        $notice->orderBy('id DESC');
+
+        if (!is_null($offset)) {
+            $notice->limit($offset, $limit);
+        }
+
+        if ($since_id != 0) {
+            $notice->whereAdd('id > ' . $since_id);
+        }
+
+        if ($max_id != 0) {
+            $notice->whereAdd('id <= ' . $max_id);
+        }
+
+        if (!is_null($since)) {
+            $notice->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\'');
+        }
+
+        $ids = array();
+
+        if ($notice->find()) {
+            while ($notice->fetch()) {
+                $ids[] = $notice->id;
+            }
+        }
+
+        $notice->free();
+        $notice = NULL;
+
+        return $ids;
+    }
+
     function addToInboxes()
     {
         $enabled = common_config('inboxes', 'enabled');
 
         if ($enabled === true || $enabled === 'transitional') {
+
+            // XXX: loads constants
+
             $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 JOIN subscription ON $UT.id = subscription.subscriber " .
-              'WHERE subscription.subscribed = ' . $this->profile_id . ' ' .
-              'AND NOT EXISTS (SELECT user_id, notice_id ' .
-              'FROM notice_inbox ' .
-              "WHERE user_id = $UT.id " .
-              'AND notice_id = ' . $this->id . ' )';
-            if ($enabled === 'transitional') {
-                $qry .= " AND $UT.inboxed = 1";
+
+            $users = $this->getSubscribedUsers();
+
+            // FIXME: kind of ignoring 'transitional'...
+            // we'll probably stop supporting inboxless mode
+            // in 0.9.x
+
+            $ni = array();
+
+            foreach ($users as $id) {
+                $ni[$id] = NOTICE_INBOX_SOURCE_SUB;
+            }
+
+            $groups = $this->saveGroups();
+
+            foreach ($groups as $group) {
+                $users = $group->getUserMembers();
+                foreach ($users as $id) {
+                    if (!array_key_exists($id, $ni)) {
+                        $ni[$id] = NOTICE_INBOX_SOURCE_GROUP;
+                    }
+                }
+            }
+
+            $cnt = 0;
+
+            $qryhdr = 'INSERT INTO notice_inbox (user_id, notice_id, source, created) VALUES ';
+            $qry = $qryhdr;
+
+            foreach ($ni as $id => $source) {
+                if ($cnt > 0) {
+                    $qry .= ', ';
+                }
+                $qry .= '('.$id.', '.$this->id.', '.$source.', "'.$this->created.'") ';
+                $cnt++;
+                if ($cnt >= MAX_BOXCARS) {
+                    $inbox = new Notice_inbox();
+                    $inbox->query($qry);
+                    $qry = $qryhdr;
+                    $cnt = 0;
+                }
+            }
+
+            if ($cnt > 0) {
+                $inbox = new Notice_inbox();
+                $inbox->query($qry);
             }
-            $inbox->query($qry);
         }
+
         return;
     }
 
+    function getSubscribedUsers()
+    {
+        $user = new User();
+
+        $qry =
+          'SELECT id ' .
+          'FROM user JOIN subscription '.
+          'ON user.id = subscription.subscriber ' .
+          'WHERE subscription.subscribed = %d ';
+
+        $user->query(sprintf($qry, $this->profile_id));
+
+        $ids = array();
+
+        while ($user->fetch()) {
+            $ids[] = $user->id;
+        }
+
+        $user->free();
+
+        return $ids;
+    }
+
     function saveGroups()
     {
+        $groups = array();
+
         $enabled = common_config('inboxes', 'enabled');
         if ($enabled !== true && $enabled !== 'transitional') {
-            return;
+            return $groups;
         }
 
         /* extract all !group */
@@ -779,7 +927,7 @@ class Notice extends Memcached_DataObject
                                 strtolower($this->content),
                                 $match);
         if (!$count) {
-            return true;
+            return $groups;
         }
 
         $profile = $this->getProfile();
@@ -805,41 +953,36 @@ class Notice extends Memcached_DataObject
 
             if ($profile->isMember($group)) {
 
-                $gi = new Group_inbox();
-
-                $gi->group_id  = $group->id;
-                $gi->notice_id = $this->id;
-                $gi->created   = common_sql_now();
-
-                $result = $gi->insert();
+                $result = $this->addToGroupInbox($group);
 
                 if (!$result) {
                     common_log_db_error($gi, 'INSERT', __FILE__);
                 }
 
-                // FIXME: do this in an offline daemon
-
-                $this->addToGroupInboxes($group);
+                $groups[] = clone($group);
             }
         }
+
+        return $groups;
     }
 
-    function addToGroupInboxes($group)
+    function addToGroupInbox($group)
     {
-        $inbox = new Notice_inbox();
-        $UT = common_config('db','type')=='pgsql'?'"user"':'user';
-        $qry = 'INSERT INTO notice_inbox (user_id, notice_id, created, source) ' .
-          "SELECT $UT.id, " . $this->id . ", '" . $this->created . "', " . NOTICE_INBOX_SOURCE_GROUP . " " .
-          "FROM $UT JOIN group_member ON $UT.id = group_member.profile_id " .
-          'WHERE group_member.group_id = ' . $group->id . ' ' .
-          'AND NOT EXISTS (SELECT user_id, notice_id ' .
-          'FROM notice_inbox ' .
-          "WHERE user_id = $UT.id " .
-          'AND notice_id = ' . $this->id . ' )';
-        if ($enabled === 'transitional') {
-            $qry .= " AND $UT.inboxed = 1";
-        }
-        $result = $inbox->query($qry);
+        $gi = Group_inbox::pkeyGet(array('group_id' => $group->id,
+                                         'notice_id' => $this->id));
+
+        if (empty($gi)) {
+
+            $gi = new Group_inbox();
+
+            $gi->group_id  = $group->id;
+            $gi->notice_id = $this->id;
+            $gi->created   = $this->created;
+
+            return $gi->insert();
+        }
+
+        return true;
     }
 
     function saveReplies()
@@ -1044,6 +1187,7 @@ class Notice extends Memcached_DataObject
 
         if (empty($cache) ||
             $since_id != 0 || $max_id != 0 || (!is_null($since) && $since > 0) ||
+            is_null($limit) ||
             ($offset + $limit) > NOTICE_CACHE_WINDOW) {
             return call_user_func_array($fn, array_merge($args, array($offset, $limit, $since_id,
                                                                       $max_id, $since)));
index 4ca2e9ae3c6705bc53c58ca8bd8826f19131d7e7..940381f84cd1e258ba0a8563ea79bae62c6b4b8a 100644 (file)
@@ -27,6 +27,7 @@ define('INBOX_CACHE_WINDOW', 101);
 
 define('NOTICE_INBOX_SOURCE_SUB', 1);
 define('NOTICE_INBOX_SOURCE_GROUP', 2);
+define('NOTICE_INBOX_SOURCE_REPLY', 3);
 define('NOTICE_INBOX_SOURCE_GATEWAY', -1);
 
 class Notice_inbox extends Memcached_DataObject
index 6b27c80cb5528e549126e4b84294f142f0c54cfb..a0ed6b3ca349e0da83dc161a409893f821cdb8af 100644 (file)
@@ -289,4 +289,52 @@ class Profile extends Memcached_DataObject
             return Avatar::defaultImage($size);
         }
     }
+
+    function getSubscriptions($offset=0, $limit=null)
+    {
+        $qry =
+          'SELECT profile.* ' .
+          'FROM profile JOIN subscription ' .
+          'ON profile.id = subscription.subscribed ' .
+          'WHERE subscription.subscriber = %d ' .
+          'AND subscription.subscribed != subscription.subscriber ' .
+          'ORDER BY subscription.created DESC ';
+
+        if (common_config('db','type') == 'pgsql') {
+            $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
+        } else {
+            $qry .= ' LIMIT ' . $offset . ', ' . $limit;
+        }
+
+        $profile = new Profile();
+
+        $profile->query(sprintf($qry, $this->id));
+
+        return $profile;
+    }
+
+    function getSubscribers($offset=0, $limit=null)
+    {
+        $qry =
+          'SELECT profile.* ' .
+          'FROM profile JOIN subscription ' .
+          'ON profile.id = subscription.subscriber ' .
+          'WHERE subscription.subscribed = %d ' .
+          'AND subscription.subscribed != subscription.subscriber ' .
+          'ORDER BY subscription.created DESC ';
+
+        if ($offset) {
+            if (common_config('db','type') == 'pgsql') {
+                $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
+            } else {
+                $qry .= ' LIMIT ' . $offset . ', ' . $limit;
+            }
+        }
+
+        $profile = new Profile();
+
+        $cnt = $profile->query(sprintf($qry, $this->id));
+
+        return $profile;
+    }
 }
diff --git a/classes/Session.php b/classes/Session.php
new file mode 100644 (file)
index 0000000..93fd99b
--- /dev/null
@@ -0,0 +1,129 @@
+<?php
+/**
+ * Table Definition for session
+ *
+ * Laconica - a 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); }
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Session extends Memcached_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'session';                         // table name
+    public $id;                              // varchar(32)  primary_key not_null
+    public $session_data;                    // text()
+    public $created;                         // datetime()   not_null
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    /* Static get */
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Session',$k,$v); }
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+
+    static function logdeb($msg)
+    {
+        if (common_config('sessions', 'debug')) {
+            common_debug("Session: " . $msg);
+        }
+    }
+
+    static function open($save_path, $session_name)
+    {
+        return true;
+    }
+
+    static function close()
+    {
+        return true;
+    }
+
+    static function read($id)
+    {
+        self::logdeb("Fetching session '$id'");
+
+        $session = Session::staticGet('id', $id);
+
+        if (empty($session)) {
+            return '';
+        } else {
+            return (string)$session->session_data;
+        }
+    }
+
+    static function write($id, $session_data)
+    {
+        self::logdeb("Writing session '$id'");
+
+        $session = Session::staticGet('id', $id);
+
+        if (empty($session)) {
+            $session = new Session();
+
+            $session->id           = $id;
+            $session->session_data = $session_data;
+            $session->created      = common_sql_now();
+
+            return $session->insert();
+        } else {
+            $session->session_data = $session_data;
+
+            return $session->update();
+        }
+    }
+
+    static function destroy($id)
+    {
+        self::logdeb("Deleting session $id");
+
+        $session = Session::staticGet('id', $id);
+
+        if (!empty($session)) {
+            return $session->delete();
+        }
+    }
+
+    static function gc($maxlifetime)
+    {
+        self::logdeb("garbage collection (maxlifetime = $maxlifetime)");
+
+        $epoch = time() - $maxlifetime;
+
+        $qry = 'DELETE FROM session ' .
+          'WHERE modified < "'.$epoch.'"';
+
+        $session = new Session();
+
+        $result = $session->query($qry);
+
+        self::logdeb("garbage collection result = $result");
+    }
+
+    static function setSaveHandler()
+    {
+        self::logdeb("setting save handlers");
+        $result = session_set_save_handler('Session::open', 'Session::close', 'Session::read',
+                                           'Session::write', 'Session::destroy', 'Session::gc');
+        self::logdeb("save handlers result = $result");
+        return $result;
+    }
+}
index f8d6756b69090c4c51e7127ba2497e4b0e1a6444..dbd722e88e9ba8e99602aeca1211e028f9623a5b 100644 (file)
@@ -132,6 +132,13 @@ class Status_network extends DB_DataObject
             }
         } else {
             $sn = self::memGet('hostname', strtolower($servername));
+
+            if (empty($sn)) {
+                // Try for a no-www address
+                if (0 == strncasecmp($servername, 'www.', 4)) {
+                    $sn = self::memGet('hostname', strtolower(substr($servername, 4)));
+                }
+            }
         }
 
         if (!empty($sn)) {
index e8c8c5a75b48d1691cbb17cbf4eb8a3db3c7ae54..04b38a0d2223ef7d240768ec0de6282d03bc51a8 100644 (file)
@@ -424,9 +424,9 @@ class User extends Memcached_DataObject
         }
     }
 
-    function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE)
+    function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE, $own=false)
     {
-        $ids = Fave::stream($this->id, $offset, $limit);
+        $ids = Fave::stream($this->id, $offset, $limit, $own);
         return Notice::getStreamByIds($ids);
     }
 
@@ -491,6 +491,8 @@ class User extends Memcached_DataObject
             // ;last cache, too
             $cache->delete(common_cache_key('fave:ids_by_user:'.$this->id));
             $cache->delete(common_cache_key('fave:ids_by_user:'.$this->id.';last'));
+            $cache->delete(common_cache_key('fave:ids_by_user_own:'.$this->id));
+            $cache->delete(common_cache_key('fave:ids_by_user_own:'.$this->id.';last'));
         }
     }
 
@@ -600,50 +602,16 @@ class User extends Memcached_DataObject
 
     function getSubscriptions($offset=0, $limit=null)
     {
-        $qry =
-          'SELECT profile.* ' .
-          'FROM profile JOIN subscription ' .
-          'ON profile.id = subscription.subscribed ' .
-          'WHERE subscription.subscriber = %d ' .
-          'AND subscription.subscribed != subscription.subscriber ' .
-          'ORDER BY subscription.created DESC ';
-
-        if (common_config('db','type') == 'pgsql') {
-            $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
-        } else {
-            $qry .= ' LIMIT ' . $offset . ', ' . $limit;
-        }
-
-        $profile = new Profile();
-
-        $profile->query(sprintf($qry, $this->id));
-
-        return $profile;
+        $profile = $this->getProfile();
+        assert(!empty($profile));
+        return $profile->getSubscriptions($offset, $limit);
     }
 
     function getSubscribers($offset=0, $limit=null)
     {
-        $qry =
-          'SELECT profile.* ' .
-          'FROM profile JOIN subscription ' .
-          'ON profile.id = subscription.subscriber ' .
-          'WHERE subscription.subscribed = %d ' .
-          'AND subscription.subscribed != subscription.subscriber ' .
-          'ORDER BY subscription.created DESC ';
-
-        if ($offset) {
-            if (common_config('db','type') == 'pgsql') {
-                $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
-            } else {
-                $qry .= ' LIMIT ' . $offset . ', ' . $limit;
-            }
-        }
-
-        $profile = new Profile();
-
-        $cnt = $profile->query(sprintf($qry, $this->id));
-
-        return $profile;
+        $profile = $this->getProfile();
+        assert(!empty($profile));
+        return $profile->getSubscribers($offset, $limit);
     }
 
     function getTaggedSubscribers($tag, $offset=0, $limit=null)
index 8a56b9e52b32e893cab4fd5f0c582abd2380330b..27b444705d93823a67b8b519226f099e931fd939 100644 (file)
@@ -126,6 +126,30 @@ class User_group extends Memcached_DataObject
         return $members;
     }
 
+    function getAdmins($offset=0, $limit=null)
+    {
+        $qry =
+          'SELECT profile.* ' .
+          'FROM profile JOIN group_member '.
+          'ON profile.id = group_member.profile_id ' .
+          'WHERE group_member.group_id = %d ' .
+          'AND group_member.is_admin = 1 ' .
+          'ORDER BY group_member.modified ASC ';
+
+        if ($limit != null) {
+            if (common_config('db','type') == 'pgsql') {
+                $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
+            } else {
+                $qry .= ' LIMIT ' . $offset . ', ' . $limit;
+            }
+        }
+
+        $admins = new Profile();
+
+        $admins->query(sprintf($qry, $this->id));
+        return $admins;
+    }
+
     function getBlocked($offset=0, $limit=null)
     {
         $qry =
@@ -246,4 +270,28 @@ class User_group extends Memcached_DataObject
         return Design::staticGet('id', $this->design_id);
     }
 
+    function getUserMembers()
+    {
+        // XXX: cache this
+
+        $user = new User();
+
+        $qry =
+          'SELECT id ' .
+          'FROM user JOIN group_member '.
+          'ON user.id = group_member.profile_id ' .
+          'WHERE group_member.group_id = %d ';
+
+        $user->query(sprintf($qry, $this->id));
+
+        $ids = array();
+
+        while ($user->fetch()) {
+            $ids[] = $user->id;
+        }
+
+        $user->free();
+
+        return $ids;
+    }
 }
old mode 100755 (executable)
new mode 100644 (file)
index 5ced158..766bed7
@@ -68,13 +68,14 @@ size = 1
 title = 2
 date = 1
 protected = 1
+filename = 2
+modified = 384
 
 [file__keys]
 id = N
 
 [file_oembed]
-id = 129
-file_id = 1
+file_id = 129
 version = 2
 type = 2
 provider = 2
@@ -86,37 +87,40 @@ title = 2
 author_name = 2
 author_url = 2
 url = 2
+modified = 384
 
 [file_oembed__keys]
-id = N
+file_id = K
 
 [file_redirection]
-id = 129
-url = 2
+url = 130
 file_id = 1
 redirections = 1
 httpcode = 1
+modified = 384
 
 [file_redirection__keys]
-id = N
+url = K
 
 [file_thumbnail]
-id = 129
-file_id = 1
+file_id = 129
 url = 2
 width = 1
 height = 1
+modified = 384
 
 [file_thumbnail__keys]
-id = N
+file_id = K
+url = U
 
 [file_to_post]
-id = 129
-file_id = 1
-post_id = 1
+file_id = 129
+post_id = 129
+modified = 384
 
 [file_to_post__keys]
-id = N
+file_id = K
+post_id = K
 
 [foreign_link]
 user_id = 129
@@ -376,6 +380,15 @@ replied_id = 1
 notice_id = K
 profile_id = K
 
+[session]
+id = 130
+session_data = 34
+created = 142
+modified = 384
+
+[session__keys]
+id = K
+
 [sms_carrier]
 id = 129
 name = 2
index d42bac9a6920be6fd97332b5553f364f8b96fd76..57aa6a6c8cc20b620af337cb66eb750e37969f5e 100644 (file)
@@ -36,6 +36,9 @@ $config['site']['path'] = 'laconica';
 // If you want logging sent to a file instead of syslog
 // $config['site']['logfile'] = '/tmp/laconica.log';
 
+// Change the syslog facility that Laconica logs to (default is LOG_USER)
+// $config['syslog']['facility'] = LOG_LOCAL7;
+
 // Enables extra log information, for example full details of PEAR DB errors
 // $config['site']['logdebug'] = true;
 
@@ -86,6 +89,9 @@ $config['sphinx']['port'] = 3312;
 // $config['xmpp']['public'][] = 'someindexer@example.net';
 // $config['xmpp']['debug'] = false;
 
+// Turn off invites
+// $config['invite']['enabled'] = false;
+
 // Default locale info
 // $config['site']['timezone'] = 'Pacific/Auckland';
 // $config['site']['language'] = 'en_NZ';
diff --git a/db/074to080.sql b/db/074to080.sql
new file mode 100644 (file)
index 0000000..ff08191
--- /dev/null
@@ -0,0 +1,109 @@
+alter table user
+     add column design_id integer comment 'id of a design' references design(id),
+     add column viewdesigns tinyint default 1 comment 'whether to view user-provided designs';
+
+alter table notice add column
+     conversation integer comment 'id of root notice in this conversation' references notice (id),
+     add index notice_conversation_idx (conversation);
+
+alter table foreign_user
+     modify column id bigint not null comment 'unique numeric key on foreign service';
+
+alter table foreign_link
+     modify column foreign_id bigint unsigned comment 'link to user on foreign service, if exists';
+
+alter table user_group
+      add column design_id integer comment 'id of a design' references design(id);
+
+create table file (
+    id integer primary key auto_increment,
+    url varchar(255) comment 'destination URL after following redirections',
+    mimetype varchar(50) comment 'mime type of resource',
+    size integer comment 'size of resource when available',
+    title varchar(255) comment 'title of resource when available',
+    date integer(11) comment 'date of resource according to http query',
+    protected integer(1) comment 'true when URL is private (needs login)',
+    filename varchar(255) comment 'if a local file, name of the file',
+    modified timestamp comment 'date this record was modified',
+
+    unique(url)
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
+
+create table file_oembed (
+    file_id integer primary key comment 'oEmbed for that URL/file' references file (id),
+    version varchar(20) comment 'oEmbed spec. version',
+    type varchar(20) comment 'oEmbed type: photo, video, link, rich',
+    provider varchar(50) comment 'name of this oEmbed provider',
+    provider_url varchar(255) comment 'URL of this oEmbed provider',
+    width integer comment 'width of oEmbed resource when available',
+    height integer comment 'height of oEmbed resource when available',
+    html text comment 'html representation of this oEmbed resource when applicable',
+    title varchar(255) comment 'title of oEmbed resource when available',
+    author_name varchar(50) comment 'author name for this oEmbed resource',
+    author_url varchar(255) comment 'author URL for this oEmbed resource',
+    url varchar(255) comment 'URL for this oEmbed resource when applicable (photo, link)',
+    modified timestamp comment 'date this record was modified'
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
+
+create table file_redirection (
+
+    url varchar(255) primary key comment 'short URL (or any other kind of redirect) for file (id)',
+    file_id integer comment 'short URL for what URL/file' references file (id),
+    redirections integer comment 'redirect count',
+    httpcode integer comment 'HTTP status code (20x, 30x, etc.)',
+    modified timestamp comment 'date this record was modified'
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table file_thumbnail (
+
+    file_id integer primary key comment 'thumbnail for what URL/file' references file (id),
+    url varchar(255) comment 'URL of thumbnail',
+    width integer comment 'width of thumbnail',
+    height integer comment 'height of thumbnail',
+    modified timestamp comment 'date this record was modified',
+
+    unique(url)
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table file_to_post (
+
+    file_id integer comment 'id of URL/file' references file (id),
+    post_id integer comment 'id of the notice it belongs to' references notice (id),
+    modified timestamp comment 'date this record was modified',
+
+    constraint primary key (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',
+    disposition tinyint default 1 comment 'bit 1 = hide background image, bit 2 = display background image, bit 4 = tile background image'
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table group_block (
+   group_id integer not null comment 'group profile is blocked from' references user_group (id),
+   blocked integer not null comment 'profile that is blocked' references profile (id),
+   blocker integer not null comment 'user making the block' references user (id),
+   modified timestamp comment 'date of blocking',
+
+   constraint primary key (group_id, blocked)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table group_alias (
+
+   alias varchar(64) primary key comment 'additional nickname for the group',
+   group_id integer not null comment 'group profile is blocked from' references user_group (id),
+   modified timestamp comment 'date alias was created',
+
+   index group_alias_group_id_idx (group_id)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
diff --git a/db/innodb.sql b/db/innodb.sql
new file mode 100644 (file)
index 0000000..f3ab6cd
--- /dev/null
@@ -0,0 +1,2 @@
+alter table profile drop index nickname, engine=InnoDB;
+alter table notice drop index content, engine=InnoDB;
index 8d1d47d38d35c14d2d9d45b46f5410645cbc452b..2c04f680a85d587a032cd06fada5c63c8488eaa5 100644 (file)
@@ -277,7 +277,7 @@ create table foreign_service (
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
 
 create table foreign_user (
-     id int not null comment 'unique numeric key on foreign service',
+     id bigint not null comment 'unique numeric key on foreign service',
      service int not null comment 'foreign key to service' references foreign_service(id),
      uri varchar(255) not null unique key comment 'identifying URI',
      nickname varchar(255) comment 'nickname on foreign service',
@@ -431,6 +431,7 @@ create table group_inbox (
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
 
 create table file (
+
     id integer primary key auto_increment,
     url varchar(255) comment 'destination URL after following redirections',
     mimetype varchar(50) comment 'mime type of resource',
@@ -438,13 +439,15 @@ create table file (
     title varchar(255) comment 'title of resource when available',
     date integer(11) comment 'date of resource according to http query',
     protected integer(1) comment 'true when URL is private (needs login)',
+    filename varchar(255) comment 'if a local file, name of the file',
+
+    modified timestamp comment 'date this record was modified',
 
     unique(url)
-) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
 
 create table file_oembed (
-    id integer primary key auto_increment,
-    file_id integer comment 'oEmbed for that URL/file' references file (id),
+    file_id integer primary key comment 'oEmbed for that URL/file' references file (id),
     version varchar(20) comment 'oEmbed spec. version',
     type varchar(20) comment 'oEmbed type: photo, video, link, rich',
     provider varchar(50) comment 'name of this oEmbed provider',
@@ -456,37 +459,39 @@ create table file_oembed (
     author_name varchar(50) comment 'author name for this oEmbed resource',
     author_url varchar(255) comment 'author URL for this oEmbed resource',
     url varchar(255) comment 'URL for this oEmbed resource when applicable (photo, link)',
+    modified timestamp comment 'date this record was modified'
 
-    unique(file_id)
-) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
 
 create table file_redirection (
-    id integer primary key auto_increment,
-    url varchar(255) comment 'short URL (or any other kind of redirect) for file (id)',
+
+    url varchar(255) primary key comment 'short URL (or any other kind of redirect) for file (id)',
     file_id integer comment 'short URL for what URL/file' references file (id),
     redirections integer comment 'redirect count',
     httpcode integer comment 'HTTP status code (20x, 30x, etc.)',
+    modified timestamp comment 'date this record was modified'
 
-    unique(url)
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
 
 create table file_thumbnail (
-    id integer primary key auto_increment,
-    file_id integer comment 'thumbnail for what URL/file' references file (id),
+
+    file_id integer primary key comment 'thumbnail for what URL/file' references file (id),
     url varchar(255) comment 'URL of thumbnail',
     width integer comment 'width of thumbnail',
     height integer comment 'height of thumbnail',
+    modified timestamp comment 'date this record was modified',
 
-    unique(file_id),
     unique(url)
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
 
 create table file_to_post (
-    id integer primary key auto_increment,
+
     file_id integer comment 'id of URL/file' references file (id),
     post_id integer comment 'id of the notice it belongs to' references notice (id),
+    modified timestamp comment 'date this record was modified',
+
+    constraint primary key (file_id, post_id)
 
-    unique(file_id, post_id)
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
 
 create table design (
@@ -519,3 +524,14 @@ create table group_alias (
    index group_alias_group_id_idx (group_id)
 
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table session (
+
+    id varchar(32) primary key comment 'session ID',
+    session_data text comment 'session data',
+    created datetime not null comment 'date this record was created',
+    modified timestamp comment 'date this record was modified',
+
+    index session_modified_idx (modified)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
\ No newline at end of file
diff --git a/extlib/Console/Getopt.php b/extlib/Console/Getopt.php
new file mode 100644 (file)
index 0000000..bb9d69c
--- /dev/null
@@ -0,0 +1,290 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Andrei Zmievski <andrei@php.net>                             |
+// +----------------------------------------------------------------------+
+//
+// $Id: Getopt.php,v 1.4 2007/06/12 14:58:56 cellog Exp $
+
+require_once 'PEAR.php';
+
+/**
+ * Command-line options parsing class.
+ *
+ * @author Andrei Zmievski <andrei@php.net>
+ *
+ */
+class Console_Getopt {
+    /**
+     * Parses the command-line options.
+     *
+     * The first parameter to this function should be the list of command-line
+     * arguments without the leading reference to the running program.
+     *
+     * The second parameter is a string of allowed short options. Each of the
+     * option letters can be followed by a colon ':' to specify that the option
+     * requires an argument, or a double colon '::' to specify that the option
+     * takes an optional argument.
+     *
+     * The third argument is an optional array of allowed long options. The
+     * leading '--' should not be included in the option name. Options that
+     * require an argument should be followed by '=', and options that take an
+     * option argument should be followed by '=='.
+     *
+     * The return value is an array of two elements: the list of parsed
+     * options and the list of non-option command-line arguments. Each entry in
+     * the list of parsed options is a pair of elements - the first one
+     * specifies the option, and the second one specifies the option argument,
+     * if there was one.
+     *
+     * Long and short options can be mixed.
+     *
+     * Most of the semantics of this function are based on GNU getopt_long().
+     *
+     * @param array  $args           an array of command-line arguments
+     * @param string $short_options  specifies the list of allowed short options
+     * @param array  $long_options   specifies the list of allowed long options
+     *
+     * @return array two-element array containing the list of parsed options and
+     * the non-option arguments
+     *
+     * @access public
+     *
+     */
+    function getopt2($args, $short_options, $long_options = null)
+    {
+        return Console_Getopt::doGetopt(2, $args, $short_options, $long_options);
+    }
+
+    /**
+     * This function expects $args to start with the script name (POSIX-style).
+     * Preserved for backwards compatibility.
+     * @see getopt2()
+     */    
+    function getopt($args, $short_options, $long_options = null)
+    {
+        return Console_Getopt::doGetopt(1, $args, $short_options, $long_options);
+    }
+
+    /**
+     * The actual implementation of the argument parsing code.
+     */
+    function doGetopt($version, $args, $short_options, $long_options = null)
+    {
+        // in case you pass directly readPHPArgv() as the first arg
+        if (PEAR::isError($args)) {
+            return $args;
+        }
+        if (empty($args)) {
+            return array(array(), array());
+        }
+        $opts     = array();
+        $non_opts = array();
+
+        settype($args, 'array');
+
+        if ($long_options) {
+            sort($long_options);
+        }
+
+        /*
+         * Preserve backwards compatibility with callers that relied on
+         * erroneous POSIX fix.
+         */
+        if ($version < 2) {
+            if (isset($args[0]{0}) && $args[0]{0} != '-') {
+                array_shift($args);
+            }
+        }
+
+        reset($args);
+        while (list($i, $arg) = each($args)) {
+
+            /* The special element '--' means explicit end of
+               options. Treat the rest of the arguments as non-options
+               and end the loop. */
+            if ($arg == '--') {
+                $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
+                break;
+            }
+
+            if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) {
+                $non_opts = array_merge($non_opts, array_slice($args, $i));
+                break;
+            } elseif (strlen($arg) > 1 && $arg{1} == '-') {
+                $error = Console_Getopt::_parseLongOption(substr($arg, 2), $long_options, $opts, $args);
+                if (PEAR::isError($error))
+                    return $error;
+            } elseif ($arg == '-') {
+                // - is stdin
+                $non_opts = array_merge($non_opts, array_slice($args, $i));
+                break;
+            } else {
+                $error = Console_Getopt::_parseShortOption(substr($arg, 1), $short_options, $opts, $args);
+                if (PEAR::isError($error))
+                    return $error;
+            }
+        }
+
+        return array($opts, $non_opts);
+    }
+
+    /**
+     * @access private
+     *
+     */
+    function _parseShortOption($arg, $short_options, &$opts, &$args)
+    {
+        for ($i = 0; $i < strlen($arg); $i++) {
+            $opt = $arg{$i};
+            $opt_arg = null;
+
+            /* Try to find the short option in the specifier string. */
+            if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':')
+            {
+                return PEAR::raiseError("Console_Getopt: unrecognized option -- $opt");
+            }
+
+            if (strlen($spec) > 1 && $spec{1} == ':') {
+                if (strlen($spec) > 2 && $spec{2} == ':') {
+                    if ($i + 1 < strlen($arg)) {
+                        /* Option takes an optional argument. Use the remainder of
+                           the arg string if there is anything left. */
+                        $opts[] = array($opt, substr($arg, $i + 1));
+                        break;
+                    }
+                } else {
+                    /* Option requires an argument. Use the remainder of the arg
+                       string if there is anything left. */
+                    if ($i + 1 < strlen($arg)) {
+                        $opts[] = array($opt,  substr($arg, $i + 1));
+                        break;
+                    } else if (list(, $opt_arg) = each($args)) {
+                        /* Else use the next argument. */;
+                        if (Console_Getopt::_isShortOpt($opt_arg) || Console_Getopt::_isLongOpt($opt_arg)) {
+                            return PEAR::raiseError("Console_Getopt: option requires an argument -- $opt");
+                        }
+                    } else {
+                        return PEAR::raiseError("Console_Getopt: option requires an argument -- $opt");
+                    }
+                }
+            }
+
+            $opts[] = array($opt, $opt_arg);
+        }
+    }
+
+    /**
+     * @access private
+     *
+     */
+    function _isShortOpt($arg)
+    {
+        return strlen($arg) == 2 && $arg[0] == '-' && preg_match('/[a-zA-Z]/', $arg[1]);
+    }
+
+    /**
+     * @access private
+     *
+     */
+    function _isLongOpt($arg)
+    {
+        return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' &&
+            preg_match('/[a-zA-Z]+$/', substr($arg, 2));
+    }
+
+    /**
+     * @access private
+     *
+     */
+    function _parseLongOption($arg, $long_options, &$opts, &$args)
+    {
+        @list($opt, $opt_arg) = explode('=', $arg, 2);
+        $opt_len = strlen($opt);
+
+        for ($i = 0; $i < count($long_options); $i++) {
+            $long_opt  = $long_options[$i];
+            $opt_start = substr($long_opt, 0, $opt_len);
+            $long_opt_name = str_replace('=', '', $long_opt);
+
+            /* Option doesn't match. Go on to the next one. */
+            if ($long_opt_name != $opt) {
+                continue;
+            }
+
+            $opt_rest  = substr($long_opt, $opt_len);
+
+            /* Check that the options uniquely matches one of the allowed
+               options. */
+            if ($i + 1 < count($long_options)) {
+                $next_option_rest = substr($long_options[$i + 1], $opt_len);
+            } else {
+                $next_option_rest = '';
+            }
+            if ($opt_rest != '' && $opt{0} != '=' &&
+                $i + 1 < count($long_options) &&
+                $opt == substr($long_options[$i+1], 0, $opt_len) &&
+                $next_option_rest != '' &&
+                $next_option_rest{0} != '=') {
+                return PEAR::raiseError("Console_Getopt: option --$opt is ambiguous");
+            }
+
+            if (substr($long_opt, -1) == '=') {
+                if (substr($long_opt, -2) != '==') {
+                    /* Long option requires an argument.
+                       Take the next argument if one wasn't specified. */;
+                    if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) {
+                        return PEAR::raiseError("Console_Getopt: option --$opt requires an argument");
+                    }
+                    if (Console_Getopt::_isShortOpt($opt_arg) || Console_Getopt::_isLongOpt($opt_arg)) {
+                        return PEAR::raiseError("Console_Getopt: option requires an argument --$opt");
+                    }
+                }
+            } else if ($opt_arg) {
+                return PEAR::raiseError("Console_Getopt: option --$opt doesn't allow an argument");
+            }
+
+            $opts[] = array('--' . $opt, $opt_arg);
+            return;
+        }
+
+        return PEAR::raiseError("Console_Getopt: unrecognized option --$opt");
+    }
+
+    /**
+    * Safely read the $argv PHP array across different PHP configurations.
+    * Will take care on register_globals and register_argc_argv ini directives
+    *
+    * @access public
+    * @return mixed the $argv PHP array or PEAR error if not registered
+    */
+    function readPHPArgv()
+    {
+        global $argv;
+        if (!is_array($argv)) {
+            if (!@is_array($_SERVER['argv'])) {
+                if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
+                    return PEAR::raiseError("Console_Getopt: Could not read cmd args (register_argc_argv=Off?)");
+                }
+                return $GLOBALS['HTTP_SERVER_VARS']['argv'];
+            }
+            return $_SERVER['argv'];
+        }
+        return $argv;
+    }
+
+}
+
+?>
diff --git a/extlib/Mail/mimeDecode.php b/extlib/Mail/mimeDecode.php
new file mode 100644 (file)
index 0000000..aaa870c
--- /dev/null
@@ -0,0 +1,849 @@
+<?php
+/**
+ * The Mail_mimeDecode class is used to decode mail/mime messages
+ *
+ * This class will parse a raw mime email and return
+ * the structure. Returned structure is similar to
+ * that returned by imap_fetchstructure().
+ *
+ *  +----------------------------- IMPORTANT ------------------------------+
+ *  | Usage of this class compared to native php extensions such as        |
+ *  | mailparse or imap, is slow and may be feature deficient. If available|
+ *  | you are STRONGLY recommended to use the php extensions.              |
+ *  +----------------------------------------------------------------------+
+ *
+ * Compatible with PHP versions 4 and 5
+ *
+ * LICENSE: This LICENSE is in the BSD license style.
+ * Copyright (c) 2002-2003, Richard Heyes <richard@phpguru.org>
+ * Copyright (c) 2003-2006, PEAR <pear-group@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - Neither the name of the authors, nor the names of its contributors 
+ *   may be used to endorse or promote products derived from this 
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   Mail
+ * @package    Mail_Mime
+ * @author     Richard Heyes  <richard@phpguru.org>
+ * @author     George Schlossnagle <george@omniti.com>
+ * @author     Cipriano Groenendal <cipri@php.net>
+ * @author     Sean Coates <sean@php.net>
+ * @copyright  2003-2006 PEAR <pear-group@php.net>
+ * @license    http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @version    CVS: $Id: mimeDecode.php,v 1.48 2006/12/03 13:43:33 cipri Exp $
+ * @link       http://pear.php.net/package/Mail_mime
+ */
+
+
+/**
+ * require PEAR
+ *
+ * This package depends on PEAR to raise errors.
+ */
+require_once 'PEAR.php';
+
+
+/**
+ * The Mail_mimeDecode class is used to decode mail/mime messages
+ *
+ * This class will parse a raw mime email and return the structure.
+ * Returned structure is similar to that returned by imap_fetchstructure().
+ *
+ *  +----------------------------- IMPORTANT ------------------------------+
+ *  | Usage of this class compared to native php extensions such as        |
+ *  | mailparse or imap, is slow and may be feature deficient. If available|
+ *  | you are STRONGLY recommended to use the php extensions.              |
+ *  +----------------------------------------------------------------------+
+ *
+ * @category   Mail
+ * @package    Mail_Mime
+ * @author     Richard Heyes  <richard@phpguru.org>
+ * @author     George Schlossnagle <george@omniti.com>
+ * @author     Cipriano Groenendal <cipri@php.net>
+ * @author     Sean Coates <sean@php.net>
+ * @copyright  2003-2006 PEAR <pear-group@php.net>
+ * @license    http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/Mail_mime
+ */
+class Mail_mimeDecode extends PEAR
+{
+    /**
+     * The raw email to decode
+     *
+     * @var    string
+     * @access private
+     */
+    var $_input;
+
+    /**
+     * The header part of the input
+     *
+     * @var    string
+     * @access private
+     */
+    var $_header;
+
+    /**
+     * The body part of the input
+     *
+     * @var    string
+     * @access private
+     */
+    var $_body;
+
+    /**
+     * If an error occurs, this is used to store the message
+     *
+     * @var    string
+     * @access private
+     */
+    var $_error;
+
+    /**
+     * Flag to determine whether to include bodies in the
+     * returned object.
+     *
+     * @var    boolean
+     * @access private
+     */
+    var $_include_bodies;
+
+    /**
+     * Flag to determine whether to decode bodies
+     *
+     * @var    boolean
+     * @access private
+     */
+    var $_decode_bodies;
+
+    /**
+     * Flag to determine whether to decode headers
+     *
+     * @var    boolean
+     * @access private
+     */
+    var $_decode_headers;
+
+    /**
+     * Constructor.
+     *
+     * Sets up the object, initialise the variables, and splits and
+     * stores the header and body of the input.
+     *
+     * @param string The input to decode
+     * @access public
+     */
+    function Mail_mimeDecode($input)
+    {
+        list($header, $body)   = $this->_splitBodyHeader($input);
+
+        $this->_input          = $input;
+        $this->_header         = $header;
+        $this->_body           = $body;
+        $this->_decode_bodies  = false;
+        $this->_include_bodies = true;
+    }
+
+    /**
+     * Begins the decoding process. If called statically
+     * it will create an object and call the decode() method
+     * of it.
+     *
+     * @param array An array of various parameters that determine
+     *              various things:
+     *              include_bodies - Whether to include the body in the returned
+     *                               object.
+     *              decode_bodies  - Whether to decode the bodies
+     *                               of the parts. (Transfer encoding)
+     *              decode_headers - Whether to decode headers
+     *              input          - If called statically, this will be treated
+     *                               as the input
+     * @return object Decoded results
+     * @access public
+     */
+    function decode($params = null)
+    {
+        // determine if this method has been called statically
+        $isStatic = !(isset($this) && get_class($this) == __CLASS__);
+
+        // Have we been called statically?
+       // If so, create an object and pass details to that.
+        if ($isStatic AND isset($params['input'])) {
+
+            $obj = new Mail_mimeDecode($params['input']);
+            $structure = $obj->decode($params);
+
+        // Called statically but no input
+        } elseif ($isStatic) {
+            return PEAR::raiseError('Called statically and no input given');
+
+        // Called via an object
+        } else {
+            $this->_include_bodies = isset($params['include_bodies']) ?
+                                    $params['include_bodies'] : false;
+            $this->_decode_bodies  = isset($params['decode_bodies']) ?
+                                    $params['decode_bodies']  : false;
+            $this->_decode_headers = isset($params['decode_headers']) ?
+                                    $params['decode_headers'] : false;
+
+            $structure = $this->_decode($this->_header, $this->_body);
+            if ($structure === false) {
+                $structure = $this->raiseError($this->_error);
+            }
+        }
+
+        return $structure;
+    }
+
+    /**
+     * Performs the decoding. Decodes the body string passed to it
+     * If it finds certain content-types it will call itself in a
+     * recursive fashion
+     *
+     * @param string Header section
+     * @param string Body section
+     * @return object Results of decoding process
+     * @access private
+     */
+    function _decode($headers, $body, $default_ctype = 'text/plain')
+    {
+        $return = new stdClass;
+        $return->headers = array();
+        $headers = $this->_parseHeaders($headers);
+
+        foreach ($headers as $value) {
+            if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) {
+                $return->headers[strtolower($value['name'])]   = array($return->headers[strtolower($value['name'])]);
+                $return->headers[strtolower($value['name'])][] = $value['value'];
+
+            } elseif (isset($return->headers[strtolower($value['name'])])) {
+                $return->headers[strtolower($value['name'])][] = $value['value'];
+
+            } else {
+                $return->headers[strtolower($value['name'])] = $value['value'];
+            }
+        }
+
+        reset($headers);
+        while (list($key, $value) = each($headers)) {
+            $headers[$key]['name'] = strtolower($headers[$key]['name']);
+            switch ($headers[$key]['name']) {
+
+                case 'content-type':
+                    $content_type = $this->_parseHeaderValue($headers[$key]['value']);
+
+                    if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) {
+                        $return->ctype_primary   = $regs[1];
+                        $return->ctype_secondary = $regs[2];
+                    }
+
+                    if (isset($content_type['other'])) {
+                        while (list($p_name, $p_value) = each($content_type['other'])) {
+                            $return->ctype_parameters[$p_name] = $p_value;
+                        }
+                    }
+                    break;
+
+                case 'content-disposition':
+                    $content_disposition = $this->_parseHeaderValue($headers[$key]['value']);
+                    $return->disposition   = $content_disposition['value'];
+                    if (isset($content_disposition['other'])) {
+                        while (list($p_name, $p_value) = each($content_disposition['other'])) {
+                            $return->d_parameters[$p_name] = $p_value;
+                        }
+                    }
+                    break;
+
+                case 'content-transfer-encoding':
+                    $content_transfer_encoding = $this->_parseHeaderValue($headers[$key]['value']);
+                    break;
+            }
+        }
+
+        if (isset($content_type)) {
+            switch (strtolower($content_type['value'])) {
+                case 'text/plain':
+                    $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
+                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;
+                    break;
+
+                case 'text/html':
+                    $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
+                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body) : null;
+                    break;
+
+                case 'multipart/parallel':
+                case 'multipart/appledouble': // Appledouble mail
+                case 'multipart/report': // RFC1892
+                case 'multipart/signed': // PGP
+                case 'multipart/digest':
+                case 'multipart/alternative':
+                case 'multipart/related':
+                case 'multipart/mixed':
+                    if(!isset($content_type['other']['boundary'])){
+                        $this->_error = 'No boundary found for ' . $content_type['value'] . ' part';
+                        return false;
+                    }
+
+                    $default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain';
+
+                    $parts = $this->_boundarySplit($body, $content_type['other']['boundary']);
+                    for ($i = 0; $i < count($parts); $i++) {
+                        list($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]);
+                        $part = $this->_decode($part_header, $part_body, $default_ctype);
+                        if($part === false)
+                            $part = $this->raiseError($this->_error);
+                        $return->parts[] = $part;
+                    }
+                    break;
+
+                case 'message/rfc822':
+                    $obj = &new Mail_mimeDecode($body);
+                    $return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies,
+                                                                             'decode_bodies'  => $this->_decode_bodies,
+                                                                                                                 'decode_headers' => $this->_decode_headers));
+                    unset($obj);
+                    break;
+
+                default:
+                    if(!isset($content_transfer_encoding['value']))
+                        $content_transfer_encoding['value'] = '7bit';
+                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value']) : $body) : null;
+                    break;
+            }
+
+        } else {
+            $ctype = explode('/', $default_ctype);
+            $return->ctype_primary   = $ctype[0];
+            $return->ctype_secondary = $ctype[1];
+            $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null;
+        }
+
+        return $return;
+    }
+
+    /**
+     * Given the output of the above function, this will return an
+     * array of references to the parts, indexed by mime number.
+     *
+     * @param  object $structure   The structure to go through
+     * @param  string $mime_number Internal use only.
+     * @return array               Mime numbers
+     */
+    function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '')
+    {
+        $return = array();
+        if (!empty($structure->parts)) {
+            if ($mime_number != '') {
+                $structure->mime_id = $prepend . $mime_number;
+                $return[$prepend . $mime_number] = &$structure;
+            }
+            for ($i = 0; $i < count($structure->parts); $i++) {
+
+            
+                if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') {
+                    $prepend      = $prepend . $mime_number . '.';
+                    $_mime_number = '';
+                } else {
+                    $_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1));
+                }
+
+                $arr = &Mail_mimeDecode::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend);
+                foreach ($arr as $key => $val) {
+                    $no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key];
+                }
+            }
+        } else {
+            if ($mime_number == '') {
+                $mime_number = '1';
+            }
+            $structure->mime_id = $prepend . $mime_number;
+            $no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure;
+        }
+        
+        return $return;
+    }
+
+    /**
+     * Given a string containing a header and body
+     * section, this function will split them (at the first
+     * blank line) and return them.
+     *
+     * @param string Input to split apart
+     * @return array Contains header and body section
+     * @access private
+     */
+    function _splitBodyHeader($input)
+    {
+        if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) {
+            return array($match[1], $match[2]);
+        }
+        $this->_error = 'Could not split header and body';
+        return false;
+    }
+
+    /**
+     * Parse headers given in $input and return
+     * as assoc array.
+     *
+     * @param string Headers to parse
+     * @return array Contains parsed headers
+     * @access private
+     */
+    function _parseHeaders($input)
+    {
+
+        if ($input !== '') {
+            // Unfold the input
+            $input   = preg_replace("/\r?\n/", "\r\n", $input);
+            $input   = preg_replace("/\r\n(\t| )+/", ' ', $input);
+            $headers = explode("\r\n", trim($input));
+
+            foreach ($headers as $value) {
+                $hdr_name = substr($value, 0, $pos = strpos($value, ':'));
+                $hdr_value = substr($value, $pos+1);
+                if($hdr_value[0] == ' ')
+                    $hdr_value = substr($hdr_value, 1);
+
+                $return[] = array(
+                                  'name'  => $hdr_name,
+                                  'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value
+                                 );
+            }
+        } else {
+            $return = array();
+        }
+
+        return $return;
+    }
+
+    /**
+     * Function to parse a header value,
+     * extract first part, and any secondary
+     * parts (after ;) This function is not as
+     * robust as it could be. Eg. header comments
+     * in the wrong place will probably break it.
+     *
+     * @param string Header value to parse
+     * @return array Contains parsed result
+     * @access private
+     */
+    function _parseHeaderValue($input)
+    {
+
+        if (($pos = strpos($input, ';')) !== false) {
+
+            $return['value'] = trim(substr($input, 0, $pos));
+            $input = trim(substr($input, $pos+1));
+
+            if (strlen($input) > 0) {
+
+                // This splits on a semi-colon, if there's no preceeding backslash
+                // Now works with quoted values; had to glue the \; breaks in PHP
+                // the regex is already bordering on incomprehensible
+                $splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/';
+                preg_match_all($splitRegex, $input, $matches);
+                $parameters = array();
+                for ($i=0; $i<count($matches[0]); $i++) {
+                    $param = $matches[0][$i];
+                    while (substr($param, -2) == '\;') {
+                        $param .= $matches[0][++$i];
+                    }
+                    $parameters[] = $param;
+                }
+
+                for ($i = 0; $i < count($parameters); $i++) {
+                    $param_name  = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ ");
+                    $param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ ");
+                    if ($param_value[0] == '"') {
+                        $param_value = substr($param_value, 1, -1);
+                    }
+                    $return['other'][$param_name] = $param_value;
+                    $return['other'][strtolower($param_name)] = $param_value;
+                }
+            }
+        } else {
+            $return['value'] = trim($input);
+        }
+
+        return $return;
+    }
+
+    /**
+     * This function splits the input based
+     * on the given boundary
+     *
+     * @param string Input to parse
+     * @return array Contains array of resulting mime parts
+     * @access private
+     */
+    function _boundarySplit($input, $boundary)
+    {
+        $parts = array();
+
+        $bs_possible = substr($boundary, 2, -2);
+        $bs_check = '\"' . $bs_possible . '\"';
+
+        if ($boundary == $bs_check) {
+            $boundary = $bs_possible;
+        }
+
+        $tmp = explode('--' . $boundary, $input);
+
+        for ($i = 1; $i < count($tmp) - 1; $i++) {
+            $parts[] = $tmp[$i];
+        }
+
+        return $parts;
+    }
+
+    /**
+     * Given a header, this function will decode it
+     * according to RFC2047. Probably not *exactly*
+     * conformant, but it does pass all the given
+     * examples (in RFC2047).
+     *
+     * @param string Input header value to decode
+     * @return string Decoded header value
+     * @access private
+     */
+    function _decodeHeader($input)
+    {
+        // Remove white space between encoded-words
+        $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input);
+
+        // For each encoded-word...
+        while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) {
+
+            $encoded  = $matches[1];
+            $charset  = $matches[2];
+            $encoding = $matches[3];
+            $text     = $matches[4];
+
+            switch (strtolower($encoding)) {
+                case 'b':
+                    $text = base64_decode($text);
+                    break;
+
+                case 'q':
+                    $text = str_replace('_', ' ', $text);
+                    preg_match_all('/=([a-f0-9]{2})/i', $text, $matches);
+                    foreach($matches[1] as $value)
+                        $text = str_replace('='.$value, chr(hexdec($value)), $text);
+                    break;
+            }
+
+            $input = str_replace($encoded, $text, $input);
+        }
+
+        return $input;
+    }
+
+    /**
+     * Given a body string and an encoding type,
+     * this function will decode and return it.
+     *
+     * @param  string Input body to decode
+     * @param  string Encoding type to use.
+     * @return string Decoded body
+     * @access private
+     */
+    function _decodeBody($input, $encoding = '7bit')
+    {
+        switch (strtolower($encoding)) {
+            case '7bit':
+                return $input;
+                break;
+
+            case 'quoted-printable':
+                return $this->_quotedPrintableDecode($input);
+                break;
+
+            case 'base64':
+                return base64_decode($input);
+                break;
+
+            default:
+                return $input;
+        }
+    }
+
+    /**
+     * Given a quoted-printable string, this
+     * function will decode and return it.
+     *
+     * @param  string Input body to decode
+     * @return string Decoded body
+     * @access private
+     */
+    function _quotedPrintableDecode($input)
+    {
+        // Remove soft line breaks
+        $input = preg_replace("/=\r?\n/", '', $input);
+
+        // Replace encoded characters
+               $input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input);
+
+        return $input;
+    }
+
+    /**
+     * Checks the input for uuencoded files and returns
+     * an array of them. Can be called statically, eg:
+     *
+     * $files =& Mail_mimeDecode::uudecode($some_text);
+     *
+     * It will check for the begin 666 ... end syntax
+     * however and won't just blindly decode whatever you
+     * pass it.
+     *
+     * @param  string Input body to look for attahcments in
+     * @return array  Decoded bodies, filenames and permissions
+     * @access public
+     * @author Unknown
+     */
+    function &uudecode($input)
+    {
+        // Find all uuencoded sections
+        preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", $input, $matches);
+
+        for ($j = 0; $j < count($matches[3]); $j++) {
+
+            $str      = $matches[3][$j];
+            $filename = $matches[2][$j];
+            $fileperm = $matches[1][$j];
+
+            $file = '';
+            $str = preg_split("/\r?\n/", trim($str));
+            $strlen = count($str);
+
+            for ($i = 0; $i < $strlen; $i++) {
+                $pos = 1;
+                $d = 0;
+                $len=(int)(((ord(substr($str[$i],0,1)) -32) - ' ') & 077);
+
+                while (($d + 3 <= $len) AND ($pos + 4 <= strlen($str[$i]))) {
+                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
+                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
+                    $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
+                    $c3 = (ord(substr($str[$i],$pos+3,1)) ^ 0x20);
+                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
+
+                    $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
+
+                    $file .= chr(((($c2 - ' ') & 077) << 6) |  (($c3 - ' ') & 077));
+
+                    $pos += 4;
+                    $d += 3;
+                }
+
+                if (($d + 2 <= $len) && ($pos + 3 <= strlen($str[$i]))) {
+                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
+                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
+                    $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20);
+                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
+
+                    $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2));
+
+                    $pos += 3;
+                    $d += 2;
+                }
+
+                if (($d + 1 <= $len) && ($pos + 2 <= strlen($str[$i]))) {
+                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20);
+                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20);
+                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4));
+
+                }
+            }
+            $files[] = array('filename' => $filename, 'fileperm' => $fileperm, 'filedata' => $file);
+        }
+
+        return $files;
+    }
+
+    /**
+     * getSendArray() returns the arguments required for Mail::send()
+     * used to build the arguments for a mail::send() call 
+     *
+     * Usage:
+     * $mailtext = Full email (for example generated by a template)
+     * $decoder = new Mail_mimeDecode($mailtext);
+     * $parts =  $decoder->getSendArray();
+     * if (!PEAR::isError($parts) {
+     *     list($recipents,$headers,$body) = $parts;
+     *     $mail = Mail::factory('smtp');
+     *     $mail->send($recipents,$headers,$body);
+     * } else {
+     *     echo $parts->message;
+     * }
+     * @return mixed   array of recipeint, headers,body or Pear_Error
+     * @access public
+     * @author Alan Knowles <alan@akbkhome.com>
+     */
+    function getSendArray()
+    {
+        // prevent warning if this is not set
+        $this->_decode_headers = FALSE;
+        $headerlist =$this->_parseHeaders($this->_header);
+        $to = "";
+        if (!$headerlist) {
+            return $this->raiseError("Message did not contain headers");
+        }
+        foreach($headerlist as $item) {
+            $header[$item['name']] = $item['value'];
+            switch (strtolower($item['name'])) {
+                case "to":
+                case "cc":
+                case "bcc":
+                    $to = ",".$item['value'];
+                default:
+                   break;
+            }
+        }
+        if ($to == "") {
+            return $this->raiseError("Message did not contain any recipents");
+        }
+        $to = substr($to,1);
+        return array($to,$header,$this->_body);
+    } 
+
+    /**
+     * Returns a xml copy of the output of
+     * Mail_mimeDecode::decode. Pass the output in as the
+     * argument. This function can be called statically. Eg:
+     *
+     * $output = $obj->decode();
+     * $xml    = Mail_mimeDecode::getXML($output);
+     *
+     * The DTD used for this should have been in the package. Or
+     * alternatively you can get it from cvs, or here:
+     * http://www.phpguru.org/xmail/xmail.dtd.
+     *
+     * @param  object Input to convert to xml. This should be the
+     *                output of the Mail_mimeDecode::decode function
+     * @return string XML version of input
+     * @access public
+     */
+    function getXML($input)
+    {
+        $crlf    =  "\r\n";
+        $output  = '<?xml version=\'1.0\'?>' . $crlf .
+                   '<!DOCTYPE email SYSTEM "http://www.phpguru.org/xmail/xmail.dtd">' . $crlf .
+                   '<email>' . $crlf .
+                   Mail_mimeDecode::_getXML($input) .
+                   '</email>';
+
+        return $output;
+    }
+
+    /**
+     * Function that does the actual conversion to xml. Does a single
+     * mimepart at a time.
+     *
+     * @param  object  Input to convert to xml. This is a mimepart object.
+     *                 It may or may not contain subparts.
+     * @param  integer Number of tabs to indent
+     * @return string  XML version of input
+     * @access private
+     */
+    function _getXML($input, $indent = 1)
+    {
+        $htab    =  "\t";
+        $crlf    =  "\r\n";
+        $output  =  '';
+        $headers = @(array)$input->headers;
+
+        foreach ($headers as $hdr_name => $hdr_value) {
+
+            // Multiple headers with this name
+            if (is_array($headers[$hdr_name])) {
+                for ($i = 0; $i < count($hdr_value); $i++) {
+                    $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value[$i], $indent);
+                }
+
+            // Only one header of this sort
+            } else {
+                $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value, $indent);
+            }
+        }
+
+        if (!empty($input->parts)) {
+            for ($i = 0; $i < count($input->parts); $i++) {
+                $output .= $crlf . str_repeat($htab, $indent) . '<mimepart>' . $crlf .
+                           Mail_mimeDecode::_getXML($input->parts[$i], $indent+1) .
+                           str_repeat($htab, $indent) . '</mimepart>' . $crlf;
+            }
+        } elseif (isset($input->body)) {
+            $output .= $crlf . str_repeat($htab, $indent) . '<body><![CDATA[' .
+                       $input->body . ']]></body>' . $crlf;
+        }
+
+        return $output;
+    }
+
+    /**
+     * Helper function to _getXML(). Returns xml of a header.
+     *
+     * @param  string  Name of header
+     * @param  string  Value of header
+     * @param  integer Number of tabs to indent
+     * @return string  XML version of input
+     * @access private
+     */
+    function _getXML_helper($hdr_name, $hdr_value, $indent)
+    {
+        $htab   = "\t";
+        $crlf   = "\r\n";
+        $return = '';
+
+        $new_hdr_value = ($hdr_name != 'received') ? Mail_mimeDecode::_parseHeaderValue($hdr_value) : array('value' => $hdr_value);
+        $new_hdr_name  = str_replace(' ', '-', ucwords(str_replace('-', ' ', $hdr_name)));
+
+        // Sort out any parameters
+        if (!empty($new_hdr_value['other'])) {
+            foreach ($new_hdr_value['other'] as $paramname => $paramvalue) {
+                $params[] = str_repeat($htab, $indent) . $htab . '<parameter>' . $crlf .
+                            str_repeat($htab, $indent) . $htab . $htab . '<paramname>' . htmlspecialchars($paramname) . '</paramname>' . $crlf .
+                            str_repeat($htab, $indent) . $htab . $htab . '<paramvalue>' . htmlspecialchars($paramvalue) . '</paramvalue>' . $crlf .
+                            str_repeat($htab, $indent) . $htab . '</parameter>' . $crlf;
+            }
+
+            $params = implode('', $params);
+        } else {
+            $params = '';
+        }
+
+        $return = str_repeat($htab, $indent) . '<header>' . $crlf .
+                  str_repeat($htab, $indent) . $htab . '<headername>' . htmlspecialchars($new_hdr_name) . '</headername>' . $crlf .
+                  str_repeat($htab, $indent) . $htab . '<headervalue>' . htmlspecialchars($new_hdr_value['value']) . '</headervalue>' . $crlf .
+                  $params .
+                  str_repeat($htab, $indent) . '</header>' . $crlf;
+
+        return $return;
+    }
+
+} // End of class
diff --git a/extlib/System/Command.php b/extlib/System/Command.php
new file mode 100644 (file)
index 0000000..f5c3ec6
--- /dev/null
@@ -0,0 +1,587 @@
+<?php
+// {{{ license
+
+// +----------------------------------------------------------------------+
+// | PHP Version 4.0                                                      |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Anders Johannsen <anders@johannsen.com>                      |
+// | Author: Dan Allen <dan@mojavelinux.com>
+// +----------------------------------------------------------------------+
+
+// $Id: Command.php,v 1.9 2007/04/20 21:08:48 cconstantine Exp $
+
+// }}}
+// {{{ includes
+
+require_once 'PEAR.php';
+require_once 'System.php';
+
+// }}}
+// {{{ constants
+
+define('SYSTEM_COMMAND_OK',                 1);
+define('SYSTEM_COMMAND_ERROR',             -1);
+define('SYSTEM_COMMAND_NO_SHELL',          -2);
+define('SYSTEM_COMMAND_INVALID_SHELL',     -3);
+define('SYSTEM_COMMAND_TMPDIR_ERROR',      -4);
+define('SYSTEM_COMMAND_INVALID_OPERATOR',  -5);
+define('SYSTEM_COMMAND_INVALID_COMMAND',   -6);
+define('SYSTEM_COMMAND_OPERATOR_PLACEMENT',-7);
+define('SYSTEM_COMMAND_COMMAND_PLACEMENT', -8);
+define('SYSTEM_COMMAND_NOHUP_MISSING',     -9);
+define('SYSTEM_COMMAND_NO_OUTPUT',        -10);
+define('SYSTEM_COMMAND_STDERR',           -11);
+define('SYSTEM_COMMAND_NONZERO_EXIT',     -12);
+
+// }}}
+
+// {{{ class System_Command
+
+/**
+ * The System_Command:: class implements an abstraction for various ways 
+ * of executing commands (directly using the backtick operator,
+ * as a background task after the script has terminated using
+ * register_shutdown_function() or as a detached process using nohup).
+ *
+ * @author  Anders Johannsen <anders@johannsen.com>
+ * @author  Dan Allen <dan@mojavelinux.com>
+ * @version $Revision: 1.9 $
+ */
+
+// }}}
+class System_Command {
+    // {{{ properties
+
+    /**
+     * Array of settings used when creating the shell command
+     *
+     * @var array
+     * @access private
+     */
+    var $options = array();
+
+    /**
+     * Array of available shells to use to execute the command
+     *
+     * @var array
+     * @access private
+     */
+    var $shells = array();
+
+    /**
+     * Array of available control operators used between commands
+     *
+     * @var array
+     * @access private
+     */
+    var $controlOperators = array();
+
+    /**
+     * The system command to be executed
+     *
+     * @var string
+     * @access private
+     */
+    var $systemCommand = null;
+
+    /**
+     * Previously added part to the command string
+     *
+     * @var string
+     * @access private
+     */
+    var $previousElement = null;
+
+    /**
+     * Directory for writing stderr output
+     *
+     * @var string
+     * @access private
+     */
+    var $tmpDir = null;
+
+    /**
+     * To allow the pear error object to accumulate when building
+     * the command, we use the command status to keep track when
+     * a pear error is raised
+     *
+     * @var int
+     * @access private
+     */
+    var $commandStatus = 0;
+    
+    /**
+     * Hold initialization PEAR_Error
+     *
+     * @var object
+     * @access private
+     **/
+    var $_initError = null;
+        
+    // }}}
+    // {{{ constructor
+
+    /**
+     * Class constructor
+     * 
+     * Defines all necessary constants and sets defaults
+     * 
+     * @access public
+     */
+    function System_Command($in_shell = null)
+    {
+        // Defining constants
+        $this->options = array(
+            'SEQUENCE'   => true,
+            'SHUTDOWN'   => false,
+            'SHELL'      => $this->which($in_shell),
+            'OUTPUT'     => true,
+            'NOHUP'      => false,
+            'BACKGROUND' => false,
+            'STDERR'     => false
+        );
+
+        // prepare the available control operators
+        $this->controlOperators = array(
+            'PIPE'  => '|',
+            'AND'   => '&&',
+            'OR'    => '||',
+            'GROUP' => ';',
+            'LFIFO' => '<',
+            'RFIFO' => '>',
+        );
+                
+        // List of allowed/available shells
+        $this->shells = array(
+            'sh',
+            'bash',
+            'zsh',
+            'tcsh',
+            'csh',
+            'ash',
+            'sash',
+            'esh',
+            'ksh'
+        );
+                                   
+        // Find the first available shell
+        if (empty($this->options['SHELL'])) {
+            foreach ($this->shells as $shell) {
+                if ($this->options['SHELL'] = $this->which($shell)) {
+                    break;
+                }
+            }
+
+            // see if we still have no shell
+            if (empty($this->options['SHELL'])) {
+               $this->_initError =& PEAR::raiseError(null, SYSTEM_COMMAND_NO_SHELL, null, E_USER_WARNING, null, 'System_Command_Error', true);
+                return;
+            }
+        }
+
+        // Caputre a temporary directory for capturing stderr from commands
+        $this->tmpDir = System::tmpdir();
+        if (!System::mkDir("-p {$this->tmpDir}")) {
+            $this->_initError =& PEAR::raiseError(null, SYSTEM_COMMAND_TMPDIR_ERROR, null, E_USER_WARNING, null, 'System_Command_Error', true);
+            return;
+        }
+    }
+        
+    // }}}
+    // {{{ setOption()
+
+    /**
+     * Sets the value for an option. Each option should be set to true
+     * or false; except the 'SHELL' option which should be a string
+     * naming a shell. The options are:
+     * 
+     * 'SEQUENCE'   Allow a sequence command or not (right now this is always on);
+     *
+     * 'SHUTDOWN'   Execute commands via a shutdown function;
+     *
+     * 'SHELL'      Path to shell;
+     *
+     * 'OUTPUT'     Output stdout from process;
+     *
+     * 'NOHUP'      Use nohup to detach process;
+     *
+     * 'BACKGROUND' Run as a background process with &;
+     *
+     * 'STDERR'     Output on stderr will raise an error, even if
+     *              the command's exit value is zero. The output from
+     *              stderr can be retrieved using the getDebugInfo()
+     *              method of the Pear_ERROR object returned by
+     *              execute().;
+     *
+     * @param string $in_option is a case-sensitive string,
+     *                          corresponding to the option
+     *                          that should be changed
+     * @param mixed $in_setting is the new value for the option
+     * @access public
+     * @return bool true if succes, else false
+     */
+    function setOption($in_option, $in_setting)
+    {
+       if ($this->_initError) {
+            return $this->_initError;
+        }
+
+        $option = strtoupper($in_option);
+
+        if (!isset($this->options[$option])) {
+            PEAR::raiseError(null, SYSTEM_COMMAND_ERROR, null, E_USER_NOTICE, null, 'System_Command_Error', true);
+            return false;
+        }
+                
+        switch ($option) {
+            case 'OUTPUT':
+            case 'SHUTDOWN':
+            case 'SEQUENCE':
+            case 'BACKGROUND':
+            case 'STDERR':
+                $this->options[$option] = !empty($in_setting);
+                return true;
+            break;
+                
+            case 'SHELL':
+                if (($shell = $this->which($in_setting)) !== false) {
+                    $this->options[$option] = $shell;
+                    return true;
+                } 
+                else {
+                    PEAR::raiseError(null, SYSTEM_COMMAND_NO_SHELL, null, E_USER_NOTICE, $in_setting, 'System_Command_Error', true);
+                    return false;
+                }
+            break;
+                        
+            case 'NOHUP':
+                if (empty($in_setting)) {
+                    $this->options[$option] = false;
+                } 
+                else if ($location = $this->which('nohup')) {
+                    $this->options[$option] = $location;
+                } 
+                else {
+                    PEAR::raiseError(null, SYSTEM_COMMAND_NOHUP_MISSING, null, E_USER_NOTICE, null, 'System_Command_Error', true);
+                    return false;
+                }
+            break;
+        }
+    }
+    
+    // }}}
+    // {{{ pushCommand()
+
+    /**
+     * Used to push a command onto the running command to be executed
+     *
+     * @param  string $in_command binary to be run
+     * @param  string $in_argument either an option or argument value, to be handled appropriately
+     * @param  string $in_argument
+     * @param  ...
+     *
+     * @access public
+     * @return boolean true on success {or System_Command_Error Exception}
+     */
+    function pushCommand($in_command)
+    {
+       if ($this->_initError) {
+            return $this->_initError;
+        }
+        
+        if (!is_null($this->previousElement) && !in_array($this->previousElement, $this->controlOperators)) {
+            $this->commandStatus = -1;
+            $error = PEAR::raiseError(null, SYSTEM_COMMAND_COMMAND_PLACEMENT, null, E_USER_WARNING, null, 'System_Command_Error', true);
+        }
+
+        // check for error here
+        $command = escapeshellcmd($this->which($in_command));
+        if ($command === false) {
+            $error = PEAR::raiseError(null, SYSTEM_COMMAND_INVALID_COMMAND, null, E_USER_WARNING, null, 'System_Command_Error', true);
+        }
+
+        $argv = func_get_args();
+        array_shift($argv);
+        foreach($argv as $arg) {
+            if (strpos($arg, '-') === 0) {
+                $command .= ' ' . $arg; 
+            }
+            elseif ($arg != '') {
+                $command .= ' ' . escapeshellarg($arg);
+            }
+        }
+
+        $this->previousElement = $command;
+        $this->systemCommand .= $command;
+
+        return isset($error) ? $error : true;
+    }
+
+    // }}}
+    // {{{ pushOperator()
+
+    /**
+     * Used to push an operator onto the running command to be executed
+     *
+     * @param  string $in_operator Either string reprentation of operator or system character
+     *
+     * @access public
+     * @return boolean true on success {or System_Command_Error Exception}
+     */
+    function pushOperator($in_operator)
+    {
+       if ($this->_initError) {
+            return $this->_initError;
+        }
+
+        $operator = isset($this->controlOperators[$in_operator]) ? $this->controlOperators[$in_operator] : $in_operator;
+
+        if (is_null($this->previousElement) || in_array($this->previousElement, $this->controlOperators)) {
+            $this->commandStatus = -1;
+            $error = PEAR::raiseError(null, SYSTEM_COMMAND_OPERATOR_PLACEMENT, null, E_USER_WARNING, null, 'System_Command_Error', true);
+        }
+        elseif (!in_array($operator, $this->controlOperators)) {
+            $this->commandStatus = -1;
+            $error = PEAR::raiseError(null, SYSTEM_COMMAND_INVALID_OPERATOR, null, E_USER_WARNING, $operator, 'System_Command_Error', true);
+        }
+
+        $this->previousElement = $operator;
+        $this->systemCommand .= ' ' . $operator . ' ';
+        return isset($error) ? $error : true;
+    }
+
+    // }}}
+    // {{{ execute()
+
+    /**
+     * Executes the code according to given options
+     *
+     * @return bool true if success {or System_Command_Exception}
+     *
+     * @access public
+     */
+    function execute() 
+    {
+       if ($this->_initError) {
+            return $this->_initError;
+        }
+
+        // if the command is empty or if the last element was a control operator, we can't continue
+        if (is_null($this->previousElement) || $this->commandStatus == -1 || in_array($this->previousElement, $this->controlOperators)) {
+            return PEAR::raiseError(null, SYSTEM_COMMAND_INVALID_COMMAND, null, E_USER_WARNING, $this->systemCommand, 'System_Command_Error', true);
+        }
+
+        // Warning about impossible mix of options
+        if (!empty($this->options['OUTPUT'])) {
+            if (!empty($this->options['SHUTDOWN']) || !empty($this->options['NOHUP'])) {
+                return PEAR::raiseError(null, SYSTEM_COMMAND_NO_OUTPUT, null, E_USER_WARNING, null, 'System_Command_Error', true);
+            }
+        }
+                
+        // if this is not going to stdout, then redirect to /dev/null
+        if (empty($this->options['OUTPUT'])) {
+            $this->systemCommand .= ' >/dev/null';
+        }
+                
+        $suffix = '';
+        // run a command immune to hangups, with output to a non-tty
+        if (!empty($this->options['NOHUP'])) {
+            $this->systemCommand = $this->options['NOHUP'] . $this->systemCommand;
+        }
+        // run a background process (only if not nohup)
+        elseif (!empty($this->options['BACKGROUND'])) {
+            $suffix = ' &';
+        }
+                
+        // Register to be run on shutdown
+        if (!empty($this->options['SHUTDOWN'])) {
+            $line = "system(\"{$this->systemCommand}$suffix\");";
+            $function = create_function('', $line);
+            register_shutdown_function($function);
+            return true;
+        } 
+        else {
+            // send stderr to a file so that we can reap the error message
+            $tmpFile = tempnam($this->tmpDir, 'System_Command-');
+            $this->systemCommand .= ' 2>' . $tmpFile . $suffix;
+            $shellPipe = $this->which('echo') . ' ' . escapeshellarg($this->systemCommand) . ' | ' . $this->options['SHELL'];
+            exec($shellPipe, $result, $returnVal);
+
+            if ($returnVal !== 0) {
+                // command returned nonzero; that's always an error
+                $return = PEAR::raiseError(null, SYSTEM_COMMAND_NONZERO_EXIT, null, E_USER_WARNING, null, 'System_Command_Error', true);
+            }
+            else if (!$this->options['STDERR']) {
+                // caller does not care about stderr; return success
+                $return = implode("\n", $result);
+            }
+            else {
+                // our caller cares about stderr; check stderr output
+                clearstatcache();
+                if (filesize($tmpFile) > 0) {
+                    // the command actually wrote to stderr
+                    $stderr_output = file_get_contents($tmpFile);
+                    $return = PEAR::raiseError(null, SYSTEM_COMMAND_STDERR, null, E_USER_WARNING, $stderr_output, 'System_Command_Error', true);
+                } else {
+                    // total success; return stdout gathered by exec()
+                    $return = implode("\n", $result);
+                }
+            }
+
+            unlink($tmpFile);
+            return $return;
+        }
+    }
+
+    // }}}
+    // {{{ which()
+
+    /**
+     * Functionality similiar to unix 'which'. Searches the path
+     * for the specified program. 
+     *
+     * @param $cmd name of the executable to search for 
+     *
+     * @access private
+     * @return string returns the full path if found, false if not
+     */
+    function which($in_cmd)
+    {
+        // only pass non-empty strings to System::which()
+        if (!is_string($in_cmd) || '' === $in_cmd) {
+            return(false);
+        }
+
+        // explicitly pass false as fallback value
+        return System::which($in_cmd, false);
+    }   
+
+    // }}}
+    // {{{ reset()
+
+    /**
+     * Prepare for a new command to be built
+     *
+     * @access public
+     * @return void
+     */
+    function reset()
+    {
+        $this->previousElement = null;
+        $this->systemCommand = null;
+        $this->commandStatus = 0;
+    }
+
+    // }}}
+    // {{{ errorMessage()
+
+    /**
+     * Return a textual error message for a System_Command error code
+     *
+     * @param integer error code
+     *
+     * @return string error message, or false if the error code was
+     * not recognized
+     */
+    function errorMessage($in_value)
+    {
+        static $errorMessages;
+        if (!isset($errorMessages)) {
+            $errorMessages = array(
+                SYSTEM_COMMAND_OK                     => 'no error',
+                SYSTEM_COMMAND_ERROR                  => 'unknown error',
+                SYSTEM_COMMAND_NO_SHELL               => 'no shell found',
+                SYSTEM_COMMAND_INVALID_SHELL          => 'invalid shell',
+                SYSTEM_COMMAND_TMPDIR_ERROR           => 'could not create temporary directory',
+                SYSTEM_COMMAND_INVALID_OPERATOR       => 'control operator invalid',
+                SYSTEM_COMMAND_INVALID_COMMAND        => 'invalid system command',
+                SYSTEM_COMMAND_OPERATOR_PLACEMENT     => 'invalid placement of control operator',
+                SYSTEM_COMMAND_COMMAND_PLACEMENT      => 'invalid placement of command',
+                SYSTEM_COMMAND_NOHUP_MISSING          => 'nohup not found on system',
+                SYSTEM_COMMAND_NO_OUTPUT              => 'output not allowed',
+                SYSTEM_COMMAND_STDERR                 => 'command wrote to stderr',
+                SYSTEM_COMMAND_NONZERO_EXIT           => 'non-zero exit value from command',
+            );
+        }
+
+        if (System_Command::isError($in_value)) {
+            $in_value = $in_value->getCode();
+        }
+
+        return isset($errorMessages[$in_value]) ? $errorMessages[$in_value] : $errorMessages[SYSTEM_COMMAND_ERROR];
+    }
+
+    // }}}
+    // {{{ isError()
+
+    /**
+     * Tell whether a result code from a System_Command method is an error
+     *
+     * @param int result code
+     *
+     * @return bool whether $in_value is an error
+     *
+     * @access public
+     */
+    function isError($in_value)
+    {
+        return (is_object($in_value) &&
+                (strtolower(get_class($in_value)) == 'system_command_error' ||
+                 is_subclass_of($in_value, 'system_command_error')));
+    }
+    
+    // }}}
+}
+
+// {{{ class System_Command_Error
+
+/**
+ * System_Command_Error constructor.
+ *
+ * @param mixed      System_Command error code, or string with error message.
+ * @param integer    what "error mode" to operate in
+ * @param integer    what error level to use for $mode & PEAR_ERROR_TRIGGER
+ * @param mixed      additional debug info, such as the last query
+ *
+ * @access public
+ *
+ * @see PEAR_Error
+ */
+
+// }}}
+class System_Command_Error extends PEAR_Error
+{
+    // {{{ properties
+
+    /**
+     * Message in front of the error message
+     * @var string $error_message_prefix
+     */
+    var $error_message_prefix = 'System_Command Error: ';
+
+    // }}}
+    // {{{ constructor
+
+    function System_Command_Error($code = SYSTEM_COMMAND_ERROR, $mode = PEAR_ERROR_RETURN,
+              $level = E_USER_NOTICE, $debuginfo = null)
+    {
+        if (is_int($code)) {
+            $this->PEAR_Error(System_Command::errorMessage($code), $code, $mode, $level, $debuginfo);
+        } else {
+            $this->PEAR_Error("Invalid error code: $code", SYSTEM_COMMAND_ERROR, $mode, $level, $debuginfo);
+        }
+    }
+    
+    // }}}
+}
+?>
index b147443d79bcb28ac88e67bef52dbe0d64c71c36..befaf60a77151cc533743ab5adb4211a05971d94 100644 (file)
@@ -27,7 +27,7 @@
  */
 
 /** XMPPHP_XMLStream */
-require_once "XMPP.php";
+require_once dirname(__FILE__) . "/XMPP.php";
 
 /**
  * XMPPHP Main Class
index 0fcfea375e1d4110925dbe63a34310985e232dbb..d33411ec54140dafc1c527c946d05cbc931807c9 100644 (file)
  */
 
 /** XMPPHP_Exception */
-require_once 'Exception.php';
+require_once dirname(__FILE__) . '/Exception.php';
 
 /** XMPPHP_XMLObj */
-require_once 'XMLObj.php';
+require_once dirname(__FILE__) . '/XMLObj.php';
 
 /** XMPPHP_Log */
-require_once 'Log.php';
+require_once dirname(__FILE__) . '/Log.php';
 
 /**
  * XMPPHP XML Stream
@@ -375,7 +375,7 @@ class XMPPHP_XMLStream {
         * integer -> process for this amount of time 
         */
        
-       private function __process($maximum=0) {
+       private function __process($maximum=5) {
                
                $remaining = $maximum;
                
index 73fbd265840b878e176aba334a7cf984bca72689..429f45e565eb57648930988c8133b73bd6d26c5d 100644 (file)
@@ -27,8 +27,8 @@
  */
 
 /** XMPPHP_XMLStream */
-require_once "XMLStream.php";
-require_once "Roster.php";
+require_once dirname(__FILE__) . "/XMLStream.php";
+require_once dirname(__FILE__) . "/Roster.php";
 
 /**
  * XMPPHP Main Class
@@ -208,6 +208,15 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream {
                
                $this->send($out);
        }
+       /**
+        * Send Auth request
+        *
+        * @param string $jid
+        */
+       public function subscribe($jid) {
+               $this->send("<presence type='subscribe' to='{$jid}' from='{$this->fulljid}' />");
+               #$this->send("<presence type='subscribed' to='{$jid}' from='{$this->fulljid}' />");
+       }
 
        /**
         * Message handler
index cb26e21961412e2cc59b41965762e3acb74965ed..5f9a048f2c7225917dcc56bfbb1b7326067ef4f9 100644 (file)
--- a/index.php
+++ b/index.php
@@ -48,7 +48,14 @@ function handleError($error)
         $logmsg .= " : ". $error->getDebugInfo();
     }
     common_log(LOG_ERR, $logmsg);
-    if ($error instanceof DB_DataObject_Error) {
+    if(common_config('site', 'logdebug')) {
+        $bt = $error->getBacktrace();
+        foreach ($bt as $line) {
+            common_log(LOG_ERR, $line);
+        }
+    }
+    if ($error instanceof DB_DataObject_Error ||
+        $error instanceof DB_Error) {
         $msg = sprintf(_('The database for %s isn\'t responding correctly, '.
                          'so the site won\'t work properly. '.
                          'The site admins probably know about the problem, '.
@@ -66,13 +73,45 @@ function handleError($error)
     exit(-1);
 }
 
+function checkMirror($action_obj)
+{
+    global $config;
+
+    static $alwaysRW = array('session', 'remember_me');
+
+    if (common_config('db', 'mirror') && $action_obj->isReadOnly($args)) {
+        if (is_array(common_config('db', 'mirror'))) {
+            // "load balancing", ha ha
+            $arr = common_config('db', 'mirror');
+            $k = array_rand($arr);
+            $mirror = $arr[$k];
+        } else {
+            $mirror = common_config('db', 'mirror');
+        }
+
+        // We ensure that these tables always are used
+        // on the master DB
+
+        $config['db']['database_rw'] = $config['db']['database'];
+        $config['db']['ini_rw'] = INSTALLDIR.'/classes/laconica.ini';
+
+        foreach ($alwaysRW as $table) {
+            $config['db']['table_'.$table] = 'rw';
+        }
+
+        // everyone else uses the mirror
+
+        $config['db']['database'] = $mirror;
+    }
+}
+
 function main()
 {
     // quick check for fancy URL auto-detection support in installer.
     if (isset($_SERVER['REDIRECT_URL']) && ((dirname($_SERVER['REQUEST_URI']) . '/check-fancy') === $_SERVER['REDIRECT_URL'])) {
         die("Fancy URL support detection succeeded. We suggest you enable this to get fancy (pretty) URLs.");
     }
-    global $user, $action, $config;
+    global $user, $action;
 
     Snapshot::check();
 
@@ -139,19 +178,7 @@ function main()
     } else {
         $action_obj = new $action_class();
 
-        // XXX: find somewhere for this little block to live
-
-        if (common_config('db', 'mirror') && $action_obj->isReadOnly($args)) {
-            if (is_array(common_config('db', 'mirror'))) {
-                // "load balancing", ha ha
-                $arr = common_config('db', 'mirror');
-                $k = array_rand($arr);
-                $mirror = $arr[$k];
-            } else {
-                $mirror = common_config('db', 'mirror');
-            }
-            $config['db']['database'] = $mirror;
-        }
+        checkMirror($action_obj);
 
         try {
             if ($action_obj->prepare($args)) {
index b94a9293607e7928659c4da53e09323283d61f5b..570b08edf473b13e709ece605656a279d89016de 100644 (file)
@@ -72,6 +72,12 @@ function checkPrereqs()
          <?
             $pass = false;
        }
+       if (!is_writable(INSTALLDIR.'/background/')) {
+         ?><p class="error">Cannot write background directory: <code><?php echo INSTALLDIR; ?>/background/</code></p>
+              <p>On your server, try this command: <code>chmod a+w <?php echo INSTALLDIR; ?>/background/</code></p>
+         <?
+            $pass = false;
+       }
 
        return $pass;
 }
diff --git a/js/jquery.joverlay.js b/js/jquery.joverlay.js
new file mode 100644 (file)
index 0000000..e4effec
--- /dev/null
@@ -0,0 +1,271 @@
+/* Copyright (c) 2009 Alvaro A. Lima Jr http://alvarojunior.com/jquery/joverlay.html
+ * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
+ * Version: 0.7.1 (JUN 15, 2009)
+ * Requires: jQuery 1.3+
+ */
+
+(function($) {
+
+       // Global vars
+       var isIE6 = $.browser.msie && $.browser.version == 6.0; // =(
+       var JOVERLAY_TIMER = null;
+       var     JOVERLAY_ELEMENT_PREV = null;
+
+       $.fn.jOverlay = function(options) {
+
+               // Element exist?
+               if ( $('#jOverlay').length ) {$.closeOverlay();}
+
+               // Clear Element Prev
+               JOVERLAY_ELEMENT_PREV = null;
+
+               // Clear Timer
+               if (JOVERLAY_TIMER !== null) {
+                       clearTimeout( JOVERLAY_TIMER );
+               }
+
+               // Set Options
+               var options = $.extend({}, $.fn.jOverlay.options, options);
+
+               // private function
+               function center(id) {
+                       if (options.center) {
+                               $.center(id);
+                       }
+               }
+
+               var element = this.is('*') ? this : '#jOverlayContent';
+               var position = isIE6 ? 'absolute' : 'fixed';
+               var isImage = /([^\/\\]+)\.(png|gif|jpeg|jpg|bmp)$/i.test( options.url );
+
+               var imgLoading = options.imgLoading ? "<img id='jOverlayLoading' src='"+options.imgLoading+"' style='position:"+position+"; z-index:"+(options.zIndex + 9)+";'/>" : '';
+
+               $('body').prepend(imgLoading + "<div id='jOverlay' />"
+                       + "<div id='jOverlayContent' style='position:"+position+"; z-index:"+(options.zIndex + 5)+"; display:none;'/>"
+               );
+
+               // Loading Centered
+               $('#jOverlayLoading').load(function(){
+                       center(this);
+               });
+
+               //IE 6 FIX
+               if ( isIE6 ) {
+                       $('select').hide();
+                       $('#jOverlayContent select').show();
+               }
+
+               // Overlay Style
+               $('#jOverlay').css({
+                       backgroundColor : options.color,
+                       position : position,
+                       top : '0px',
+                       left : '0px',
+                       filter : 'alpha(opacity='+ (options.opacity * 100) +')', // IE =(
+                       opacity : options.opacity, // Good Browser =D
+                       zIndex : options.zIndex,
+                       width : !isIE6 ? '100%' : $(window).width() + 'px',
+                       height : !isIE6 ? '100%' : $(document).height() + 'px'
+               }).show();
+
+               // ELEMENT
+               if ( this.is('*') ) {
+
+                       JOVERLAY_ELEMENT_PREV = this.prev();
+
+                       $('#jOverlayContent').html(
+                               this.show().attr('display', options.autoHide ? 'none' : this.css('display') )
+                       );
+                       
+                       if ( !isImage ) {
+
+                               center('#jOverlayContent');
+
+                               $('#jOverlayContent').show();
+                               
+                               // Execute callback
+                               if ( !options.url && $.isFunction( options.success ) ) {
+                                       options.success( this );
+                               }
+
+                       }
+
+               }
+
+               // IMAGE
+               if ( isImage ) {
+
+                       $('<img/>').load(function(){
+                               var resize = $.resize(this.width, this.height);
+
+                               $(this).css({
+                                       width : resize.width,
+                                       height : resize.height
+                               });
+
+                               $( element ).html(this);
+
+                               center('#jOverlayContent');
+
+                               $('#jOverlayLoading').fadeOut(500);
+                               $('#jOverlayContent').show();
+
+                               // Execute callback
+                               if ( $.isFunction( options.success ) ) {
+                                       options.success( this );
+                               }
+
+                       }).error(function(){
+                               alert('Image ('+options.url+') not found.');
+                               $.closeOverlay();
+                       }).attr({'src' : options.url, 'alt' : options.url});
+
+               }
+
+               // AJAX
+               if ( options.url && !isImage ) {
+
+                       $.ajax({
+                               type: options.method,
+                               data: options.data,
+                               url: options.url,
+                               success: function(responseText) {
+
+                                       $('#jOverlayLoading').fadeOut(500);
+
+                                       $( element ).html(responseText).show();
+
+                                       center('#jOverlayContent');
+
+                                       // Execute callback
+                                       if ($.isFunction( options.success )) {
+                                               options.success(responseText);
+                                       }
+
+                               },
+                               error : function() {
+                                       alert('URL ('+options.url+') not found.');
+                                       $.closeOverlay();
+                               }
+                       });
+
+               }
+
+               // :(
+               if ( isIE6 ) {
+
+                       // Window scroll
+                       $(window).scroll(function(){
+                               center('#jOverlayContent');
+                       });
+
+                       // Window resize
+                       $(window).resize(function(){
+
+                               $('#jOverlay').css({
+                                       width: $(window).width() + 'px',
+                                       height: $(document).height() + 'px'
+                               });
+
+                               center('#jOverlayContent');
+
+                       });
+
+               }
+
+               // Press ESC to close
+               $(document).keydown(function(event){
+                       if (event.keyCode == 27) {
+                               $.closeOverlay();
+                       }
+               });
+
+               // Click to close
+               if ( options.bgClickToClose ) {
+                       $('#jOverlay').click($.closeOverlay);
+               }
+
+               // Timeout (auto-close)
+               // time in millis to wait before auto-close
+               // set to 0 to disable
+               if ( Number(options.timeout) > 0 ) {
+                       jOverlayTimer = setTimeout( $.closeOverlay, Number(options.timeout) );
+               }
+
+               // ADD CSS
+               $('#jOverlayContent').css(options.css || {});
+       };
+
+       // Resizing large images - orginal by Christian Montoya.
+       // Edited by - Cody Lindley (http://www.codylindley.com) (Thickbox 3.1)
+       $.resize = function(imageWidth, imageHeight) {
+               var x = $(window).width() - 150;
+               var y = $(window).height() - 150;
+               if (imageWidth > x) {
+                       imageHeight = imageHeight * (x / imageWidth); 
+                       imageWidth = x; 
+                       if (imageHeight > y) { 
+                               imageWidth = imageWidth * (y / imageHeight); 
+                               imageHeight = y; 
+                       }
+               } else if (imageHeight > y) { 
+                       imageWidth = imageWidth * (y / imageHeight); 
+                       imageHeight = y; 
+                       if (imageWidth > x) { 
+                               imageHeight = imageHeight * (x / imageWidth); 
+                               imageWidth = x;
+                       }
+               }
+               return {width:imageWidth, height:imageHeight};
+       };
+
+       // Centered Element
+       $.center = function(element) {
+               var element = $(element);
+               var elemWidth = element.width();
+
+               element.css({
+                       width : elemWidth + 'px',
+                       marginLeft : '-' + (elemWidth / 2) + 'px',
+                       marginTop : '-' + element.height() / 2 + 'px',
+                       height : 'auto',
+               top : !isIE6 ? '50%' : $(window).scrollTop() + ($(window).height() / 2) + 'px',
+               left : '50%'
+               });
+       };
+
+       // Options default
+       $.fn.jOverlay.options = {
+               method : 'GET',
+               data : '',
+               url : '',
+               color : '#000',
+               opacity : '0.6',
+               zIndex : 9999,
+               center : true,
+               imgLoading : '',
+               bgClickToClose : true,
+               success : null,
+               timeout : 0,
+               autoHide : true,
+               css : {}
+       };
+
+       // Close
+       $.closeOverlay = function() {
+
+               if (isIE6) { $("select").show(); }
+
+               if ( JOVERLAY_ELEMENT_PREV !== null ) {
+                       if ( JOVERLAY_ELEMENT_PREV !== null ) {
+                               var element = $('#jOverlayContent').children();
+                               JOVERLAY_ELEMENT_PREV.after( element.css('display', element.attr('display') ) );
+                               element.removeAttr('display');
+                       }
+               }
+
+               $('#jOverlayLoading, #jOverlayContent, #jOverlay').remove();
+
+       };
+
+})(jQuery);
\ No newline at end of file
index c9168506a5ae2410277802e6cb2fa9e6370a4d6e..44cd46043fe07bc0212ba6d3d8ab4b5fdc51ba77 100644 (file)
@@ -1,6 +1,7 @@
 /* Copyright (c) 2009 Alvaro A. Lima Jr http://alvarojunior.com/jquery/joverlay.html
  * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
- * Version: 0.6 (Abr 23, 2009)
+ * Version: 0.7.1 (JUN 15, 2009
  * Requires: jQuery 1.3+
+ * Packer from http://dean.edwards.name/packer/
  */
-(function($){var f=$.browser.msie&&$.browser.version==6.0;var g=null;$.fn.jOverlay=function(b){var b=$.extend({},$.fn.jOverlay.options,b);if(g!=null){clearTimeout(g)}var c=this.is('*')?this:'#jOverlayContent';var d=f?'absolute':'fixed';var e=b.imgLoading?"<img id='jOverlayLoading' src='"+b.imgLoading+"' style='position:"+d+"; z-index:"+(b.zIndex+9)+";'/>":'';$('body').prepend(e+"<div id='jOverlay' />"+"<div id='jOverlayContent' style='position:"+d+"; z-index:"+(b.zIndex+5)+"; display:none;'/>");$('#jOverlayLoading').load(function(){if(b.center){$.center(this)}});if(f){$("select").hide();$("#jOverlayContent select").show()}$('#jOverlay').css({backgroundColor:b.color,position:d,top:'0px',left:'0px',filter:'alpha(opacity='+(b.opacity*100)+')',opacity:b.opacity,zIndex:b.zIndex,width:!f?'100%':$(window).width()+'px',height:!f?'100%':$(document).height()+'px'}).show();if(this.is('*')){$('#jOverlayContent').html(this.addClass('jOverlayChildren').show()).show();if(b.center){$.center('#jOverlayContent')}if(!b.url&&$.isFunction(b.success)){b.success(this.html())}}if(b.url){$.ajax({type:b.method,data:b.data,url:b.url,success:function(a){$('#jOverlayLoading').fadeOut(600);$(c).html(a).show();if(b.center){$.center('#jOverlayContent')}if($.isFunction(b.success)){b.success(a)}}})}if(f){$(window).scroll(function(){if(b.center){$.center('#jOverlayContent')}});$(window).resize(function(){$('#jOverlay').css({width:$(window).width()+'px',height:$(document).height()+'px'});if(b.center){$.center('#jOverlayContent')}})}$(document).keydown(function(a){if(a.keyCode==27){$.closeOverlay()}});if(b.bgClickToClose){$('#jOverlay').click($.closeOverlay)}if(Number(b.timeout)>0){g=setTimeout($.closeOverlay,Number(b.timeout))}};$.center=function(a){var a=$(a);var b=a.height();var c=a.width();a.css({width:c+'px',marginLeft:'-'+(c/2)+'px',marginTop:'-'+b/2+'px',height:'auto',top:!f?'50%':$(window).scrollTop()+($(window).height()/2)+"px",left:'50%'})};$.fn.jOverlay.options={method:'GET',data:'',url:'',color:'#000',opacity:'0.6',zIndex:9999,center:true,imgLoading:'',bgClickToClose:true,success:null,timeout:0};$.closeOverlay=function(){if(f){$("select").show()}$('#jOverlayContent .jOverlayChildren').hide().prependTo($('body'));$('#jOverlayLoading, #jOverlayContent, #jOverlay').remove()}})(jQuery);
\ No newline at end of file
+(function($){var g=$.browser.msie&&$.browser.version==6.0;var h=null;var i=null;$.fn.jOverlay=function(b){if($('#jOverlay').length){$.closeOverlay()}i=null;if(h!==null){clearTimeout(h)}var b=$.extend({},$.fn.jOverlay.options,b);function center(a){if(b.center){$.center(a)}}var c=this.is('*')?this:'#jOverlayContent';var d=g?'absolute':'fixed';var e=/([^\/\\]+)\.(png|gif|jpeg|jpg|bmp)$/i.test(b.url);var f=b.imgLoading?"<img id='jOverlayLoading' src='"+b.imgLoading+"' style='position:"+d+"; z-index:"+(b.zIndex+9)+";'/>":'';$('body').prepend(f+"<div id='jOverlay' />"+"<div id='jOverlayContent' style='position:"+d+"; z-index:"+(b.zIndex+5)+"; display:none;'/>");$('#jOverlayLoading').load(function(){center(this)});if(g){$('select').hide();$('#jOverlayContent select').show()}$('#jOverlay').css({backgroundColor:b.color,position:d,top:'0px',left:'0px',filter:'alpha(opacity='+(b.opacity*100)+')',opacity:b.opacity,zIndex:b.zIndex,width:!g?'100%':$(window).width()+'px',height:!g?'100%':$(document).height()+'px'}).show();if(this.is('*')){i=this.prev();$('#jOverlayContent').html(this.show().attr('display',b.autoHide?'none':this.css('display')));if(!e){center('#jOverlayContent');$('#jOverlayContent').show();if(!b.url&&$.isFunction(b.success)){b.success(this)}}}if(e){$('<img/>').load(function(){var a=$.resize(this.width,this.height);$(this).css({width:a.width,height:a.height});$(c).html(this);center('#jOverlayContent');$('#jOverlayLoading').fadeOut(500);$('#jOverlayContent').show();if($.isFunction(b.success)){b.success(this)}}).error(function(){alert('Image ('+b.url+') not found.');$.closeOverlay()}).attr({'src':b.url,'alt':b.url})}if(b.url&&!e){$.ajax({type:b.method,data:b.data,url:b.url,success:function(a){$('#jOverlayLoading').fadeOut(500);$(c).html(a).show();center('#jOverlayContent');if($.isFunction(b.success)){b.success(a)}},error:function(){alert('URL ('+b.url+') not found.');$.closeOverlay()}})}if(g){$(window).scroll(function(){center('#jOverlayContent')});$(window).resize(function(){$('#jOverlay').css({width:$(window).width()+'px',height:$(document).height()+'px'});center('#jOverlayContent')})}$(document).keydown(function(a){if(a.keyCode==27){$.closeOverlay()}});if(b.bgClickToClose){$('#jOverlay').click($.closeOverlay)}if(Number(b.timeout)>0){jOverlayTimer=setTimeout($.closeOverlay,Number(b.timeout))}$('#jOverlayContent').css(b.css||{})};$.resize=function(a,b){var x=$(window).width()-150;var y=$(window).height()-150;if(a>x){b=b*(x/a);a=x;if(b>y){a=a*(y/b);b=y}}else if(b>y){a=a*(y/b);b=y;if(a>x){b=b*(x/a);a=x}}return{width:a,height:b}};$.center=function(a){var a=$(a);var b=a.width();a.css({width:b+'px',marginLeft:'-'+(b/2)+'px',marginTop:'-'+a.height()/2+'px',height:'auto',top:!g?'50%':$(window).scrollTop()+($(window).height()/2)+'px',left:'50%'})};$.fn.jOverlay.options={method:'GET',data:'',url:'',color:'#000',opacity:'0.6',zIndex:9999,center:true,imgLoading:'',bgClickToClose:true,success:null,timeout:0,autoHide:true,css:{}};$.closeOverlay=function(){if(g){$("select").show()}if(i!==null){if(i!==null){var a=$('#jOverlayContent').children();i.after(a.css('display',a.attr('display')));a.removeAttr('display')}}$('#jOverlayLoading, #jOverlayContent, #jOverlay').remove()}})(jQuery);
index b54b492cce34ed52e6c7efed3dc29d027bd899ed..dda86294ed99abda715578176d12adf860c3a397 100644 (file)
@@ -11,7 +11,7 @@ $(document).ready(function() {
         C = $(S).val();
         switch (parseInt(S.id.slice(-1))) {
             case 1: default:
-                $('html, body').css({'background-color':C});
+                $('body').css({'background-color':C});
                 break;
             case 2:
                 $('#content, #site_nav_local_views .current a').css({'background-color':C});
@@ -89,11 +89,11 @@ $(document).ready(function() {
         $('body').css({'background-image':'none'});
     });
     $('#design_background-image_on').focus(function() {
-        var bis = $('#design_background-image_onoff img')[0].src;
-        $('body').css({'background-image':'url('+bis+')'});
+        $('body').css({'background-image':'url('+$('#design_background-image_onoff img')[0].src+')'});
+        $('body').css({'background-attachment': 'fixed'});
     });
 
     $('#design_background-image_repeat').click(function() {
         ($(this)[0].checked) ? $('body').css({'background-repeat':'repeat'}) : $('body').css({'background-repeat':'no-repeat'});
     });
-});
\ No newline at end of file
+});
index 17ae4c071926829234a28b741d91a21076e68784..9bb7c91288ff6577b445b1b9a7e1512e14ac7d58 100644 (file)
@@ -49,8 +49,9 @@ $(document).ready(function(){
                // run once in case there's something in there
                counter();
 
-               // set the focus
-               $("#notice_data-text").focus();
+        if($('body')[0].id != 'conversation') {
+            $("#notice_data-text").focus();
+        }
        }
 
        // XXX: refactor this code
@@ -217,10 +218,12 @@ $(document).ready(function(){
                                                             $('#'+li.id).css({display:'none'});
                                                             $('#'+li.id).fadeIn(2500);
                                                             NoticeReply();
+                                                            NoticeAttachments();
                                                          }
                                                                                                        }
                                                                                                        $("#notice_data-text").val("");
                                                                                                $("#notice_data-attach").val("");
+                                                    $('#notice_data-attach_selected').remove();
                                                     counter();
                                                                                                }
                                                                                                $("#form_notice").removeClass("processing");
@@ -232,6 +235,7 @@ $(document).ready(function(){
        $("#form_notice").each(addAjaxHidden);
     NoticeReply();
     NoticeAttachments();
+    NoticeDataAttach();
 });
 
 function NoticeReply() {
@@ -269,23 +273,25 @@ function NoticeAttachments() {
         color : '#000',
         opacity : '0.6',
         zIndex : 99,
-        center : true,
+        center : false,
         imgLoading : $('address .url')[0].href+'theme/base/images/illustrations/illu_progress_loading-01.gif',
         bgClickToClose : true,
         success : function() {
             $('#jOverlayContent').append('<button>&#215;</button>');
             $('#jOverlayContent button').click($.closeOverlay);
         },
-        timeout : 0
+        timeout : 0,
+        autoHide : true,
+        css : {'max-width':'502px', 'top':'22.5%', 'left':'32.5%'}
     };
 
-    $('a.attachment').click(function() {
-        $().jOverlay({url: $('address .url')[0].href+'/attachment/' + ($(this).attr('id').substring('attachment'.length + 1)) + '/ajax'});
+    $('#content .notice a.attachment').click(function() {
+        $().jOverlay({url: $('address .url')[0].href+'attachment/' + ($(this).attr('id').substring('attachment'.length + 1)) + '/ajax'});
         return false;
     });
-    
+
     var t;
-    $("body:not(#shownotice) a.thumbnail").hover(
+    $("body:not(#shownotice) #content .notice a.thumbnail").hover(
         function() {
             var anchor = $(this);
             $("a.thumbnail").children('img').hide();
@@ -293,7 +299,7 @@ function NoticeAttachments() {
 
             if (anchor.children('img').length == 0) {
                 t = setTimeout(function() {
-                    $.get($('address .url')[0].href+'/attachment/' + (anchor.attr('id').substring('attachment'.length + 1)) + '/thumbnail', null, function(data) {
+                    $.get($('address .url')[0].href+'attachment/' + (anchor.attr('id').substring('attachment'.length + 1)) + '/thumbnail', null, function(data) {
                         anchor.append(data);
                     });
                 }, 500);
@@ -309,3 +315,16 @@ function NoticeAttachments() {
         }
     );
 }
+
+function NoticeDataAttach() {
+    NDA = $('#notice_data-attach');
+    NDA.change(function() {
+        S = '<div id="notice_data-attach_selected" class="success"><code>'+$(this).val()+'</code> <button>&#215;</button></div>';
+        NDAS = $('#notice_data-attach_selected');
+        (NDAS.length > 0) ? NDAS.replaceWith(S) : $('#form_notice').append(S);
+        $('#notice_data-attach_selected button').click(function(){
+            $('#notice_data-attach_selected').remove();
+            NDA.val('');
+        });
+    });
+}
index 29f4eb3a66ff3eefbe989d9f07c51e815ac2487a..22d5b4cb5442e762b05d7a0d9a85c0944f4ad8e3 100644 (file)
@@ -40,7 +40,7 @@ class ShortUrlApi
     }
 
     private function is_long($url) {
-        return strlen($url) >= $this->long_limit;
+        return strlen($url) >= common_config('site', 'shorturllength');
     }
 
     protected function http_post($data) {
index 77a8ee391f3efc77416ceb2205abc9303ea39e57..928eb48c0b383d19adc3501bfb8f967f08d1e977 100644 (file)
@@ -383,15 +383,18 @@ class Action extends HTMLOutputter // lawsuit
     {
         $this->elementStart('address', array('id' => 'site_contact',
                                              'class' => 'vcard'));
-        $this->elementStart('a', array('class' => 'url home bookmark',
-                                       'href' => common_local_url('public')));
-        if (common_config('site', 'logo') || file_exists(theme_file('logo.png'))) {
-            $this->element('img', array('class' => 'logo photo',
-                                        'src' => (common_config('site', 'logo')) ? common_config('site', 'logo') : theme_path('logo.png'),
-                                        'alt' => common_config('site', 'name')));
+        if (Event::handle('StartAddressData', array($this))) {
+            $this->elementStart('a', array('class' => 'url home bookmark',
+                                           'href' => common_local_url('public')));
+            if (common_config('site', 'logo') || file_exists(theme_file('logo.png'))) {
+                $this->element('img', array('class' => 'logo photo',
+                                            'src' => (common_config('site', 'logo')) ? common_config('site', 'logo') : theme_path('logo.png'),
+                                            'alt' => common_config('site', 'name')));
+            }
+            $this->element('span', array('class' => 'fn org'), common_config('site', 'name'));
+            $this->elementEnd('a');
+            Event::handle('EndAddressData', array($this));
         }
-        $this->element('span', array('class' => 'fn org'), common_config('site', 'name'));
-        $this->elementEnd('a');
         $this->elementEnd('address');
     }
 
@@ -421,11 +424,13 @@ class Action extends HTMLOutputter // lawsuit
                     $this->menuItem(common_local_url('smssettings'),
                                     _('Connect'), _('Connect to SMS, Twitter'), false, 'nav_connect');
                 }
-                $this->menuItem(common_local_url('invite'),
-                                _('Invite'),
-                                sprintf(_('Invite friends and colleagues to join you on %s'),
-                                        common_config('site', 'name')),
-                                false, 'nav_invitecontact');
+                if (common_config('invite', 'enabled')) {
+                    $this->menuItem(common_local_url('invite'),
+                                    _('Invite'),
+                                    sprintf(_('Invite friends and colleagues to join you on %s'),
+                                            common_config('site', 'name')),
+                                    false, 'nav_invitecontact');
+                }
                 $this->menuItem(common_local_url('logout'),
                                 _('Logout'), _('Logout from the site'), false, 'nav_logout');
             }
@@ -968,12 +973,16 @@ class Action extends HTMLOutputter // lawsuit
         $action = $this->trimmed('action');
         $args   = $this->args;
         unset($args['action']);
+        if (common_config('site', 'fancy')) {
+            unset($args['p']);
+        }
         if (array_key_exists('submit', $args)) {
             unset($args['submit']);
         }
         foreach (array_keys($_COOKIE) as $cookie) {
             unset($args[$cookie]);
         }
+
         return common_local_url($action, $args);
     }
 
index a2446a886a73715193f9ea68f36fcc149733b312..f6a1b59d03d8521752aa58f9ade72c1ac82d428e 100644 (file)
@@ -82,7 +82,8 @@ class AttachmentList extends Widget
         $atts = new File;
         $att = $atts->getAttachments($this->notice->id);
         if (empty($att)) return 0;
-        $this->out->elementStart('dl', array('id' =>'attachments'));
+        $this->out->elementStart('dl', array('id' =>'attachments',
+                                             'class' => 'entry-content'));
         $this->out->element('dt', null, _('Attachments'));
         $this->out->elementStart('dd');
         $this->out->elementStart('ol', array('class' => 'attachments'));
@@ -249,10 +250,13 @@ class Attachment extends AttachmentListItem
         $this->out->elementStart('div', 'entry-title');
         $this->out->elementStart('a', $this->linkAttr());
         $this->out->element('span', null, $this->linkTitle());
-        $this->showRepresentation();
         $this->out->elementEnd('a');
         $this->out->elementEnd('div');
 
+        $this->out->elementStart('div', 'entry-content');
+        $this->showRepresentation();
+        $this->out->elementEnd('div');
+
         if (!empty($this->oembed->author_name) || !empty($this->oembed->provider)) {
             $this->out->elementStart('div', array('id' => 'oembed_info', 
                                                   'class' => 'entry-content'));
index a4da2515783aedbd26b5a496b7d4af9ca71d4666..14be747bc7a65db1ac31f44bbfc2c9899c71ad51 100644 (file)
@@ -67,14 +67,25 @@ function _sn_to_path($sn)
     return $p;
 }
 
-// try to figure out where we are
+// try to figure out where we are. $server and $path
+// can be set by including module, else we guess based
+// on HTTP info.
+
+if (isset($server)) {
+    $_server = $server;
+} else {
+    $_server = array_key_exists('SERVER_NAME', $_SERVER) ?
+      strtolower($_SERVER['SERVER_NAME']) :
+    null;
+}
 
-$_server = array_key_exists('SERVER_NAME', $_SERVER) ?
-  strtolower($_SERVER['SERVER_NAME']) :
-  null;
-$_path = array_key_exists('SCRIPT_NAME', $_SERVER) ?
-  _sn_to_path($_SERVER['SCRIPT_NAME']) :
-  null;
+if (isset($path)) {
+    $_path = $path;
+} else {
+    $_path = array_key_exists('SCRIPT_NAME', $_SERVER) ?
+      _sn_to_path($_SERVER['SCRIPT_NAME']) :
+    null;
+}
 
 // default configuration, overwritten in config.php
 
@@ -83,11 +94,10 @@ $config =
         array('name' => 'Just another Laconica microblog',
               'server' => $_server,
               'theme' => 'default',
-              'skin' => 'default',
               'design' =>
-              array('backgroundcolor' => '#F0F2F5',
+              array('backgroundcolor' => '#CEE1E9',
                     'contentcolor' => '#FFFFFF',
-                    'sidebarcolor' => '#CEE1E9',
+                    'sidebarcolor' => '#C8D1D5',
                     'textcolor' => '#000000',
                     'linkcolor' => '#002E6E',
                     'backgroundimage' => null,
@@ -110,12 +120,20 @@ $config =
               'private' => false,
               'ssl' => 'never',
               'sslserver' => null,
+              'shorturllength' => 30,
               'dupelimit' => 60), # default for same person saying the same thing
         'syslog' =>
         array('appname' => 'laconica', # for syslog
-              'priority' => 'debug'), # XXX: currently ignored
+              'priority' => 'debug', # XXX: currently ignored
+              'facility' => LOG_USER),
         'queue' =>
-        array('enabled' => false),
+        array('enabled' => false,
+              'subsystem' => 'db', # default to database, or 'stomp'
+              'stomp_server' => null,
+              'queue_basename' => 'laconica',
+              'stomp_username' => null,
+              'stomp_password' => null,
+              ),
         'license' =>
         array('url' => 'http://creativecommons.org/licenses/by/3.0/',
               'title' => 'Creative Commons Attribution 3.0',
@@ -141,7 +159,9 @@ $config =
               'blacklist' => array(),
               'autosource' => array()),
         'theme' =>
-        array('server' => null),
+        array('server' => null,
+              'dir' => null,
+              'path'=> null),
         'throttle' =>
         array('enabled' => false, // whether to throttle edits; false by default
               'count' => 20, // number of allowed messages in timespan
@@ -157,6 +177,8 @@ $config =
               'host' => null, # only set if != server
               'debug' => false, # print extra debug info
               'public' => array()), # JIDs of users who want to receive the public stream
+        'invite' =>
+        array('enabled' => true),
         'sphinx' =>
         array('enabled' => false,
               'server' => 'localhost',
@@ -191,45 +213,54 @@ $config =
               'frequency' => 10000,
               'reporturl' => 'http://laconi.ca/stats/report'),
         'attachments' =>
-        array('supported' => array('image/png',
-            'image/jpeg',
-            'image/gif',
-            'image/svg+xml',
-            'audio/mpeg',
-            'audio/x-speex',
-            'application/ogg',
-            'application/pdf',
-            'application/vnd.oasis.opendocument.text',
-            'application/vnd.oasis.opendocument.text-template',
-            'application/vnd.oasis.opendocument.graphics',
-            'application/vnd.oasis.opendocument.graphics-template',
-            'application/vnd.oasis.opendocument.presentation',
-            'application/vnd.oasis.opendocument.presentation-template',
-            'application/vnd.oasis.opendocument.spreadsheet',
-            'application/vnd.oasis.opendocument.spreadsheet-template',
-            'application/vnd.oasis.opendocument.chart',
-            'application/vnd.oasis.opendocument.chart-template',
-            'application/vnd.oasis.opendocument.image',
-            'application/vnd.oasis.opendocument.image-template',
-            'application/vnd.oasis.opendocument.formula',
-            'application/vnd.oasis.opendocument.formula-template',
-            'application/vnd.oasis.opendocument.text-master',
-            'application/vnd.oasis.opendocument.text-web',
-            'application/x-zip',
-            'application/zip',
-            'text/plain',
-            'video/mpeg',
-            'video/mp4',
-            'video/quicktime',
-            'video/mpeg'),
+        array('server' => null,
+              'dir' => INSTALLDIR . '/file/',
+              'path' => $_path . '/file/',
+              'supported' => array('image/png',
+                                   'image/jpeg',
+                                   'image/gif',
+                                   'image/svg+xml',
+                                   'audio/mpeg',
+                                   'audio/x-speex',
+                                   'application/ogg',
+                                   'application/pdf',
+                                   'application/vnd.oasis.opendocument.text',
+                                   'application/vnd.oasis.opendocument.text-template',
+                                   'application/vnd.oasis.opendocument.graphics',
+                                   'application/vnd.oasis.opendocument.graphics-template',
+                                   'application/vnd.oasis.opendocument.presentation',
+                                   'application/vnd.oasis.opendocument.presentation-template',
+                                   'application/vnd.oasis.opendocument.spreadsheet',
+                                   'application/vnd.oasis.opendocument.spreadsheet-template',
+                                   'application/vnd.oasis.opendocument.chart',
+                                   'application/vnd.oasis.opendocument.chart-template',
+                                   'application/vnd.oasis.opendocument.image',
+                                   'application/vnd.oasis.opendocument.image-template',
+                                   'application/vnd.oasis.opendocument.formula',
+                                   'application/vnd.oasis.opendocument.formula-template',
+                                   'application/vnd.oasis.opendocument.text-master',
+                                   'application/vnd.oasis.opendocument.text-web',
+                                   'application/x-zip',
+                                   'application/zip',
+                                   'text/plain',
+                                   'video/mpeg',
+                                   'video/mp4',
+                                   'video/quicktime',
+                                   'video/mpeg'),
         'file_quota' => 5000000,
         'user_quota' => 50000000,
         'monthly_quota' => 15000000,
         'uploads' => true,
+        'filecommand' => '/usr/bin/file',
         ),
         'group' =>
         array('maxaliases' => 3),
-        'oohembed' => array('endpoint' => 'http://oohembed.com/oohembed/')
+        'oohembed' => array('endpoint' => 'http://oohembed.com/oohembed/'),
+        'search' =>
+        array('type' => 'fulltext'),
+        'sessions' =>
+        array('handle' => false, // whether to handle sessions ourselves
+              'debug' => false), // debugging output for sessions
         );
 
 $config['db'] = &PEAR::getStaticProperty('DB_DataObject','options');
@@ -255,14 +286,18 @@ if (function_exists('date_default_timezone_set')) {
 // server-wide, then vhost-wide, then for a path,
 // finally for a dir (usually only need one of the last two).
 
-$_config_files = array('/etc/laconica/laconica.php',
-                  '/etc/laconica/'.$_server.'.php');
+if (isset($conffile)) {
+    $_config_files = array($conffile);
+} else {
+    $_config_files = array('/etc/laconica/laconica.php',
+                           '/etc/laconica/'.$_server.'.php');
 
-if (strlen($_path) > 0) {
-    $_config_files[] = '/etc/laconica/'.$_server.'_'.$_path.'.php';
-}
+    if (strlen($_path) > 0) {
+        $_config_files[] = '/etc/laconica/'.$_server.'_'.$_path.'.php';
+    }
 
-$_config_files[] = INSTALLDIR.'/config.php';
+    $_config_files[] = INSTALLDIR.'/config.php';
+}
 
 $_have_a_config = false;
 
index 7c2520cf67cbf292334a821dd952be1caa99da15..4c7e15a8b7a4dd6c8f17d418847944508c0b4046 100644 (file)
@@ -53,14 +53,19 @@ class CurrentUserDesignAction extends Action
       *
       * @return nothing
       */
+
      function showStylesheets()
      {
          parent::showStylesheets();
 
-         $design = $this->getDesign();
+         $user = common_current_user();
+
+         if (empty($user) || $user->viewdesigns) {
+             $design = $this->getDesign();
 
-         if (!empty($design)) {
-             $design->showCSS($this);
+             if (!empty($design)) {
+                 $design->showCSS($this);
+             }
          }
      }
 
@@ -84,5 +89,4 @@ class CurrentUserDesignAction extends Action
         return $cur->getDesign();
     }
 
-
 }
index a0df00bdcc852cc434f025ed6f062aa4496a653d..9d89c63e781eb2e25353c5501850510cb87cc612 100644 (file)
@@ -23,6 +23,13 @@ if (!defined('LACONICA')) {
 
 class Daemon
 {
+    var $daemonize = true;
+
+    function __construct($daemonize = true)
+    {
+        $this->daemonize = $daemonize;
+    }
+
     function name()
     {
         return null;
@@ -129,12 +136,16 @@ class Daemon
             common_log(LOG_INFO, $this->name() . ' already running. Exiting.');
             exit(0);
         }
-        if ($this->background()) {
-            $this->writePidFile();
-            $this->changeUser();
-            $this->run();
-            $this->clearPidFile();
+
+        if ($this->daemonize) {
+            common_log(LOG_INFO, 'Backgrounding daemon "'.$this->name().'"');
+            $this->background();
         }
+
+        $this->writePidFile();
+        $this->changeUser();
+        $this->run();
+        $this->clearPidFile();
     }
 
     function run()
index 6aa6bb2f193cef4fe345e363f0b94e2e790ea24b..fbffdb208f1e73a35e03497768f61e8f304beadc 100644 (file)
@@ -35,6 +35,20 @@ if (!defined('LACONICA')) {
 require_once INSTALLDIR . '/lib/accountsettingsaction.php';
 require_once INSTALLDIR . '/lib/webcolor.php';
 
+/**
+ * Base class for setting a user or group design
+ *
+ * Shows the design setting form and also handles some things like saving
+ * background images, and fetching a default design
+ *
+ * @category Settings
+ * @package  Laconica
+ * @author   Zach Copley <zach@controlyourself.ca>
+ * @author   Sarven Capadisli <csarven@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 DesignSettingsAction extends AccountSettingsAction
 {
 
@@ -63,6 +77,14 @@ class DesignSettingsAction extends AccountSettingsAction
         'with a background image and a colour palette of your choice.');
     }
 
+    /**
+     * Shows the design settings form
+     *
+     * @param Design $design a working design to show
+     *
+     * @return nothing
+     */
+
     function showDesignForm($design)
     {
 
@@ -94,7 +116,8 @@ class DesignSettingsAction extends AccountSettingsAction
 
         if (!empty($design->backgroundimage)) {
 
-            $this->elementStart('li', array('id' => 'design_background-image_onoff'));
+            $this->elementStart('li', array('id' =>
+                'design_background-image_onoff'));
 
             $this->element('img', array('src' =>
                 Design::url($design->backgroundimage)));
@@ -132,13 +155,13 @@ class DesignSettingsAction extends AccountSettingsAction
                                           _('Off'));
             $this->element('p', 'form_guide', _('Turn background image on or off.'));
             $this->elementEnd('li');
-        }
 
-        $this->elementStart('li');
-        $this->checkbox('design_background-image_repeat',
-                        _('Tile background image'),
-                        ($design->disposition & BACKGROUND_TILE) ? true : false );
-        $this->elementEnd('li');
+            $this->elementStart('li');
+            $this->checkbox('design_background-image_repeat',
+                            _('Tile background image'),
+                            ($design->disposition & BACKGROUND_TILE) ? true : false);
+            $this->elementEnd('li');
+        }
 
         $this->elementEnd('ul');
         $this->elementEnd('fieldset');
@@ -212,18 +235,19 @@ class DesignSettingsAction extends AccountSettingsAction
                                          'maxlength' => '7',
                                          'size' => '7',
                                          'value' => '#' . $lcolor->hexValue()));
+            $this->elementEnd('li');
 
-           $this->elementEnd('li');
+        } catch (WebColorException $e) {
+            common_log(LOG_ERR, 'Bad color values in design ID: ' .$design->id);
+        }
 
-       } catch (WebColorException $e) {
-           common_log(LOG_ERR, 'Bad color values in design ID: ' .
-               $design->id);
-       }
+        $this->elementEnd('ul');
+        $this->elementEnd('fieldset');
 
-       $this->elementEnd('ul');
-       $this->elementEnd('fieldset');
+        $this->submit('defaults', _('Use defaults'), 'submit form_action-default',
+            'defaults', _('Restore default designs'));
 
-       $this->element('input', array('id' => 'settings_design_reset',
+        $this->element('input', array('id' => 'settings_design_reset',
                                      'type' => 'reset',
                                      'value' => 'Reset',
                                      'class' => 'submit form_action-primary',
@@ -271,8 +295,8 @@ class DesignSettingsAction extends AccountSettingsAction
 
         if ($this->arg('save')) {
             $this->saveDesign();
-        } else if ($this->arg('reset')) {
-            $this->resetDesign();
+        } else if ($this->arg('defaults')) {
+            $this->restoreDefaults();
         } else {
             $this->showForm(_('Unexpected form submission.'));
         }
@@ -316,7 +340,7 @@ class DesignSettingsAction extends AccountSettingsAction
     }
 
     /**
-     * Get a default user design
+     * Get a default design
      *
      * @return Design design
      */
@@ -358,7 +382,16 @@ class DesignSettingsAction extends AccountSettingsAction
         return $design;
     }
 
-    function saveBackgroundImage($design) {
+    /**
+     * Save the background image, if any, and set its disposition
+     *
+     * @param Design $design a working design to attach the img to
+     *
+     * @return nothing
+     */
+
+    function saveBackgroundImage($design)
+    {
 
         // Now that we have a Design ID we can add a file to the design.
         // XXX: This is an additional DB hit, but figured having the image
@@ -371,12 +404,12 @@ class DesignSettingsAction extends AccountSettingsAction
             $filepath = null;
 
             try {
-                    $imagefile =
-                        ImageFile::fromUpload('design_background-image_file');
-                } catch (Exception $e) {
-                    $this->showForm($e->getMessage());
-                    return;
-                }
+                $imagefile =
+                    ImageFile::fromUpload('design_background-image_file');
+            } catch (Exception $e) {
+                $this->showForm($e->getMessage());
+                return;
+            }
 
             $filename = Design::filename($design->id,
                 image_type_to_extension($imagefile->type),
@@ -386,9 +419,20 @@ class DesignSettingsAction extends AccountSettingsAction
 
             move_uploaded_file($imagefile->filepath, $filepath);
 
+            // delete any old backround img laying around
+
+            if (isset($design->backgroundimage)) {
+                @unlink(Design::path($design->backgroundimage));
+            }
+
             $original = clone($design);
+
             $design->backgroundimage = $filename;
+
+            // default to on, no tile
+
             $design->setDisposition(true, false, false);
+
             $result = $design->update($original);
 
             if ($result === false) {
@@ -399,4 +443,35 @@ class DesignSettingsAction extends AccountSettingsAction
         }
     }
 
+    /**
+     * Restore the user or group design to system defaults
+     *
+     * @return nothing
+     */
+
+    function restoreDefaults()
+    {
+        $design   = $this->getWorkingDesign();
+        $default  = $this->defaultDesign();
+        $original = clone($design);
+
+        $design->backgroundcolor = $default->backgroundcolor;
+        $design->contentcolor    = $default->contentcolor;
+        $design->sidebarcolor    = $default->sidebarcolor;
+        $design->textcolor       = $default->textcolor;
+        $design->linkcolor       = $default->linkcolor;
+
+        $design->setDisposition(false, true, false);
+
+        $result = $design->update($original);
+
+        if ($result === false) {
+            common_log_db_error($design, 'UPDATE', __FILE__);
+            $this->showForm(_('Couldn\'t update your design.'));
+            return;
+        }
+
+        $this->showForm(_('Design defaults restored.'), true);
+    }
+
 }
index a445750f7e00eee711f054a200c7ebc0dbd0f75e..1ae90d53bdeb534afd8a65a1f5f51c5b90a2b480 100644 (file)
@@ -213,12 +213,14 @@ class FacebookAction extends Action
             array('href' => 'index.php', 'title' => _('Home')), _('Home'));
         $this->elementEnd('li');
 
-        $this->elementStart('li',
-            array('class' =>
-                ($this->action == 'facebookinvite') ? 'current' : 'facebook_invite'));
-        $this->element('a',
-            array('href' => 'invite.php', 'title' => _('Invite')), _('Invite'));
-        $this->elementEnd('li');
+        if (common_config('invite', 'enabled')) {
+            $this->elementStart('li',
+                array('class' =>
+                    ($this->action == 'facebookinvite') ? 'current' : 'facebook_invite'));
+            $this->element('a',
+                array('href' => 'invite.php', 'title' => _('Invite')), _('Invite'));
+            $this->elementEnd('li');
+        }
 
         $this->elementStart('li',
             array('class' =>
index bc95921f1b156b1751b55fb060b7ea5930ea233e..58777c283a06859a5b635baf08b397ac8b713a5a 100644 (file)
@@ -34,7 +34,7 @@ if (!defined('LACONICA')) {
 /**
  * Base class for actions that use a group's design
  *
- * Pages related to groups can be themed with a design. 
+ * Pages related to groups can be themed with a design.
  * This superclass returns that design.
  *
  * @category Action
@@ -48,7 +48,7 @@ class GroupDesignAction extends Action {
 
     /** The group in question */
     var $group = null;
-    
+
     /**
       * Show the groups's design stylesheet
       *
@@ -58,10 +58,14 @@ class GroupDesignAction extends Action {
      {
          parent::showStylesheets();
 
-         $design = $this->getDesign();
+         $user = common_current_user();
+
+         if (empty($user) || $user->viewdesigns) {
+             $design = $this->getDesign();
 
-         if (!empty($design)) {
-             $design->showCSS($this);
+             if (!empty($design)) {
+                 $design->showCSS($this);
+             }
          }
      }
 
@@ -76,12 +80,10 @@ class GroupDesignAction extends Action {
 
     function getDesign()
     {
-
         if (empty($this->group)) {
             return null;
         }
 
         return $this->group->getDesign();
     }
-
 }
index 7e8d6eea3ad93cb9d8f567e4dde8abfabe8f52b3..fbb39129bc3bc4f7b05f552b318bf0e94029b8c8 100644 (file)
@@ -130,30 +130,46 @@ class GroupEditForm extends Form
 
     function formData()
     {
+        if ($this->group) {
+            $id = $this->group->id;
+            $nickname = $this->group->nickname;
+            $fullname = $this->group->fullname;
+            $homepage = $this->group->homepage;
+            $description = $this->group->description;
+            $location = $this->group->location;
+        } else {
+            $id = '';
+            $nickname = '';
+            $fullname = '';
+            $homepage = '';
+            $description = '';
+            $location = '';
+        }
+
         $this->out->elementStart('ul', 'form_data');
         $this->out->elementStart('li');
-        $this->out->hidden('groupid', $this->group->id);
+        $this->out->hidden('groupid', $id);
         $this->out->input('nickname', _('Nickname'),
-                     ($this->out->arg('nickname')) ? $this->out->arg('nickname') : $this->group->nickname,
+                     ($this->out->arg('nickname')) ? $this->out->arg('nickname') : $nickname,
                      _('1-64 lowercase letters or numbers, no punctuation or spaces'));
         $this->out->elementEnd('li');
         $this->out->elementStart('li');
         $this->out->input('fullname', _('Full name'),
-                     ($this->out->arg('fullname')) ? $this->out->arg('fullname') : $this->group->fullname);
+                     ($this->out->arg('fullname')) ? $this->out->arg('fullname') : $fullname);
         $this->out->elementEnd('li');
         $this->out->elementStart('li');
         $this->out->input('homepage', _('Homepage'),
-                     ($this->out->arg('homepage')) ? $this->out->arg('homepage') : $this->group->homepage,
+                     ($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage,
                      _('URL of the homepage or blog of the group or topic'));
         $this->out->elementEnd('li');
         $this->out->elementStart('li');
         $this->out->textarea('description', _('Description'),
-                        ($this->out->arg('description')) ? $this->out->arg('description') : $this->group->description,
+                        ($this->out->arg('description')) ? $this->out->arg('description') : $description,
                         _('Describe the group or topic in 140 chars'));
         $this->out->elementEnd('li');
         $this->out->elementStart('li');
         $this->out->input('location', _('Location'),
-                     ($this->out->arg('location')) ? $this->out->arg('location') : $this->group->location,
+                     ($this->out->arg('location')) ? $this->out->arg('location') : $location,
                      _('Location for the group, if any, like "City, State (or Region), Country"'));
         $this->out->elementEnd('li');
         if (common_config('group', 'maxaliases') > 0) {
index 0c93b257ed394d5433e6a2c8f147e18aad480587..52e4c4b2272d66e8a2b9e0de1612e0a73e7d308e 100644 (file)
@@ -72,7 +72,8 @@ class ImageFile
             break;
          case UPLOAD_ERR_INI_SIZE:
          case UPLOAD_ERR_FORM_SIZE:
-            throw new Exception(sprintf(_('That file is too big. The maximum file size is %d.'), $this->maxFileSize()));
+            throw new Exception(sprintf(_('That file is too big. The maximum file size is %d.'),
+                ImageFile::maxFileSize()));
             return;
          case UPLOAD_ERR_PARTIAL:
             @unlink($_FILES[$param]['tmp_name']);
index f786c20a806ed53c3c55cff6eb9c0cbe0367dad5..7beea9328d27649d507b4e8a753324ae836648d5 100644 (file)
@@ -89,6 +89,7 @@ class JSONSearchResultsList
     function show()
     {
         $cnt = 0;
+        $this->max_id = 0;
 
         $time_start = microtime(true);
 
index cd6498d30b5d108677080a4b1494efa7e54d9a90..3ea3dd2aa0a5237412a980fb1266fce75d10e9e9 100644 (file)
@@ -108,7 +108,7 @@ function get_all_languages() {
                'el'      => array('q' => 0.1, 'lang' => 'el',    'name' => 'Greek', 'direction' => 'ltr'),
                'en-us'   => array('q' => 1, 'lang' => 'en_US', 'name' => 'English (US)', 'direction' => 'ltr'),
                'en-gb'   => array('q' => 1, 'lang' => 'en_GB', 'name' => 'English (British)', 'direction' => 'ltr'),
-               'en'      => array('q' => 1, 'lang' => 'en',    'name' => 'English', 'direction' => 'ltr'),
+               'en'      => array('q' => 1, 'lang' => 'en_US',    'name' => 'English (US)', 'direction' => 'ltr'),
                'es'      => array('q' => 1, 'lang' => 'es',    'name' => 'Spanish', 'direction' => 'ltr'),
                'fi'      => array('q' => 1, 'lang' => 'fi', 'name' => 'Finnish', 'direction' => 'ltr'),
                'fr-fr'   => array('q' => 1, 'lang' => 'fr_FR', 'name' => 'French', 'direction' => 'ltr'),
index ad792441a3794765df430698c0721a65b0737e37..44726a17b7b7a3da4116724bc0ed1d4c69b8d35b 100644 (file)
@@ -179,7 +179,6 @@ class NoticeListItem extends Widget
     {
         $this->showStart();
         $this->showNotice();
-        $this->showNoticeAttachments();
         $this->showNoticeInfo();
         $this->showNoticeOptions();
         $this->showEnd();
@@ -193,18 +192,6 @@ class NoticeListItem extends Widget
         $this->out->elementEnd('div');
     }
 
-    function showNoticeAttachments() {
-        if ($this->isUsedInList()) {
-            return;
-        }
-        $al = new AttachmentList($this->notice, $this->out);
-        $al->show();
-    }
-
-    function isUsedInList() {
-        return 'shownotice' !== $this->out->args['action'];
-    }
-
     function showNoticeInfo()
     {
         $this->out->elementStart('div', 'entry-content');
@@ -349,10 +336,6 @@ class NoticeListItem extends Widget
             // versions (>> 0.4.x)
             $this->out->raw(common_render_content($this->notice->content, $this->notice));
         }
-        $uploaded = $this->notice->getUploadedAttachment();
-        if ($uploaded) {
-            $this->out->element('a', array('href' => $uploaded[0], 'class' => 'attachment', 'id' => 'attachment-' . $uploaded[1]), $uploaded[0]);
-        }
         $this->out->elementEnd('p');
     }
 
@@ -449,7 +432,7 @@ class NoticeListItem extends Widget
             $this->out->elementStart('dl', 'response');
             $this->out->element('dt', null, _('To'));
             $this->out->elementStart('dd');
-            $this->out->element('a', array('href' => $convurl),
+            $this->out->element('a', array('href' => $convurl.'#notice-'.$this->notice->id),
                                 _('in context'));
             $this->out->elementEnd('dd');
             $this->out->elementEnd('dl');
index 424474f4298d75b69a2cfc5c3dd487641331778f..785b8a93d3aad2a0a3962e324516d9d7ef726d1e 100644 (file)
@@ -61,11 +61,15 @@ class OwnerDesignAction extends Action {
      {
          parent::showStylesheets();
 
-         $design = $this->getDesign();
+         $user = common_current_user();
 
-         if (!empty($design)) {
-             $design->showCSS($this);
-        }
+         if (empty($user) || $user->viewdesigns) {
+             $design = $this->getDesign();
+
+             if (!empty($design)) {
+                 $design->showCSS($this);
+             }
+         }
      }
 
     /**
diff --git a/lib/peoplesearchresults.php b/lib/peoplesearchresults.php
deleted file mode 100644 (file)
index 9f6696b..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-/**
- * People search results class
- *
- * PHP version 5
- *
- * @category Widget
- * @package  Laconica
- * @author   Evan Prodromou <evan@controlyourself.ca>
- * @author   Robin Millette <millette@controlyourself.ca>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://laconi.ca/
- *
- * 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/>.
- */
-
-if (!defined('LACONICA')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/lib/profilelist.php';
-
-/**
- * People search results class
- *
- * Derivative of ProfileList with specialization for highlighting search terms.
- *
- * @category Widget
- * @package  Laconica
- * @author   Evan Prodromou <evan@controlyourself.ca>
- * @author   Robin Millette <millette@controlyourself.ca>
- * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link     http://laconi.ca/
- *
- * @see PeoplesearchAction
- */
-
-class PeopleSearchResults extends ProfileList
-{
-    var $terms = null;
-    var $pattern = null;
-
-    function __construct($profile, $terms, $action)
-    {
-        parent::__construct($profile, $action);
-
-        $this->terms = array_map('preg_quote',
-                                 array_map('htmlspecialchars', $terms));
-
-        $this->pattern = '/('.implode('|',$terms).')/i';
-    }
-
-    function newProfileItem($profile)
-    {
-        return new PeopleSearchResultItem($profile, $this->action);
-    }
-}
-
-class PeopleSearchResultItem extends ProfileListItem
-{
-    function highlight($text)
-    {
-        return preg_replace($this->pattern, '<strong>\\1</strong>', htmlspecialchars($text));
-    }
-}
-
index 2519922b2b28e8292e9a4a51e270b8e6af3e8416..eeb5dbe48d80db5510b98861626f62ad6badd343 100644 (file)
@@ -108,7 +108,9 @@ class ProfileAction extends OwnerDesignAction
 
         $this->element('h2', null, _('Subscriptions'));
 
-        if ($profile) {
+        $cnt = 0;
+
+        if (!empty($profile)) {
             $pml = new ProfileMiniList($profile, $this);
             $cnt = $pml->show();
             if ($cnt == 0) {
@@ -137,7 +139,9 @@ class ProfileAction extends OwnerDesignAction
 
         $this->element('h2', null, _('Subscribers'));
 
-        if ($profile) {
+        $cnt = 0;
+
+        if (!empty($profile)) {
             $pml = new ProfileMiniList($profile, $this);
             $cnt = $pml->show();
             if ($cnt == 0) {
index a604230f85dd5c0554823cf0747550082dbd1610..774538a4b611de3a892c77c313b14ba2bb4a3088 100644 (file)
@@ -248,8 +248,13 @@ class ProfileListItem extends Widget
                 $usf = new UnsubscribeForm($this->out, $this->profile);
                 $usf->show();
             } else {
-                $sf = new SubscribeForm($this->out, $this->profile);
-                $sf->show();
+                // Is it a local user? can't remote sub from a list
+                // XXX: make that possible!
+                $other = User::staticGet('id', $this->profile->id);
+                if (!empty($other)) {
+                    $sf = new SubscribeForm($this->out, $this->profile);
+                    $sf->show();
+                }
             }
             $this->out->elementEnd('li');
         }
index 09bef6f7c6158a054c806b210add89c8b3b2daea..357b4a2db4930c8979e3ffc07fba124fe28ee03d 100644 (file)
@@ -47,6 +47,7 @@ define('PROFILES_PER_MINILIST', 27);
 
 class ProfileMiniList extends ProfileList
 {
+
     function startList()
     {
         $this->out->elementStart('ul', 'entities users xoxo');
@@ -56,6 +57,23 @@ class ProfileMiniList extends ProfileList
     {
         return new ProfileMiniListItem($profile, $this->action);
     }
+
+    function showProfiles()
+    {
+        $cnt = 0;
+
+        while ($this->profile->fetch()) {
+            $cnt++;
+            if ($cnt > PROFILES_PER_MINILIST) {
+                break;
+            }
+            $pli = $this->newListItem($this->profile);
+            $pli->show();
+        }
+
+        return $cnt;
+    }
+
 }
 
 class ProfileMiniListItem extends ProfileListItem
index d5e0150d9c9997ba334b8df6c00a15d141ea8daa..c1c4f3309a24011b60e72de070a5fcef0267ad56 100644 (file)
@@ -27,11 +27,12 @@ require_once(INSTALLDIR.'/classes/Notice.php');
 
 class QueueHandler extends Daemon
 {
-
     var $_id = 'generic';
 
-    function QueueHandler($id=null)
+    function __construct($id=null, $daemonize=true)
     {
+        parent::__construct($daemonize);
+
         if ($id) {
             $this->set_id($id);
         }
@@ -112,12 +113,21 @@ class QueueHandler extends Daemon
     }
 
     function stomp_dispatch() {
-        require("Stomp.php");
-        $con = new Stomp(common_config('queue','stomp_server'));
-        if (!$con->connect()) {
+
+        // use an external message queue system via STOMP
+        require_once("Stomp.php");
+
+        $server = common_config('queue','stomp_server');
+        $username = common_config('queue', 'stomp_username');
+        $password = common_config('queue', 'stomp_password');
+
+        $con = new Stomp($server);
+
+        if (!$con->connect($username, $password)) {
             $this->log(LOG_ERR, 'Failed to connect to queue server');
             return false;
         }
+
         $queue_basename = common_config('queue','queue_basename');
         // subscribe to the relevant queue (format: basename-transport)
         $con->subscribe('/queue/'.$queue_basename.'-'.$this->transport());
index 1f39c60dc50207449c6ddba1a196560d0a1a390c..75e72f932295d047bfd0875aab0a91bd5480855a 100644 (file)
@@ -152,6 +152,10 @@ class Router
         $m->connect('search/notice/rss?q=:q', array('action' => 'noticesearchrss'),
                     array('q' => '.+'));
 
+        $m->connect('attachment/:attachment',
+                    array('action' => 'attachment'),
+                    array('attachment' => '[0-9]+'));
+
         $m->connect('attachment/:attachment/ajax',
                     array('action' => 'attachment_ajax'),
                     array('attachment' => '[0-9]+'));
@@ -257,7 +261,7 @@ class Router
         $m->connect('api/statuses/:method',
                     array('action' => 'api',
                           'apiaction' => 'statuses'),
-                    array('method' => '(public_timeline|friends_timeline|user_timeline|update|replies|mentions|friends|followers|featured)(\.(atom|rss|xml|json))?'));
+                    array('method' => '(public_timeline|friends_timeline|user_timeline|update|replies|mentions|show|friends|followers|featured)(\.(atom|rss|xml|json))?'));
 
         $m->connect('api/statuses/:method/:argument',
                     array('action' => 'api',
@@ -313,7 +317,7 @@ class Router
         $m->connect('api/friendships/:method',
                     array('action' => 'api',
                           'apiaction' => 'friendships'),
-                    array('method' => 'exists(\.(xml|json))'));
+                    array('method' => '(show|exists)(\.(xml|json))'));
 
         // Social graph
 
index 6f6c9a8cb08e76461b1438556de367850c39e20d..0c8188e88021a8aca153d0eefb09ecf2b5805861 100644 (file)
@@ -214,7 +214,7 @@ class Rss10Action extends Action
         $this->element('cc:licence', array('rdf:resource' => common_config('license', 'url')));
         if ($notice->reply_to) {
             $replyurl = common_local_url('shownotice', array('notice' => $notice->reply_to));
-            $this->element('sioc:reply_to', array('rdf:resource' => $replyurl));
+            $this->element('sioc:reply_of', array('rdf:resource' => $replyurl));
         }
         $this->elementEnd('item');
         $this->creators[$creator_uri] = $profile;
index c98e8ed875b0017335a5ca07c4f0055133fbf334..772f41883b9dde17f5b5e59f39c276628fe189ad 100644 (file)
@@ -118,12 +118,20 @@ class MySQLSearch extends SearchEngine
             }
             return true;
         } else if ('identica_notices' === $this->table) {
-             $this->target->whereAdd('MATCH(content) ' .
-                                     'AGAINST (\''.addslashes($q).'\' IN BOOLEAN MODE)');
+
+            // Don't show imported notices
+            $this->target->whereAdd('notice.is_local != ' . NOTICE_GATEWAY);
+
             if (strtolower($q) != $q) {
+                $this->target->whereAdd("( MATCH(content) AGAINST ('" . addslashes($q) .
+                    "' IN BOOLEAN MODE)) OR ( MATCH(content) " .
+                    "AGAINST ('"  . addslashes(strtolower($q)) .
+                    "' IN BOOLEAN MODE))");
+            } else {
                 $this->target->whereAdd('MATCH(content) ' .
-                                        'AGAINST (\''.addslashes(strtolower($q)).'\' IN BOOLEAN MODE)', 'OR');
+                                         'AGAINST (\''.addslashes($q).'\' IN BOOLEAN MODE)');
             }
+
             return true;
         } else {
             throw new ServerException('Unknown table: ' . $this->table);
@@ -131,6 +139,28 @@ class MySQLSearch extends SearchEngine
     }
 }
 
+class MySQLLikeSearch extends SearchEngine
+{
+    function query($q)
+    {
+        if ('identica_people' === $this->table) {
+            $qry = sprintf('(nickname LIKE "%%%1$s%%" OR '.
+                           ' fullname LIKE "%%%1$s%%" OR '.
+                           ' location LIKE "%%%1$s%%" OR '.
+                           ' bio      LIKE "%%%1$s%%" OR '.
+                           ' homepage LIKE "%%%1$s%%")', addslashes($q));
+        } else if ('identica_notices' === $this->table) {
+            $qry = sprintf('content LIKE "%%%1$s%%"', addslashes($q));
+        } else {
+            throw new ServerException('Unknown table: ' . $this->table);
+        }
+
+        $this->target->whereAdd($qry);
+
+        return true;
+    }
+}
+
 class PGSearch extends SearchEngine
 {
     function query($q)
@@ -138,6 +168,9 @@ class PGSearch extends SearchEngine
         if ('identica_people' === $this->table) {
             return $this->target->whereAdd('textsearch @@ plainto_tsquery(\''.addslashes($q).'\')');
         } else if ('identica_notices' === $this->table) {
+
+            // XXX: We need to filter out gateway notices (notice.is_local = -2) --Zach
+
             return $this->target->whereAdd('to_tsvector(\'english\', content) @@ plainto_tsquery(\''.addslashes($q).'\')');
         } else {
             throw new ServerException('Unknown table: ' . $this->table);
index bb2d50572b1131512a96ced888b7d82634b9fb34..34fe9373f43832cc793a59484a62f7f007b05620 100644 (file)
@@ -148,10 +148,10 @@ You can also try your search on other engines:
 * [Tweet scan](http://www.tweetscan.com/indexi.php?s=%s)
 * [Google](http://www.google.com/search?q=site%%3A%%%%site.server%%%%+%s)
 * [Yahoo](http://search.yahoo.com/search?p=site%%3A%%%%site.server%%%%+%s)
-
+* [Collecta](http://collecta.com/#q=%s)
 
 E_O_T
-), $qe, $qe, $qe, $qe);
+), $qe, $qe, $qe, $qe, $qe);
         $this->elementStart('dl', array('id' => 'help_search', 'class' => 'help'));
         $this->element('dt', null, _('Search help'));
         $this->elementStart('dd', 'instructions');
index 4a9b36ae8fa1a198c7d2cb7d2be8f8732c4b62cb..52099192324ada24ea0f5c0c10f7ba199612dd37 100644 (file)
@@ -100,7 +100,7 @@ class SubGroupNav extends Widget
                                          $this->user->nickname),
                                  $action == 'usergroups',
                                  'nav_usergroups');
-            if (!is_null($cur) && $this->user->id === $cur->id) {
+            if (common_config('invite', 'enabled') && !is_null($cur) && $this->user->id === $cur->id) {
                 $this->out->menuItem(common_local_url('invite'),
                                      _('Invite'),
                                      sprintf(_('Invite friends and colleagues to join you on %s'),
index 0d882482279f6bfe934450c5d165e85bf1f58d23..2fe6ab69b7799cc19fd683a5e6c27adbfb2f96ad 100644 (file)
@@ -43,10 +43,14 @@ if (!defined('LACONICA')) {
 
 function theme_file($relative, $theme=null)
 {
-    if (!$theme) {
+    if (empty($theme)) {
         $theme = common_config('site', 'theme');
     }
-    return INSTALLDIR.'/theme/'.$theme.'/'.$relative;
+    $dir = common_config('theme', 'dir');
+    if (empty($dir)) {
+        $dir = INSTALLDIR.'/theme';
+    }
+    return $dir.'/'.$theme.'/'.$relative;
 }
 
 /**
@@ -60,13 +64,31 @@ function theme_file($relative, $theme=null)
 
 function theme_path($relative, $theme=null)
 {
-    if (!$theme) {
+    if (empty($theme)) {
         $theme = common_config('site', 'theme');
     }
+
+    $path = common_config('theme', 'path');
+
+    if (empty($path)) {
+        $path = common_config('site', 'path') . '/theme/';
+    }
+
+    if ($path[strlen($path)-1] != '/') {
+        $path .= '/';
+    }
+
+    if ($path[0] != '/') {
+        $path = '/'.$path;
+    }
+
     $server = common_config('theme', 'server');
-    if ($server) {
-        return 'http://'.$server.'/'.$theme.'/'.$relative;
-    } else {
-        return common_path('theme/'.$theme.'/'.$relative);
+
+    if (empty($server)) {
+        $server = common_config('site', 'server');
     }
+
+    // XXX: protocol
+
+    return 'http://'.$server.$path.$theme.'/'.$relative;
 }
index f538a0298148e2c49659dab968e5cb7c5c89a381..40e5b5067796e45e1d914a3d2bca335ddd16524a 100644 (file)
@@ -278,6 +278,67 @@ class TwitterapiAction extends Action
         return $twitter_dm;
     }
 
+    function twitter_relationship_array($source, $target)
+    {
+        $relationship = array();
+
+        $relationship['source'] =
+            $this->relationship_details_array($source, $target);
+        $relationship['target'] =
+            $this->relationship_details_array($target, $source);
+
+        return array('relationship' => $relationship);
+    }
+
+    function relationship_details_array($source, $target)
+    {
+        $details = array();
+
+        $details['screen_name'] = $source->nickname;
+        $details['followed_by'] = $target->isSubscribed($source);
+        $details['following'] = $source->isSubscribed($target);
+
+        $notifications = false;
+
+        if ($source->isSubscribed($target)) {
+
+            $sub = Subscription::pkeyGet(array('subscriber' =>
+                $source->id, 'subscribed' => $target->id));
+
+            if (!empty($sub)) {
+                $notifications = ($sub->jabber || $sub->sms);
+            }
+        }
+
+        $details['notifications_enabled'] = $notifications;
+        $details['blocking'] = $source->hasBlocked($target);
+        $details['id'] = $source->id;
+
+        return $details;
+    }
+
+    function show_twitter_xml_relationship($relationship)
+    {
+        $this->elementStart('relationship');
+
+        foreach($relationship as $element => $value) {
+            if ($element == 'source' || $element == 'target') {
+                $this->elementStart($element);
+                $this->show_xml_relationship_details($value);
+                $this->elementEnd($element);
+            }
+        }
+
+        $this->elementEnd('relationship');
+    }
+
+    function show_xml_relationship_details($details)
+    {
+        foreach($details as $element => $value) {
+            $this->element($element, null, $value);
+        }
+    }
+
     function show_twitter_xml_status($twitter_status)
     {
         $this->elementStart('status');
index 0aff893fd81ee8b66f5d8dace456c53b69635cbe..d4d79afb30c25fc76d405887a5f2c2e06ffe80ec 100644 (file)
@@ -114,7 +114,7 @@ function common_check_user($nickname, $password)
         return false;
     }
     $user = User::staticGet('nickname', $nickname);
-    if (is_null($user)) {
+    if (is_null($user) || $user === false) {
         return false;
     } else {
         if (0 == strcmp(common_munge_password($password, $user->id),
@@ -139,8 +139,22 @@ function common_have_session()
 
 function common_ensure_session()
 {
+    $c = null;
+    if (array_key_exists(session_name, $_COOKIE)) {
+        $c = $_COOKIE[session_name()];
+    }
     if (!common_have_session()) {
+        if (common_config('sessions', 'handle')) {
+            Session::setSaveHandler();
+        }
         @session_start();
+        if (!isset($_SESSION['started'])) {
+            $_SESSION['started'] = time();
+            if (!empty($c)) {
+                common_log(LOG_WARNING, 'Session cookie "' . $_COOKIE[session_name()] . '" ' .
+                           ' is set but started value is null');
+            }
+        }
     }
 }
 
@@ -485,44 +499,73 @@ function common_linkify($url) {
     // It comes in special'd, so we unspecial it before passing to the stringifying
     // functions
     $url = htmlspecialchars_decode($url);
-    $display = File_redirection::_canonUrl($url);
+
+    $canon = File_redirection::_canonUrl($url);
+
     $longurl_data = File_redirection::where($url);
     if (is_array($longurl_data)) {
         $longurl = $longurl_data['url'];
     } elseif (is_string($longurl_data)) {
         $longurl = $longurl_data;
     } else {
-        die('impossible to linkify');
+        throw new ServerException("Can't linkify url '$url'");
+    }
+
+    $attrs = array('href' => $canon, 'rel' => 'external');
+
+    $is_attachment = false;
+    $attachment_id = null;
+    $has_thumb = false;
+
+    // Check to see whether there's a filename associated with this URL.
+    // If there is, it's an upload and qualifies as an attachment
+
+    $localfile = File::staticGet('url', $longurl);
+
+    if (!empty($localfile)) {
+        if (isset($localfile->filename)) {
+            $is_attachment = true;
+            $attachment_id = $localfile->id;
+        }
     }
 
-    $attrs = array('href' => $longurl, 'rel' => 'external');
+    // if this URL is an attachment, then we set class='attachment' and id='attahcment-ID'
+    // where ID is the id of the attachment for the given URL.
+    //
+    // we need a better test telling what can be shown as an attachment
+    // we're currently picking up oembeds only.
+    // I think the best option is another file_view table in the db
+    // and associated dbobject.
 
-// if this URL is an attachment, then we set class='attachment' and id='attahcment-ID'
-// where ID is the id of the attachment for the given URL.
-//
-// we need a better test telling what can be shown as an attachment
-// we're currently picking up oembeds only.
-// I think the best option is another file_view table in the db
-// and associated dbobject.
     $query = "select file_oembed.file_id as file_id from file join file_oembed on file.id = file_oembed.file_id where file.url='$longurl'";
     $file = new File;
     $file->query($query);
     $file->fetch();
 
     if (!empty($file->file_id)) {
+        $is_attachment = true;
+        $attachment_id = $file->file_id;
+
         $query = "select file_thumbnail.file_id as file_id from file join file_thumbnail on file.id = file_thumbnail.file_id where file.url='$longurl'";
         $file2 = new File;
         $file2->query($query);
         $file2->fetch();
 
-        if (empty($file2->file_id)) {
-            $attrs['class'] = 'attachment';
-        } else {
+        if (!empty($file2)) {
+            $has_thumb = true;
+        }
+    }
+
+    // Add clippy
+    if ($is_attachment) {
+        $attrs['class'] = 'attachment';
+        if ($has_thumb) {
             $attrs['class'] = 'attachment thumbnail';
         }
-        $attrs['id'] = "attachment-{$file->file_id}";
+        $attrs['id'] = "attachment-{$attachment_id}";
     }
-    return XMLStringer::estring('a', $attrs, $display);
+
+    return XMLStringer::estring('a', $attrs, $url);
 }
 
 function common_shorten_links($text)
@@ -790,7 +833,12 @@ function common_date_iso8601($dt)
 
 function common_sql_now()
 {
-    return strftime('%Y-%m-%d %H:%M:%S', time());
+    return common_sql_date(time());
+}
+
+function common_sql_date($datetime)
+{
+    return strftime('%Y-%m-%d %H:%M:%S', $datetime);
 }
 
 function common_redirect($url, $code=307)
@@ -826,89 +874,91 @@ function common_broadcast_notice($notice, $remote=false)
 
 function common_enqueue_notice($notice)
 {
+    $transports = array('omb', 'sms', 'public', 'twitter', 'facebook', 'ping');
+
+    if (common_config('xmpp', 'enabled'))
+    {
+        $transports[] = 'jabber';
+    }
+
     if (common_config('queue','subsystem') == 'stomp') {
-       // use an external message queue system via STOMP
-       require_once("Stomp.php");
-       $con = new Stomp(common_config('queue','stomp_server'));
-       if (!$con->connect()) {
-               common_log(LOG_ERR, 'Failed to connect to queue server');
-               return false;
-       }
-       $queue_basename = common_config('queue','queue_basename');
-       foreach (array('jabber', 'omb', 'sms', 'public', 'twitter', 'facebook', 'ping') as $transport) {
-               if (!$con->send(
-                       '/queue/'.$queue_basename.'-'.$transport, // QUEUE
-                       $notice->id,            // BODY of the message
-                       array (                 // HEADERS of the msg
-                       'created' => $notice->created
-                       ))) {
-                       common_log(LOG_ERR, 'Error sending to '.$transport.' queue');
-                       return false;
-               }
-        common_log(LOG_DEBUG, 'complete remote queueing notice ID = ' . $notice->id . ' for ' . $transport);
-       }
-
-       //send tags as headers, so they can be used as JMS selectors
-        common_log(LOG_DEBUG, 'searching for tags ' . $notice->id);
-        $tags = array();
-       $tag = new Notice_tag();
-        $tag->notice_id = $notice->id;
-        if ($tag->find()) {
-            while ($tag->fetch()) {
-               common_log(LOG_DEBUG, 'tag found = ' . $tag->tag);
-               array_push($tags,$tag->tag);
-            }
-        }
-        $tag->free();
-
-       $con->send('/topic/laconica.'.$notice->profile_id,
-                       $notice->content,
-                       array(
-                               'profile_id' => $notice->profile_id,
-                               'created' => $notice->created,
-                               'tags' => implode($tags,' - ')
-                               )
-                       );
-        common_log(LOG_DEBUG, 'sent to personal topic ' . $notice->id);
-       $con->send('/topic/laconica.allusers',
-                       $notice->content,
-                       array(
-                               'profile_id' => $notice->profile_id,
-                               'created' => $notice->created,
-                               'tags' => implode($tags,' - ')
-                               )
-                       );
-        common_log(LOG_DEBUG, 'sent to catch-all topic ' . $notice->id);
-       $result = true;
+        common_enqueue_notice_stomp($notice, $transports);
     }
     else {
-       // in any other case, 'internal'
-       foreach (array('jabber', 'omb', 'sms', 'public', 'twitter', 'facebook', 'ping') as $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);
-                   return false;
-               }
-               common_log(LOG_DEBUG, 'complete queueing notice ID = ' . $notice->id . ' for ' . $transport);
-        }
+        common_enqueue_notice_db($notice, $transports);
     }
     return $result;
 }
 
-function common_post_inbox_transports()
+function common_enqueue_notice_stomp($notice, $transports)
 {
-    $transports = array('omb', 'sms');
+    // use an external message queue system via STOMP
+    require_once("Stomp.php");
+
+    $server = common_config('queue','stomp_server');
+    $username = common_config('queue', 'stomp_username');
+    $password = common_config('queue', 'stomp_password');
+
+    $con = new Stomp($server);
+
+    if (!$con->connect($username, $password)) {
+        common_log(LOG_ERR, 'Failed to connect to queue server');
+        return false;
+    }
+
+    $queue_basename = common_config('queue','queue_basename');
 
-    if (common_config('xmpp', 'enabled')) {
-        $transports = array_merge($transports, array('jabber', 'public'));
+    foreach ($transports as $transport) {
+        $result = $con->send('/queue/'.$queue_basename.'-'.$transport, // QUEUE
+                             $notice->id,              // BODY of the message
+                             array ('created' => $notice->created));
+        if (!$result) {
+            common_log(LOG_ERR, 'Error sending to '.$transport.' queue');
+            return false;
+        }
+        common_log(LOG_DEBUG, 'complete remote queueing notice ID = ' . $notice->id . ' for ' . $transport);
     }
 
-    return $transports;
+    //send tags as headers, so they can be used as JMS selectors
+    common_log(LOG_DEBUG, 'searching for tags ' . $notice->id);
+    $tags = array();
+    $tag = new Notice_tag();
+    $tag->notice_id = $notice->id;
+    if ($tag->find()) {
+        while ($tag->fetch()) {
+            common_log(LOG_DEBUG, 'tag found = ' . $tag->tag);
+            array_push($tags,$tag->tag);
+        }
+    }
+    $tag->free();
+
+    $con->send('/topic/laconica.'.$notice->profile_id,
+               $notice->content,
+               array(
+                     'profile_id' => $notice->profile_id,
+                     'created' => $notice->created,
+                     'tags' => implode($tags,' - ')
+                     )
+               );
+    common_log(LOG_DEBUG, 'sent to personal topic ' . $notice->id);
+    $con->send('/topic/laconica.allusers',
+               $notice->content,
+               array(
+                     'profile_id' => $notice->profile_id,
+                     'created' => $notice->created,
+                     'tags' => implode($tags,' - ')
+                     )
+               );
+    common_log(LOG_DEBUG, 'sent to catch-all topic ' . $notice->id);
+    $result = true;
+}
+
+function common_enqueue_notice_db($notice, $transports)
+{
+    // in any other case, 'internal'
+    foreach ($transports as $transport) {
+        common_enqueue_notice_transport($notice, $transport);
+    }
 }
 
 function common_enqueue_notice_transport($notice, $transport)
@@ -1048,20 +1098,26 @@ function common_ensure_syslog()
 {
     static $initialized = false;
     if (!$initialized) {
-        openlog(common_config('syslog', 'appname'), 0, LOG_USER);
+        openlog(common_config('syslog', 'appname'), 0,
+            common_config('syslog', 'facility'));
         $initialized = true;
     }
 }
 
+function common_log_line($priority, $msg)
+{
+    static $syslog_priorities = array('LOG_EMERG', 'LOG_ALERT', 'LOG_CRIT', 'LOG_ERR',
+                                      'LOG_WARNING', 'LOG_NOTICE', 'LOG_INFO', 'LOG_DEBUG');
+    return date('Y-m-d H:i:s') . ' ' . $syslog_priorities[$priority] . ': ' . $msg . "\n";
+}
+
 function common_log($priority, $msg, $filename=null)
 {
     $logfile = common_config('site', 'logfile');
     if ($logfile) {
         $log = fopen($logfile, "a");
         if ($log) {
-            static $syslog_priorities = array('LOG_EMERG', 'LOG_ALERT', 'LOG_CRIT', 'LOG_ERR',
-                                              'LOG_WARNING', 'LOG_NOTICE', 'LOG_INFO', 'LOG_DEBUG');
-            $output = date('Y-m-d H:i:s') . ' ' . $syslog_priorities[$priority] . ': ' . $msg . "\n";
+            $output = common_log_line($priority, $msg);
             fwrite($log, $output);
             fclose($log);
         }
@@ -1292,18 +1348,39 @@ function common_canonical_sms($sms)
 function common_error_handler($errno, $errstr, $errfile, $errline, $errcontext)
 {
     switch ($errno) {
+
+     case E_ERROR:
+     case E_COMPILE_ERROR:
+     case E_CORE_ERROR:
      case E_USER_ERROR:
-        common_log(LOG_ERR, "[$errno] $errstr ($errfile:$errline)");
-        exit(1);
+     case E_PARSE:
+     case E_RECOVERABLE_ERROR:
+        common_log(LOG_ERR, "[$errno] $errstr ($errfile:$errline) [ABORT]");
+        die();
         break;
 
+     case E_WARNING:
+     case E_COMPILE_WARNING:
+     case E_CORE_WARNING:
      case E_USER_WARNING:
         common_log(LOG_WARNING, "[$errno] $errstr ($errfile:$errline)");
         break;
 
+     case E_NOTICE:
      case E_USER_NOTICE:
         common_log(LOG_NOTICE, "[$errno] $errstr ($errfile:$errline)");
         break;
+
+     case E_STRICT:
+     case E_DEPRECATED:
+     case E_USER_DEPRECATED:
+        // XXX: config variable to log this stuff, too
+        break;
+
+     default:
+        common_log(LOG_ERR, "[$errno] $errstr ($errfile:$errline) [UNKNOWN LEVEL, die()'ing]");
+        die();
+        break;
     }
 
     // FIXME: show error page if we're on the Web
@@ -1377,3 +1454,92 @@ function common_database_tablename($tablename)
   //table prefixes could be added here later
   return $tablename;
 }
+
+function common_shorten_url($long_url)
+{
+    $user = common_current_user();
+    if (empty($user)) {
+        // common current user does not find a user when called from the XMPP daemon
+        // therefore we'll set one here fix, so that XMPP given URLs may be shortened
+        $svc = 'ur1.ca';
+    } else {
+        $svc = $user->urlshorteningservice;
+    }
+
+    $curlh = curl_init();
+    curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait
+    curl_setopt($curlh, CURLOPT_USERAGENT, 'Laconica');
+    curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true);
+
+    switch($svc) {
+     case 'ur1.ca':
+        require_once INSTALLDIR.'/lib/Shorturl_api.php';
+        $short_url_service = new LilUrl;
+        $short_url = $short_url_service->shorten($long_url);
+        break;
+
+     case '2tu.us':
+        $short_url_service = new TightUrl;
+        require_once INSTALLDIR.'/lib/Shorturl_api.php';
+        $short_url = $short_url_service->shorten($long_url);
+        break;
+
+     case 'ptiturl.com':
+        require_once INSTALLDIR.'/lib/Shorturl_api.php';
+        $short_url_service = new PtitUrl;
+        $short_url = $short_url_service->shorten($long_url);
+        break;
+
+     case 'bit.ly':
+        curl_setopt($curlh, CURLOPT_URL, 'http://bit.ly/api?method=shorten&long_url='.urlencode($long_url));
+        $short_url = current(json_decode(curl_exec($curlh))->results)->hashUrl;
+        break;
+
+     case 'is.gd':
+        curl_setopt($curlh, CURLOPT_URL, 'http://is.gd/api.php?longurl='.urlencode($long_url));
+        $short_url = curl_exec($curlh);
+        break;
+     case 'snipr.com':
+        curl_setopt($curlh, CURLOPT_URL, 'http://snipr.com/site/snip?r=simple&link='.urlencode($long_url));
+        $short_url = curl_exec($curlh);
+        break;
+     case 'metamark.net':
+        curl_setopt($curlh, CURLOPT_URL, 'http://metamark.net/api/rest/simple?long_url='.urlencode($long_url));
+        $short_url = curl_exec($curlh);
+        break;
+     case 'tinyurl.com':
+        curl_setopt($curlh, CURLOPT_URL, 'http://tinyurl.com/api-create.php?url='.urlencode($long_url));
+        $short_url = curl_exec($curlh);
+        break;
+     default:
+        $short_url = false;
+    }
+
+    curl_close($curlh);
+
+    return $short_url;
+}
+
+function common_client_ip()
+{
+    if (!isset($_SERVER) || !array_key_exists('REQUEST_METHOD', $_SERVER)) {
+        return null;
+    }
+
+    if ($_SERVER['HTTP_X_FORWARDED_FOR']) {
+        if ($_SERVER['HTTP_CLIENT_IP']) {
+            $proxy = $_SERVER['HTTP_CLIENT_IP'];
+        } else {
+            $proxy = $_SERVER['REMOTE_ADDR'];
+        }
+        $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
+    } else {
+        if ($_SERVER['HTTP_CLIENT_IP']) {
+            $ip = $_SERVER['HTTP_CLIENT_IP'];
+        } else {
+            $ip = $_SERVER['REMOTE_ADDR'];
+        }
+    }
+
+    return array($ip, $proxy);
+}
index a078cd9f7e13c90fd10e520caa7cb004c36588a8..986e09c25e4c353cb0d849b5a4736edfd6638cac 100644 (file)
@@ -22,7 +22,7 @@ if (!defined('LACONICA')) { exit(1); }
 require_once(INSTALLDIR.'/lib/queuehandler.php');
 
 /**
- * Common superclass for all XMPP-using queue handlers. They all need to 
+ * Common superclass for all XMPP-using queue handlers. They all need to
  * service their message queues on idle, and forward any incoming messages
  * to the XMPP listener connection. So, we abstract out common code to a
  * superclass.
@@ -30,12 +30,11 @@ require_once(INSTALLDIR.'/lib/queuehandler.php');
 
 class XmppQueueHandler extends QueueHandler
 {
-    
     function start()
     {
         # Low priority; we don't want to receive messages
         $this->log(LOG_INFO, "INITIALIZE");
-        $this->conn = jabber_connect($this->_id);
+        $this->conn = jabber_connect($this->_id.$this->transport());
         if ($this->conn) {
             $this->conn->addEventHandler('message', 'forward_message', $this);
             $this->conn->addEventHandler('reconnect', 'handle_reconnect', $this);
@@ -44,7 +43,7 @@ class XmppQueueHandler extends QueueHandler
         }
         return !is_null($this->conn);
     }
-    
+
     function handle_reconnect(&$pl)
     {
         $this->conn->processUntil('session_start');
@@ -63,7 +62,7 @@ class XmppQueueHandler extends QueueHandler
             die($e->getMessage());
         }
     }
-    
+
     function forward_message(&$pl)
     {
         if ($pl['type'] != 'chat') {
index 4699ce636c4405f119f7ae514d83fa3be1e3f17b..3cf9fefc1380fa5bb958040bb2642334184a40a3 100644 (file)
@@ -66,7 +66,7 @@ class FBConnectauthAction extends Action
             // User is already logged in.  Does she already have a linked Facebook acct?
             $flink = Foreign_link::getByForeignID($this->fbuid, FACEBOOK_CONNECT_SERVICE);
 
-            if ($flink) {
+            if (!empty($flink)) {
 
                 // User already has a linked Facebook account and shouldn't be here
                 common_debug('There is already a local user (' . $flink->user_id .
@@ -337,7 +337,7 @@ class FBConnectauthAction extends Action
         if ($flink) {
             $user = $flink->getUser();
 
-            if ($user) {
+            if (!empty($user)) {
 
                 common_debug("Logged in Facebook user $flink->foreign_id as user $user->id ($user->nickname)");
 
index 564fdaee9e44e0eb550ff8c4f3749f50aceda494..e52675459e93f32eace3927da70e54baf1204473 100644 (file)
 #site_nav_global_primary #nav_fb {
 position:relative;
 margin-left:18px;
-margin-right:-7px;
 }
 
-#nav_fb .fb_profile_pic_rendered img {
-position:relative;
-top:3px;
-left:0;
+#nav_fb #fbc_profile-pic {
+position:absolute;
+top:-3px;
+left:-18px;
 display:inline;
 border:1px solid #3B5998;
 padding:1px;
 }
 
-#nav_fb img {
+#nav_fb #fb_favicon {
 position:absolute;
 top:-13px;
-left:-11px;
+left:-25px;
 display:inline;
 }
 
index a366985be424432c076dadadd8fe54cc922a9088..d8af1a4e86e58ff7493e8344574be923886c599b 100644 (file)
@@ -69,100 +69,158 @@ class FBConnectPlugin extends Plugin
     // Add in xmlns:fb
     function onStartShowHTML($action)
     {
-        // XXX: Horrible hack to make Safari, FF2, and Chrome work with
-        // Facebook Connect. These browser cannot use Facebook's
-        // DOM parsing routines unless the mime type of the page is
-        // text/html even though Facebook Connect uses XHTML.  This is
-        // A bug in Facebook Connect, and this is a temporary solution
-        // until they fix their JavaScript libs.
-        header('Content-Type: text/html');
 
-        $action->extraHeaders();
+        if ($this->reqFbScripts($action)) {
 
-        $action->startXML('html',
-            '-//W3C//DTD XHTML 1.0 Strict//EN',
-            'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd');
+            // XXX: Horrible hack to make Safari, FF2, and Chrome work with
+            // Facebook Connect. These browser cannot use Facebook's
+            // DOM parsing routines unless the mime type of the page is
+            // text/html even though Facebook Connect uses XHTML.  This is
+            // A bug in Facebook Connect, and this is a temporary solution
+            // until they fix their JavaScript libs.
+            header('Content-Type: text/html');
 
-        $language = $action->getLanguage();
+            $action->extraHeaders();
 
-        $action->elementStart('html',
-            array('xmlns'  => 'http://www.w3.org/1999/xhtml',
-                  'xmlns:fb' => 'http://www.facebook.com/2008/fbml',
-                  'xml:lang' => $language,
-                  'lang'     => $language));
+            $action->startXML('html',
+                '-//W3C//DTD XHTML 1.0 Strict//EN',
+                'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd');
 
-        return false;
+            $language = $action->getLanguage();
+
+            $action->elementStart('html',
+                array('xmlns'  => 'http://www.w3.org/1999/xhtml',
+                      'xmlns:fb' => 'http://www.facebook.com/2008/fbml',
+                      'xml:lang' => $language,
+                      'lang'     => $language));
+
+            return false;
+
+        } else {
+
+            return true;
+        }
     }
 
     // Note: this script needs to appear in the <body>
 
     function onStartShowHeader($action)
     {
-        $apikey = common_config('facebook', 'apikey');
-        $plugin_path = common_path('plugins/FBConnect');
-
-        $login_url = common_local_url('FBConnectAuth');
-        $logout_url = common_local_url('logout');
-
-        // XXX: Facebook says we don't need this FB_RequireFeatures(),
-        // but we actually do, for IE and Safari. Gar.
-
-        $html = sprintf('<script type="text/javascript">
-                            window.onload = function () {
-                                FB_RequireFeatures(
-                                    ["XFBML"],
-                                        function() {
-                                            FB.Facebook.init("%s", "../xd_receiver.html");
-                                        }
-                                    ); }
-
-                            function goto_login() {
-                                window.location = "%s";
-                            }
-
-                            function goto_logout() {
-                                window.location = "%s";
-                            }
-                          </script>', $apikey,
-                              $login_url, $logout_url);
-
-        $action->raw($html);
+        if ($this->reqFbScripts($action)) {
+
+            $apikey = common_config('facebook', 'apikey');
+            $plugin_path = common_path('plugins/FBConnect');
+
+            $login_url = common_local_url('FBConnectAuth');
+            $logout_url = common_local_url('logout');
+
+            // XXX: Facebook says we don't need this FB_RequireFeatures(),
+            // but we actually do, for IE and Safari. Gar.
+
+            $html = sprintf('<script type="text/javascript">
+                                window.onload = function () {
+                                    FB_RequireFeatures(
+                                        ["XFBML"],
+                                            function() {
+                                                FB.Facebook.init("%s", "../xd_receiver.html");
+                                            }
+                                        ); }
+
+                                function goto_login() {
+                                    window.location = "%s";
+                                }
+
+                                function goto_logout() {
+                                    window.location = "%s";
+                                }
+                              </script>', $apikey,
+                                  $login_url, $logout_url);
+
+            $action->raw($html);
+        }
+
     }
 
     // Note: this script needs to appear as close as possible to </body>
 
     function onEndShowFooter($action)
     {
+        if ($this->reqFbScripts($action)) {
 
-        $action->element('script',
-            array('type' => 'text/javascript',
-                  'src'  => 'http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php'),
-                  '');
+            $action->element('script',
+                array('type' => 'text/javascript',
+                      'src'  => 'http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php'),
+                      '');
+        }
     }
 
     function onEndShowLaconicaStyles($action)
     {
-        $action->element('link', array('rel' => 'stylesheet',
-            'type' => 'text/css',
-            'href' => common_path('plugins/FBConnect/FBConnectPlugin.css')));
+
+        if ($this->reqFbScripts($action)) {
+
+            $action->element('link', array('rel' => 'stylesheet',
+                'type' => 'text/css',
+                'href' => common_path('plugins/FBConnect/FBConnectPlugin.css')));
+        }
     }
 
-    function onStartPrimaryNav($action)
+    /**
+     * Does the Action we're plugged into require the FB Scripts?  We only
+     * want to output FB namespace, scripts, CSS, etc. on the pages that
+     * really need them.
+     *
+     * @param Action the action in question
+     *
+     * @return boolean true
+     */
+
+    function reqFbScripts($action) {
+
+        // If you're logged in w/FB Connect, you always need the FB stuff
+
+        $fbuid = $this->loggedIn();
+
+        if (!empty($fbuid)) {
+            return true;
+        }
+
+        // List of actions that require FB stuff
+
+        $needy = array('FBConnectLoginAction',
+                       'FBConnectauthAction',
+                       'FBConnectSettingsAction');
+
+        if (in_array(get_class($action), $needy)) {
+            return true;
+        }
+
+        return false;
+
+    }
+
+    /**
+     * Is the user currently logged in with FB Connect?
+     *
+     * @return mixed $fbuid the Facebook ID of the logged in user, or null
+     */
+
+    function loggedIn()
     {
         $user = common_current_user();
 
-        if ($user) {
+        if (!empty($user)) {
 
             $flink = Foreign_link::getByUserId($user->id,
                 FACEBOOK_CONNECT_SERVICE);
             $fbuid = 0;
 
-            if ($flink) {
+            if (!empty($flink)) {
 
                 try {
 
                     $facebook = getFacebook();
-                    $fbuid = getFacebook()->get_loggedin_user();
+                    $fbuid    = getFacebook()->get_loggedin_user();
 
                 } catch (Exception $e) {
                     common_log(LOG_WARNING,
@@ -170,23 +228,47 @@ class FBConnectPlugin extends Plugin
                             $e->getMessage());
                 }
 
-                // Display Facebook Logged in indicator w/Facebook favicon
-
                 if ($fbuid > 0) {
+                    return $fbuid;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    function onStartPrimaryNav($action)
+    {
 
-                    $action->elementStart('li', array('id' => 'nav_fb'));
-                    $action->elementStart('fb:profile-pic', array('uid' => $flink->foreign_id,
-                        'linked' => 'false',
-                        'width' => 16,
-                        'height' => 16));
-                    $action->elementEnd('fb:profile-pic');
+        $user = common_current_user();
 
-                    $iconurl =  common_path('/plugins/FBConnect/fbfavicon.ico');
-                    $action->element('img', array('src' => $iconurl));
+        if (!empty($user)) {
 
-                    $action->elementEnd('li');
+            $fbuid = $this->loggedIn();
+
+            if (!empty($fbuid)) {
+
+                /* Default FB silhouette pic for FB users who haven't
+                   uploaded a profile pic yet. */
+
+                $silhouetteUrl =
+                    'http://static.ak.fbcdn.net/pics/q_silhouette.gif';
+
+                $url = $this->getProfilePicURL($fbuid);
+
+                $action->elementStart('li', array('id' => 'nav_fb'));
+
+                $action->element('img', array('id' => 'fbc_profile-pic',
+                    'src' => (!empty($url)) ? $url : $silhouetteUrl,
+                    'alt' => 'Facebook Connect User',
+                    'width' => '16'), '');
+
+                $iconurl =  common_path('plugins/FBConnect/fbfavicon.ico');
+                $action->element('img', array('id' => 'fb_favicon',
+                    'src' => $iconurl));
+
+                $action->elementEnd('li');
 
-                }
             }
 
             $action->menuItem(common_local_url('all', array('nickname' => $user->nickname)),
@@ -200,14 +282,16 @@ class FBConnectPlugin extends Plugin
              $action->menuItem(common_local_url('smssettings'),
                  _('Connect'), _('Connect to SMS, Twitter'), false, 'nav_connect');
             }
-            $action->menuItem(common_local_url('invite'),
-                _('Invite'),
-                sprintf(_('Invite friends and colleagues to join you on %s'),
-                common_config('site', 'name')),
-                false, 'nav_invitecontact');
+            if (common_config('invite', 'enabled')) {
+                $action->menuItem(common_local_url('invite'),
+                    _('Invite'),
+                    sprintf(_('Invite friends and colleagues to join you on %s'),
+                    common_config('site', 'name')),
+                    false, 'nav_invitecontact');
+            }
 
             // Need to override the Logout link to make it do FB stuff
-            if ($flink && $fbuid > 0) {
+            if (!empty($fbuid)) {
 
                 $logout_url = common_local_url('logout');
                 $title =  _('Logout from the site');
@@ -245,7 +329,7 @@ class FBConnectPlugin extends Plugin
 
     function onStartShowLocalNavBlock($action)
     {
-        $action_name = get_class($action);
+        $action_name   = get_class($action);
 
         $login_actions = array('LoginAction', 'RegisterAction',
             'OpenidloginAction', 'FBConnectLoginAction');
@@ -256,7 +340,7 @@ class FBConnectPlugin extends Plugin
             return false;
         }
 
-        $connect_actions = array('SmssettingsAction',
+        $connect_actions = array('SmssettingsAction', 'ImsettingsAction',
             'TwittersettingsAction', 'FBConnectSettingsAction');
 
         if (in_array($action_name, $connect_actions)) {
@@ -270,23 +354,13 @@ class FBConnectPlugin extends Plugin
 
     function onStartLogout($action)
     {
-        $user = common_current_user();
-
-        $flink = Foreign_link::getByUserId($user->id, FACEBOOK_CONNECT_SERVICE);
-
         $action->logout();
+        $fbuid = $this->loggedIn();
 
-        if ($flink) {
-
-            $facebook = getFacebook();
-
+        if (!empty($fbuid)) {
             try {
-                $fbuid = $facebook->get_loggedin_user();
-
-                if ($fbuid > 0) {
-                    $facebook->logout(common_local_url('public'));
-                }
-
+                $facebook = getFacebook();
+                $facebook->expire_session();
             } catch (Exception $e) {
                 common_log(LOG_WARNING, 'Could\'t logout of Facebook: ' .
                     $e->getMessage());
@@ -296,4 +370,28 @@ class FBConnectPlugin extends Plugin
         return true;
     }
 
+    function getProfilePicURL($fbuid)
+    {
+
+        $facebook = getFacebook();
+        $url      = null;
+
+        try {
+
+            $fqry = 'SELECT pic_square FROM user WHERE uid = %s';
+
+            $result = $facebook->api_client->fql_query(sprintf($fqry, $fbuid));
+
+            if (!empty($result)) {
+                $url = $result[0]['pic_square'];
+            }
+
+        } catch (Exception $e) {
+            common_log(LOG_WARNING, "Facebook client failure requesting profile pic!");
+        }
+
+       return $url;
+
+    }
+
 }
diff --git a/scripts/allsites.php b/scripts/allsites.php
new file mode 100755 (executable)
index 0000000..d6768c2
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/env php
+<?php
+/*
+ * Laconica - a 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/>.
+ */
+
+# Abort if called from a web server
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+$helptext = <<<ENDOFHELP
+allsites.php - list all sites configured for multi-site use
+
+returns the nickname of each site configured for multi-site use
+
+ENDOFHELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+$sn = new Status_network();
+
+if ($sn->find()) {
+    while ($sn->fetch()) {
+        print "$sn->nickname\n";
+    }
+}
\ No newline at end of file
diff --git a/scripts/commandline.inc b/scripts/commandline.inc
new file mode 100644 (file)
index 0000000..3b6ef60
--- /dev/null
@@ -0,0 +1,174 @@
+<?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/>.
+ */
+
+// -*- mode: php -*-
+
+# 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('LACONICA', true);
+
+// Set various flags so we don't time out on long-running processes
+
+ini_set("max_execution_time", "0");
+ini_set("max_input_time", "0");
+set_time_limit(0);
+mb_internal_encoding('UTF-8');
+
+// Add extlib to our path so we can get Console_Getopt
+
+$_extra_path = array(INSTALLDIR.'/extlib/');
+
+set_include_path(implode(PATH_SEPARATOR, $_extra_path) . PATH_SEPARATOR . get_include_path());
+
+require_once 'Console/Getopt.php';
+
+// Note: $shortoptions and $longoptions should be pre-defined!
+
+$_default_shortoptions = 'qvhc:s:p:';
+
+$_default_longoptions = array('quiet', 'verbose', 'help', 'conf=', 'server=', 'path=');
+
+if (isset($shortoptions)) {
+    $shortoptions .= $_default_shortoptions;
+} else {
+    $shortoptions = $_default_shortoptions;
+}
+
+if (isset($longoptions)) {
+    $longoptions = array_merge($longoptions, $_default_longoptions);
+} else {
+    $longoptions = $_default_longoptions;
+}
+
+$parser = new Console_Getopt();
+
+$result = $parser->getopt($argv, $shortoptions, $longoptions);
+
+if (PEAR::isError($result)) {
+    print $result->getMessage()."\n";
+    exit(1);
+} else {
+    list($options, $args) = $result;
+}
+
+function show_help()
+{
+    global $helptext;
+
+    $_default_help_text = <<<END_OF_DEFAULT
+      General options:
+
+    -q --quiet           Quiet (little output)
+    -v --verbose         Verbose (lots of output)
+    -c --conf=<filename> Use <filename> as config file
+    -s --server=<name>   Use <name> as server name
+    -p --path=<path>     Use <path> as path name
+    -h --help            Show this message and quit.
+
+END_OF_DEFAULT;
+    if (isset($helptext)) {
+        print $helptext;
+    }
+    print $_default_help_text;
+    exit(0);
+}
+
+foreach ($options as $option) {
+
+    switch ($option[0]) {
+     case '--server':
+     case 's':
+        $server = $option[1];
+        break;
+
+     case '--path':
+     case 'p':
+        $path = $option[1];
+        break;
+
+     case '--conf':
+     case 'c':
+        $conffile = $option[1];
+        break;
+
+     case '--help':
+     case 'h':
+        show_help();
+    }
+}
+
+require_once INSTALLDIR . '/lib/common.php';
+
+set_error_handler('common_error_handler');
+
+function _make_matches($opt, $alt)
+{
+    $matches = array();
+
+    if (strlen($opt) > 1 && 0 != strncmp($opt, '--', 2)) {
+        $matches[] = '--'.$opt;
+    } else {
+        $matches[] = $opt;
+    }
+
+    if (!empty($alt)) {
+        if (strlen($alt) > 1 && 0 != strncmp($alt, '--', 2)) {
+            $matches[] = '--'.$alt;
+        } else {
+            $matches[] = $alt;
+        }
+    }
+
+    return $matches;
+}
+
+function have_option($opt, $alt=null)
+{
+    global $options;
+
+    $matches = _make_matches($opt, $alt);
+
+    foreach ($options as $option) {
+        if (in_array($option[0], $matches)) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+function get_option_value($opt, $alt=null)
+{
+    global $options;
+
+    $matches = _make_matches($opt, $alt);
+
+    foreach ($options as $option) {
+        if (in_array($option[0], $matches)) {
+            return $option[1];
+        }
+    }
+
+    return null;
+}
index b18eaa2cd3fa3d783c911997efcfe920ccb36c1e..90e1ec63c0d374411af49e07c3d6fc3d8c430f13 100644 (file)
  * 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(1);
-}
-
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
+$helptext = <<<ENDOFHELP
+USAGE: decache.php <table> <id> [<column>]
+Clears the cache for the object in table <table> with id <id>
+If <column> is specified, use that instead of 'id'
+ENDOFHELP;
 
-if ($argc < 3 || $argc > 4) {
-    print "USAGE: decache.php <table> <id> [<column>]\n";
-    print "Clears the cache for the object in table <table> with id <id>.\n\n";
-    print "If <column> is specified, use that instead of 'id'\n";
-    exit(1);
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+if (count($args) < 2 || count($args) > 3) {
+    show_help();
 }
 
-$table = $argv[1];
-$id = $argv[2];
-if ($argc > 3) {
-    $column = $argv[3];
+$table = $args[0];
+$id = $args[1];
+if (count($args) > 2) {
+    $column = $args[2];
 } else {
-    $colum = 'id';
+    $column = 'id';
 }
 
 $object = Memcached_DataObject::staticGet($table, $column, $id);
diff --git a/scripts/delete_status_network.sh b/scripts/delete_status_network.sh
new file mode 100755 (executable)
index 0000000..3218738
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+source /etc/laconica/setup.cfg
+
+export nickname=$1
+
+export database=$nickname$DBBASE
+
+# Create the db
+
+mysqladmin -h $DBHOST -u $ADMIN --password=$ADMINPASS -f drop $database
+
+mysql -h $DBHOST -u $ADMIN --password=$ADMINPASS $SITEDB << ENDOFCOMMANDS
+
+delete from status_network where nickname = '$nickname';
+
+ENDOFCOMMANDS
+
+for top in $AVATARBASE $FILEBASE $BACKGROUNDBASE; do
+    rm -Rf $top/$nickname
+done
index c28bbe01d911de507ad9022d9ccd28efd40f039e..05e1d9366bed3a65cea394f1e0118bfc8ea2adf6 100755 (executable)
  * 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/mail.php');
-require_once(INSTALLDIR . '/lib/queuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_ENJIT_HELP
+Daemon script for watching new notices and posting to enjit.
+
+    -i --id           Identity (default none)
+
+END_OF_ENJIT_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+require_once INSTALLDIR . '/lib/mail.php';
+require_once INSTALLDIR . '/lib/queuehandler.php';
 
 set_error_handler('common_error_handler');
 
 class EnjitQueueHandler extends QueueHandler
 {
-    
     function transport()
     {
         return 'enjit';
@@ -60,7 +63,6 @@ class EnjitQueueHandler extends QueueHandler
                     return "skipped";
                 }
 
-
                 #
                 # Build an Atom message from the notice
                 #
@@ -93,8 +95,8 @@ class EnjitQueueHandler extends QueueHandler
         $ch   = curl_init();
 
         curl_setopt($ch, CURLOPT_URL, $url);
-                curl_setopt($ch, CURLOPT_HEADER, 1); 
+
+                curl_setopt($ch, CURLOPT_HEADER, 1);
         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
         curl_setopt($ch, CURLOPT_POST, 1) ;
         curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
@@ -103,7 +105,7 @@ class EnjitQueueHandler extends QueueHandler
                 #
         # curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
         # curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
-                # curl_setopt($ch, CURLOPT_VERBOSE, 1); 
+                # curl_setopt($ch, CURLOPT_VERBOSE, 1);
 
         $result = curl_exec($ch);
 
@@ -115,13 +117,18 @@ class EnjitQueueHandler extends QueueHandler
 
                 return $code;
     }
-    
 
 }
 
-mb_internal_encoding('UTF-8');
-
-$id = ($argc > 1) ? $argv[1] : null;
+if (have_option('-i')) {
+    $id = get_option_value('-i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
 $handler = new EnjitQueueHandler($id);
 
index a505e8ecc64c6fd3d68532b0ea0a408d08d42883..05a35577fea71b87f34a35f3746b6a0c68e9c8d7 100755 (executable)
  * 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/facebookutil.php');
-require_once(INSTALLDIR . '/lib/queuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_FACEBOOK_HELP
+Daemon script for pushing new notices to Facebook.
+
+    -i --id           Identity (default none)
+
+END_OF_FACEBOOK_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
 
-set_error_handler('common_error_handler');
+require_once INSTALLDIR . '/lib/facebookutil.php';
+require_once INSTALLDIR . '/lib/queuehandler.php';
 
 class FacebookQueueHandler extends QueueHandler
 {
-    
     function transport()
     {
         return 'facebook';
     }
-    
+
     function start()
     {
         $this->log(LOG_INFO, "INITIALIZE");
@@ -51,20 +52,22 @@ class FacebookQueueHandler extends QueueHandler
     {
         return facebookBroadcastNotice($notice);
     }
-    
+
     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;
+if (have_option('i')) {
+    $id = get_option_value('i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
 $handler = new FacebookQueueHandler($id);
 
diff --git a/scripts/fixup_conversations.php b/scripts/fixup_conversations.php
new file mode 100755 (executable)
index 0000000..0be0b4b
--- /dev/null
@@ -0,0 +1,70 @@
+#!/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/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+common_log(LOG_INFO, 'Fixing up conversations.');
+
+$nid = new Notice();
+$nid->query('select id, reply_to from notice where conversation is null');
+
+while ($nid->fetch()) {
+
+    $cid = null;
+    
+    $notice = new Notice();
+    
+    if (empty($nid->reply_to)) {
+        $cid = $nid->id;
+    } else {
+        $reply = Notice::staticGet('id', $notice->reply_to);
+
+        if (empty($reply)) {
+            common_log(LOG_WARNING, "Replied-to notice $notice->reply_to not found.");
+            $notice->conversation = $notice->id;
+        } else if (empty($reply->conversation)) {
+            common_log(LOG_WARNING, "Replied-to notice $reply->id has no conversation ID.");
+            $notice->conversation = $notice->id;
+        } else {
+            $notice->conversation = $reply->conversation;
+        }
+       
+       unset($reply);
+       $reply = null;
+    }
+
+    print "$notice->conversation";
+
+    $result = $notice->update($orig);
+
+    if (!$result) {
+        common_log_db_error($notice, 'UPDATE', __FILE__);
+        continue;
+    }
+
+    $notice = null;
+    $orig = null;
+    unset($notice);
+    unset($orig);
+    
+    print ".\n";
+}
index 1693760914e32e96c6227e4715fd3c669728aead..8c9a9127fdd772f25658020151063848101758bf 100644 (file)
  */
 
 # 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(1);
-}
-
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
 
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
-require_once('DB.php');
+$helptext = <<<ENDOFHELP
+fixup_utf8.php <maxdate> <maxid> <minid>
+
+Fixup records in a database that stored the data incorrectly (pre-0.7.4 for Laconica).
+
+ENDOFHELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+require_once 'DB.php';
 
 class UTF8FixerUpper
 {
@@ -356,9 +353,9 @@ class UTF8FixerUpper
     }
 }
 
-$max_date = ($argc > 1) ? $argv[1] : null;
-$max_id = ($argc > 2) ? $argv[2] : null;
-$min_id = ($argc > 3) ? $argv[3] : null;
+$max_date = (count($args) > 0) ? $args[0] : null;
+$max_id = (count($args) > 1) ? $args[1] : null;
+$min_id = (count($args) > 2) ? $args[2] : null;
 
 $fixer = new UTF8FixerUpper(array('max_date' => $max_date,
                                   'max_notice' => $max_id,
index d29c95cb08ccaf1934d1501e9df613e151c1882c..9927cc6d95b07a9f4a831b204dde44a6ce5b088b 100755 (executable)
  * 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');
+$helptext = <<<ENDOFHELP
+getpiddir.php - print out the currently configured PID directory
+
+ENDOFHELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
 
-echo common_config('daemon','piddir');
+echo common_config('daemon', 'piddir');
index a45ff79aba495b45bc1b6403576cf10fb2afb541..97c230784f73e5be9897bfc536583a6cdd97c900 100755 (executable)
  * 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";
-    exit();
-}
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
+$helptext = <<<ENDOFHELP
+getvaliddaemons.php - print out the currently configured PID directory
+
+ENDOFHELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
 
 if(common_config('xmpp','enabled')) {
     echo "xmppdaemon.php jabberqueuehandler.php publicqueuehandler.php ";
     echo "xmppconfirmhandler.php ";
 }
-if(common_config('memcached','enabled')) {
-    echo "memcachedqueuehandler.php ";
-}
 if(common_config('twitterbridge','enabled')) {
     echo "twitterstatusfetcher.php ";
 }
index 6d3656d2ee863b75b0141d566a56ebbcf3e0f108..4883fea2015be1e1432fdb4f8910c3100069f43c 100755 (executable)
 
 # 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__) . '/..'));
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
+$helptext = <<<ENDOFHELP
+inbox_users.php <idfile>
 
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
+Update users to use inbox table. Listed in an ID file, default 'ids.txt'.
 
-require_once(INSTALLDIR . '/lib/common.php');
+ENDOFHELP;
 
-$id_file = ($argc > 1) ? $argv[1] : 'ids.txt';
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+$id_file = (count($args) > 1) ? $args[0] : 'ids.txt';
 
 common_log(LOG_INFO, 'Updating user inboxes.');
 
 $ids = file($id_file);
 
 foreach ($ids as $id) {
-       
+
        $user = User::staticGet('id', $id);
 
        if (!$user) {
                common_log(LOG_WARNING, 'No such user: ' . $id);
                continue;
        }
-       
+
        if ($user->inboxed) {
                common_log(LOG_WARNING, 'Already inboxed: ' . $id);
                continue;
        }
-       
+
     common_log(LOG_INFO, 'Updating inbox for user ' . $user->id);
-       
+
        $user->query('BEGIN');
-       
+
        $old_inbox = new Notice_inbox();
        $old_inbox->user_id = $user->id;
-       
+
        $result = $old_inbox->delete();
-       
+
        if (is_null($result) || $result === false) {
                common_log_db_error($old_inbox, 'DELETE', __FILE__);
                continue;
        }
 
        $old_inbox->free();
-       
+
        $inbox = new Notice_inbox();
-       
+
        $result = $inbox->query('INSERT INTO notice_inbox (user_id, notice_id, created) ' .
                                                        'SELECT ' . $user->id . ', notice.id, notice.created ' .
                                                        'FROM subscription JOIN notice ON subscription.subscribed = notice.profile_id ' .
@@ -80,30 +76,30 @@ foreach ($ids as $id) {
                                                        'AND notice.created >= subscription.created ' .
                                                        'AND NOT EXISTS (SELECT user_id, notice_id ' .
                                                        'FROM notice_inbox ' .
-                                                       'WHERE user_id = ' . $user->id . ' ' . 
+                                                       'WHERE user_id = ' . $user->id . ' ' .
                                                        'AND notice_id = notice.id) ' .
                                                        'ORDER BY notice.created DESC ' .
                                                        'LIMIT 0, 1000');
-       
+
        if (is_null($result) || $result === false) {
                common_log_db_error($inbox, 'INSERT', __FILE__);
                continue;
        }
-       
+
        $orig = clone($user);
        $user->inboxed = 1;
        $result = $user->update($orig);
-       
+
        if (!$result) {
                common_log_db_error($user, 'UPDATE', __FILE__);
                continue;
        }
-       
+
        $user->query('COMMIT');
-       
+
        $inbox->free();
        unset($inbox);
-       
+
        if ($cache) {
                $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id));
                $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id . ';last'));
index 130be6e0475201f84a986ce89cd72940f9ff68b5..5b581629d33fc79a58844ab236f46a73944e3263 100755 (executable)
  * 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/jabber.php');
-require_once(INSTALLDIR . '/lib/xmppqueuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_JABBER_HELP
+Daemon script for pushing new notices to Jabber users.
+
+    -i --id           Identity (default none)
 
-set_error_handler('common_error_handler');
+END_OF_JABBER_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+require_once INSTALLDIR . '/lib/common.php';
+require_once INSTALLDIR . '/lib/jabber.php';
+require_once INSTALLDIR . '/lib/xmppqueuehandler.php';
 
 class JabberQueueHandler extends XmppQueueHandler
 {
-
     var $conn = null;
 
     function transport()
@@ -61,13 +63,16 @@ if (common_config('xmpp','enabled')==false) {
     exit();
 }
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
-
-$resource = ($argc > 1) ? $argv[1] : (common_config('xmpp','resource') . '-queuehandler');
+if (have_option('i')) {
+    $id = get_option_value('i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
-$handler = new JabberQueueHandler($resource);
+$handler = new JabberQueueHandler($id);
 
 $handler->runOnce();
index ccc1eef52fecf18fff3218b7e05db77d7b645ee8..cfb11a36fc11e113c9b1f983f744fd21d954d249 100755 (executable)
  * 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');
+$helptext = <<<END_OF_HELP
+Script for converting mail messages into notices. Takes message body
+as STDIN.
+
+END_OF_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
 require_once(INSTALLDIR . '/lib/mail.php');
 require_once('Mail/mimeDecode.php');
 
@@ -36,7 +36,6 @@ require_once('Mail/mimeDecode.php');
 
 class MailerDaemon
 {
-
     function __construct()
     {
     }
index 5fe7cf01df1cfa0cd0baf39145e450402eb535aa..1587192b6fb5b9c1532e6af18411fa76dbc44ba4 100755 (executable)
  * 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/omb.php');
-require_once(INSTALLDIR . '/lib/queuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_OMB_HELP
+Daemon script for pushing new notices to OpenMicroBlogging subscribers.
+
+    -i --id           Identity (default none)
+
+END_OF_OMB_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+require_once INSTALLDIR . '/lib/omb.php';
+require_once INSTALLDIR . '/lib/queuehandler.php';
 
 set_error_handler('common_error_handler');
 
 class OmbQueueHandler extends QueueHandler
 {
-    
+
     function transport()
     {
         return 'omb';
     }
-    
+
     function start()
     {
         $this->log(LOG_INFO, "INITIALIZE");
@@ -56,7 +60,7 @@ class OmbQueueHandler extends QueueHandler
             return omb_broadcast_remote_subscribers($notice);
         }
     }
-    
+
     function finish()
     {
     }
@@ -68,12 +72,15 @@ class OmbQueueHandler extends QueueHandler
     }
 }
 
-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;
+if (have_option('i')) {
+    $id = get_option_value('i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
 $handler = new OmbQueueHandler($id);
 
index f57752cd2d2e7bb0c57b130433bd5dd0bcd8f596..23678ea4b5278f16e6dd0f69c93e0b340931fe56 100644 (file)
  * 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/ping.php');
-require_once(INSTALLDIR . '/lib/queuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_PING_HELP
+Daemon script for pushing new notices to ping servers.
+
+    -i --id           Identity (default none)
 
-set_error_handler('common_error_handler');
+END_OF_PING_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+require_once INSTALLDIR . '/lib/ping.php';
+require_once INSTALLDIR . '/lib/queuehandler.php';
 
 class PingQueueHandler extends QueueHandler {
 
@@ -52,12 +54,15 @@ class PingQueueHandler extends QueueHandler {
        }
 }
 
-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;
+if (have_option('i')) {
+    $id = get_option_value('i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
 $handler = new PingQueueHandler($id);
 
index c0452cbee13ad7dd7260538d7825e2a672164147..701d50e0189f737ed36b41d4f90bcbcf2c23e5dc 100755 (executable)
  * 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/jabber.php');
-require_once(INSTALLDIR . '/lib/xmppqueuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_PUBLIC_HELP
+Daemon script for pushing new notices to public XMPP subscribers.
+
+    -i --id           Identity (default none)
+
+END_OF_PUBLIC_HELP;
 
-set_error_handler('common_error_handler');
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+require_once INSTALLDIR . '/lib/jabber.php';
+require_once INSTALLDIR . '/lib/xmppqueuehandler.php';
 
 class PublicQueueHandler extends XmppQueueHandler
 {
-    
+
     function transport()
     {
         return 'public';
     }
-    
+
     function handle_notice($notice)
     {
         try {
@@ -59,13 +61,16 @@ if (common_config('xmpp','enabled')==false) {
     exit();
 }
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
-
-$resource = ($argc > 1) ? $argv[1] : (common_config('xmpp','resource') . '-public');
+if (have_option('i')) {
+    $id = get_option_value('i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
-$handler = new PublicQueueHandler($resource);
+$handler = new PublicQueueHandler($id);
 
 $handler->runOnce();
index e332d856c007577e0d0a00fc1d4addccf27806fe..c644b557f0bbbb772a1d3044cf4362f7509413ab 100644 (file)
  * 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(1);
-}
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
+$helptext = <<<END_OF_SNAPSHOT_HELP
+Batch script for sending snapshot information about this installation to devs.
 
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
+END_OF_SNAPSHOT_HELP;
 
-require_once(INSTALLDIR . '/lib/common.php');
+require_once INSTALLDIR.'/scripts/commandline.inc';
 
 Snapshot::check();
index c417f741ab7f6f480665a6eac246f5051ac7c9ac..b70689f030a7a67f8ec06315a3de41a9a88eb9e5 100755 (executable)
  * 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(1);
-}
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
+$helptext = <<<END_OF_PASSWORD_HELP
+setpassword.php <username> <password>
 
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
+Sets the password of user with name <username> to <password>
 
-require_once(INSTALLDIR . '/lib/common.php');
+END_OF_PASSWORD_HELP;
 
-if ($argc != 3) {
-    print "USAGE: setpassword.php <username> <password>\n";
-    print "Sets the password of user with name <username> to <password>\n";
-    exit(1);
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+if (count($args) < 2) {
+    show_help();
 }
 
-$nickname = $argv[1];
-$password = $argv[2];
+$nickname = $args[0];
+$password = $args[1];
 
 if (mb_strlen($password) < 6) {
     print "Password must be 6 characters or more.\n";
index 450b9c30a3f6401c5a6acc630ff9a2fa40ff9397..8d03b06f5e8db8c13e00f8ee888195cc32fe3fe6 100644 (file)
@@ -1,12 +1,14 @@
 # CONFIGURATION FILE for setup_status_network.sh
 
-# Base database name; full name will include nickname
-
-export DBHOST=masterdb.example.net
+export DBHOST=localhost
+export DBHOSTNAME=masterdb.example.net
 export DBBASE=_example_net
 export USERBASE=_example_net
 export ADMIN=root
 export ADMINPASS=yourpassword
 export SITEDB=example_net_site
 export AVATARBASE=/var/www/avatar.example.net
+export BACKGROUNDBASE=/var/www/background.example.net
+export FILEBASE=/var/www/file.example.net
+export PWDGEN="pwgen 20"
 
index e1d14593fbe616c9b6fb9b3475febdfc50fc673e..17440640e429d23e9af469ce6f8bd89b5b9bd6cc 100755 (executable)
@@ -1,11 +1,11 @@
 #!/bin/bash
 
-source ./setup.cfg
+source /etc/laconica/setup.cfg
 
 export nickname=$1
 export sitename=$2
 
-export password=`pwgen 20`
+export password=`$PWDGEN`
 export database=$nickname$DBBASE
 export username=$nickname$USERBASE
 
@@ -13,7 +13,7 @@ export username=$nickname$USERBASE
 
 mysqladmin -h $DBHOST -u $ADMIN --password=$ADMINPASS create $database
 
-for f in laconica.sql sms_carrier.sql foreign_services.sql notice_source.sql; do
+for f in laconica.sql innodb.sql sms_carrier.sql foreign_services.sql notice_source.sql; do
     mysql -h $DBHOST -u $ADMIN --password=$ADMINPASS $database < ../db/$f;
 done
 
@@ -22,9 +22,11 @@ mysql -h $DBHOST -u $ADMIN --password=$ADMINPASS $SITEDB << ENDOFCOMMANDS
 GRANT INSERT,SELECT,UPDATE,DELETE ON $database.* TO '$username'@'localhost' IDENTIFIED BY '$password';
 GRANT INSERT,SELECT,UPDATE,DELETE ON $database.* TO '$username'@'%' IDENTIFIED BY '$password';
 INSERT INTO status_network (nickname, dbhost, dbuser, dbpass, dbname, sitename, created)
-VALUES ('$nickname', '$DBHOST', '$username', '$password', '$database', '$sitename', now());
+VALUES ('$nickname', '$DBHOSTNAME', '$username', '$password', '$database', '$sitename', now());
 
 ENDOFCOMMANDS
 
-mkdir $AVATARBASE/$nickname
-chmod a+w $AVATARBASE/$nickname
+for top in $AVATARBASE $FILEBASE $BACKGROUNDBASE; do
+    mkdir $top/$nickname
+    chmod a+w $top/$nickname
+done
diff --git a/scripts/showcache.php b/scripts/showcache.php
new file mode 100644 (file)
index 0000000..7a88fdb
--- /dev/null
@@ -0,0 +1,71 @@
+#!/usr/bin/env php
+<?php
+/*
+ * Laconica - a 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/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+$shortoptions = "t:c:v:k:";
+
+$helptext = <<<ENDOFHELP
+USAGE: showcache.php <args>
+shows the cached object based on the args
+
+  -t table     Table to look up
+  -c column    Column to look up, default "id"
+  -v value     Value to look up
+  -k key       Key to look up; other args are ignored
+
+ENDOFHELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+$karg = get_option_value('k');
+
+if (!empty($karg)) {
+    $k = common_cache_key($karg);
+} else {
+    $table = get_option_value('t');
+    if (empty($table)) {
+        die("No table or key specified\n");
+    }
+    $column = get_option_value('c');
+    if (empty($column)) {
+        $column = 'id';
+    }
+    $value = get_option_value('v');
+
+    $k = Memcached_DataObject::cacheKey($table, $column, $value);
+}
+
+print "Checking key '$k'...\n";
+
+$c = common_memcache();
+
+if (empty($c)) {
+    die("Can't initialize cache object!\n");
+}
+
+$obj = $c->get($k);
+
+if (empty($obj)) {
+    print "Empty.\n";
+} else {
+    var_dump($obj);
+    print "\n";
+}
index 39eb859bbad1f10e678d604cb87f08b42f6c5749..88ca2ba7aac4f56d60c7109a962e1a4b56a9ccc1 100755 (executable)
@@ -1,10 +1,37 @@
+#!/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/>.
+ */
 
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
-require_once(INSTALLDIR . '/lib/util.php');
+$shortoptions = 'f:d:u:';
+
+$helptext = <<<END_OF_SITEMAP_HELP
+Script for creating sitemaps files per http://sitemaps.org/
+
+    -f <indexfile>   Use <indexfile> as output file
+    -d <outputdir>   Use <outputdir> for new sitemaps
+    -u <outputurl>   Use <outputurl> as root for URLs
+
+END_OF_SITEMAP_HELP;
+
+require_once INSTALLDIR . '/scripts/commandline.inc';
 
 $output_paths = parse_args();
 
@@ -13,11 +40,11 @@ notices_map();
 user_map();
 index_map();
 
-# ------------------------------------------------------------------------------
-# Main functions: get data out and turn them into sitemaps
-# ------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------
+// Main functions: get data out and turn them into sitemaps
+// ------------------------------------------------------------------------------
 
-# Generate index sitemap of all other sitemaps.
+// Generate index sitemap of all other sitemaps.
 function index_map()
 {
     global $output_paths;
@@ -26,7 +53,7 @@ function index_map()
 
     foreach (glob("$output_dir*.xml") as $file_name) {
 
-        # Just the file name please.
+        // Just the file name please.
         $file_name = preg_replace("|$output_dir|", '', $file_name);
 
         $index_urls .= sitemap(
@@ -40,7 +67,7 @@ function index_map()
     write_file($output_paths['index_file'], sitemapindex($index_urls));
 }
 
-# Generate sitemap of standard site elements.
+// Generate sitemap of standard site elements.
 function standard_map()
 {
     global $output_paths;
@@ -61,7 +88,7 @@ function standard_map()
                                     )
                               );
 
-    $docs = array('about', 'faq', 'contact', 'im', 'openid', 'openmublog', 
+    $docs = array('about', 'faq', 'contact', 'im', 'openid', 'openmublog',
         'privacy', 'source', 'badge');
 
     foreach($docs as $title) {
@@ -79,7 +106,7 @@ function standard_map()
     write_file($urlset_path, urlset($standard_map_urls));
 }
 
-# Generate sitemaps of all notices.
+// Generate sitemaps of all notices.
 function notices_map()
 {
     global $output_paths;
@@ -93,14 +120,14 @@ function notices_map()
 
     while ($notices->fetch()) {
 
-        # Maximum 50,000 URLs per sitemap file.
+        // Maximum 50,000 URLs per sitemap file.
         if ($notice_count == 50000) {
             $notice_count = 0;
             $map_count++;
         }
 
-        # remote notices have an URL
-        
+        // remote notices have an URL
+
         if (!$notices->url && $notices->uri) {
             $notice = array(
                         'url'        => ($notices->uri) ? $notices->uri : common_local_url('shownotice', array('notice' => $notices->id)),
@@ -114,11 +141,11 @@ function notices_map()
         }
     }
 
-    # Make full sitemaps from the lists and save them.
+    // Make full sitemaps from the lists and save them.
     array_to_map($notice_list, 'notice');
 }
 
-# Generate sitemaps of all users.
+// Generate sitemaps of all users.
 function user_map()
 {
     global $output_paths;
@@ -132,7 +159,7 @@ function user_map()
 
     while ($users->fetch()) {
 
-        # Maximum 50,000 URLs per sitemap file.
+        // Maximum 50,000 URLs per sitemap file.
         if ($user_count == 50000) {
             $user_count = 0;
             $map_count++;
@@ -140,7 +167,7 @@ function user_map()
 
         $user_args = array('nickname' => $users->nickname);
 
-        # Define parameters for generating <url></url> elements.
+        // Define parameters for generating <url></url> elements.
         $user = array(
                       'url'        => common_local_url('showstream', $user_args),
                       'changefreq' => 'daily',
@@ -183,8 +210,8 @@ function user_map()
                       'priority'   => '0.5',
                       );
 
-        # Construct a <url></url> element for each user facet and add it
-        # to our existing list of those.
+        // Construct a <url></url> element for each user facet and add it
+        // to our existing list of those.
         $user_list[$map_count]        .= url($user);
         $user_rss_list[$map_count]    .= url($user_rss);
         $all_list[$map_count]         .= url($all);
@@ -196,9 +223,9 @@ function user_map()
         $user_count++;
     }
 
-    # Make full sitemaps from the lists and save them.
-    # Possible factoring: put all the lists into a master array, thus allowing
-    # calling with single argument (i.e., array_to_map('user')).
+    // Make full sitemaps from the lists and save them.
+    // Possible factoring: put all the lists into a master array, thus allowing
+    // calling with single argument (i.e., array_to_map('user')).
     array_to_map($user_list, 'user');
     array_to_map($user_rss_list, 'user_rss');
     array_to_map($all_list, 'all');
@@ -208,14 +235,14 @@ function user_map()
     array_to_map($foaf_list, 'foaf');
 }
 
-# ------------------------------------------------------------------------------
-# XML generation functions
-# ------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------
+// XML generation functions
+// ------------------------------------------------------------------------------
 
-# Generate a <url></url> element.
+// Generate a <url></url> element.
 function url($url_args)
 {
-    $url        = preg_replace('/&/', '&amp;', $url_args['url']); # escape ampersands for XML
+    $url        = preg_replace('/&/', '&amp;', $url_args['url']); // escape ampersands for XML
     $lastmod    = $url_args['lastmod'];
     $changefreq = $url_args['changefreq'];
     $priority   = $url_args['priority'];
@@ -246,7 +273,7 @@ function url($url_args)
 
 function sitemap($sitemap_args)
 {
-    $url        = preg_replace('/&/', '&amp;', $sitemap_args['url']); # escape ampersands for XML
+    $url        = preg_replace('/&/', '&amp;', $sitemap_args['url']); // escape ampersands for XML
     $lastmod    = $sitemap_args['lastmod'];
 
     if (is_null($url)) {
@@ -265,7 +292,7 @@ function sitemap($sitemap_args)
     return $sitemap_out;
 }
 
-# Generate a <urlset></urlset> element.
+// Generate a <urlset></urlset> element.
 function urlset($urlset_text)
 {
     $urlset = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" .
@@ -276,7 +303,7 @@ function urlset($urlset_text)
     return $urlset;
 }
 
-# Generate a <urlset></urlset> element.
+// Generate a <urlset></urlset> element.
 function sitemapindex($sitemapindex_text)
 {
     $sitemapindex = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" .
@@ -287,49 +314,31 @@ function sitemapindex($sitemapindex_text)
     return $sitemapindex;
 }
 
-# Generate a sitemap from an array containing <url></url> elements and write it to a file.
+// Generate a sitemap from an array containing <url></url> elements and write it to a file.
 function array_to_map($url_list, $filename_prefix)
 {
     global $output_paths;
 
     if ($url_list) {
-        # $map_urls is a long string containing concatenated <url></url> elements.
+        // $map_urls is a long string containing concatenated <url></url> elements.
         while (list($map_idx, $map_urls) = each($url_list)) {
             $urlset_path = $output_paths['output_dir'] . "$filename_prefix-$map_idx.xml";
-            
+
             write_file($urlset_path, urlset($map_urls));
         }
     }
 }
 
-# ------------------------------------------------------------------------------
-# Internal functions
-# ------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------
+// Internal functions
+// ------------------------------------------------------------------------------
 
-# Parse command line arguments.
+// Parse command line arguments.
 function parse_args()
 {
-    $args = getopt('f:d:u:');
-
-    if (is_null($args[f]) && is_null($args[d]) && is_null($args[u])) {
-        error('Mandatory arguments: -f <index file path> -d <output directory path> -u <URL of sitemaps directory>');
-    }
-
-    if (is_null($args[f])) {
-        error('You must specify an index file name with the -f option.');
-    }
-
-    if (is_null($args[d])) {
-        error('You must specify a directory for the output file with the -d option.');
-    }
-
-    if (is_null($args[u])) {
-        error('You must specify a URL for the directory where the sitemaps will be kept with the -u option.');
-    }
-
-    $index_file = $args[f];
-    $output_dir = $args[d];
-    $output_url = $args[u];
+    $index_file = get_option_value('f');
+    $output_dir = get_option_value('d');
+    $output_url = get_option_value('u');
 
     if (file_exists($output_dir)) {
         if (is_writable($output_dir) === false) {
@@ -348,7 +357,7 @@ function parse_args()
     return $paths;
 }
 
-# Ensure paths end with a "/".
+// Ensure paths end with a "/".
 function trailing_slash($path)
 {
     if (preg_match('/\/$/', $path) == 0) {
@@ -358,7 +367,7 @@ function trailing_slash($path)
     return $path;
 }
 
-# Write data to disk.
+// Write data to disk.
 function write_file($path, $data)
 {
     if (is_null($path)) {
@@ -376,7 +385,7 @@ function write_file($path, $data)
     }
 }
 
-# Display an error message and exit.
+// Display an error message and exit.
 function error ($error_msg)
 {
     if (is_null($error_msg)) {
index 631719aa2186aab6f58510ce4a362ee2f40b91e8..94b846d987bc22f3a6fdb7016c58ef7b73f5c24b 100755 (executable)
  * 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/mail.php');
-require_once(INSTALLDIR . '/lib/queuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_SMS_HELP
+Daemon script for pushing new notices to local subscribers using SMS.
+
+    -i --id           Identity (default none)
+
+END_OF_SMS_HELP;
 
-set_error_handler('common_error_handler');
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+require_once INSTALLDIR . '/lib/mail.php';
+require_once INSTALLDIR . '/lib/queuehandler.php';
 
 class SmsQueueHandler extends QueueHandler
 {
-    
     function transport()
     {
         return 'sms';
@@ -51,18 +52,21 @@ class SmsQueueHandler extends QueueHandler
     {
         return mail_broadcast_notice_sms($notice);
     }
-    
+
     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;
+if (have_option('i')) {
+    $id = get_option_value('i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
 $handler = new SmsQueueHandler($id);
 
index 053c4f8ee028575dd23cae44c045d676dc1c3794..9ead20acd6c0230f0b42af6a24f6cd670404c297 100755 (executable)
 # This program tries to start the daemons for Laconica.
 # Note that the 'maildaemon' needs to run as a mail filter.
 
+ARGSG=
+ARGSD=
+
+if [ $# -gt 0 ]; then
+    ARGSG="$ARGSG -s$1"
+    ID=`echo $1 | sed s/\\\\./_/g`
+    ARGSD="$ARGSD -s$1 -i$ID"
+fi
+
+if [ $# -gt 1 ]; then
+    ARGSD="$ARGSD -p$2"
+    ARGSG="$ARGSG -p$2"
+fi
+
 DIR=`dirname $0`
-DAEMONS=`php $DIR/getvaliddaemons.php`
+DAEMONS=`php $DIR/getvaliddaemons.php $ARGSG`
 
 for f in $DAEMONS; do
 
-         echo -n "Starting $f...";
-        php $DIR/$f
-        echo "DONE."
+         printf "Starting $f...";
+        php $DIR/$f $ARGSD
+        printf "DONE.\n"
+
 done
index 50155865896a8870623efb78328f2fd69d555ee5..60ffd83ad1e45d8991aa4115a3e8c305acba5ddb 100755 (executable)
@@ -24,7 +24,8 @@ SDIR=`dirname $0`
 DIR=`php $SDIR/getpiddir.php`
 
 for f in jabberhandler ombhandler publichandler smshandler pinghandler \
-        xmppconfirmhandler xmppdaemon twitterhandler facebookhandler; do
+        xmppconfirmhandler xmppdaemon twitterhandler facebookhandler \
+        twitterstatusfetcher; do
 
        FILES="$DIR/$f.*.pid"
        for ff in "$FILES" ; do
index 8a338f1b4a78a082acc8566c1b796236041a8831..fe53ff44d634fa92895dd00c34ccc804fa245295 100755 (executable)
  * 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);
 
 // Uncomment this to get useful console output
-//define('SCRIPT_DEBUG', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
+$helptext = <<<END_OF_TWITTER_HELP
+Batch script for synching local friends with Twitter friends.
+
+END_OF_TWITTER_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
 
 // Make a lockfile
 $lockfilename = lockFilename();
index 0d2eaeaf09263e0d3bed6f50110855b38c752e26..b2135d6825ee2f52eccbe481a8c56cbfa3958861 100644 (file)
  * 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(1);
-}
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
+$shortoptions = 'u::';
+$longoptions = array('start-user-id::');
 
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
+$helptext = <<<END_OF_TRIM_HELP
+Batch script for trimming notice inboxes to a reasonable size.
+
+    -u <id>
+    --start-user-id=<id>   User ID to start after. Default is all.
 
-require_once(INSTALLDIR . '/lib/common.php');
+END_OF_TRIM_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+$id = null;
+
+if (have_option('u')) {
+    $id = get_option_value('u');
+} else if (have_option('--start-user-id')) {
+    $id = get_option_value('--start-user-id');
+} else {
+    $id = null;
+}
 
 $user = new User();
-if ($argc > 1) {
-    $user->whereAdd('id > ' . $argv[1]);
+
+if (!empty($id)) {
+    $user->whereAdd('id > ' . $id);
 }
+
 $cnt = $user->find();
 
 while ($user->fetch()) {
@@ -74,10 +85,10 @@ while ($user->fetch()) {
     $delay = 3.0 * ($finish - $start);
 
     print "Delaying $delay seconds...";
-    
+
     // Wait to let slaves catch up
 
     usleep($delay * 1000000);
-    
+
     print "DONE.\n";
 }
index 6089b5c7db2a4bbd76d9c79967a432bad406fbf7..00e735d983610b99bf689c8a8bc740cbf33af1ae 100755 (executable)
  * 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/twitter.php');
-require_once(INSTALLDIR . '/lib/queuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_ENJIT_HELP
+Daemon script for pushing new notices to Twitter.
+
+    -i --id           Identity (default none)
+
+END_OF_ENJIT_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
 
-set_error_handler('common_error_handler');
+require_once INSTALLDIR . '/lib/twitter.php';
+require_once INSTALLDIR . '/lib/queuehandler.php';
 
 class TwitterQueueHandler extends QueueHandler
 {
-    
     function transport()
     {
         return 'twitter';
     }
-    
+
     function start()
     {
         $this->log(LOG_INFO, "INITIALIZE");
@@ -51,20 +52,22 @@ class TwitterQueueHandler extends QueueHandler
     {
         return broadcast_twitter($notice);
     }
-    
+
     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;
+if (have_option('i')) {
+    $id = get_option_value('i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
 $handler = new TwitterQueueHandler($id);
 
index 442435be16692eb23a9427024343a1d0392c2b19..8b10bfbadda5c5396f82c38c923da5a0526288ec 100755 (executable)
  * 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);
 
 // Tune number of processes and how often to poll Twitter
 // XXX: Should these things be in config.php?
 define('MAXCHILDREN', 2);
 define('POLL_INTERVAL', 60); // in seconds
 
-// Uncomment this to get useful logging
-define('SCRIPT_DEBUG', true);
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_TRIM_HELP
+Batch script for retrieving Twitter messages from foreign service.
+
+  -i --id      Identity (default 'generic')
+    
+END_OF_TRIM_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
 
 require_once INSTALLDIR . '/lib/common.php';
 require_once INSTALLDIR . '/lib/daemon.php';
@@ -67,7 +69,7 @@ class TwitterStatusFetcher extends Daemon
 
     function name()
     {
-        return ('twitterstatusfetcher.generic');
+        return ('twitterstatusfetcher.'.$this->_id);
     }
 
     /**
@@ -626,12 +628,18 @@ class TwitterStatusFetcher extends Daemon
     }
 }
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
 declare(ticks = 1);
 
-$fetcher = new TwitterStatusFetcher();
+if (have_option('i')) {
+    $id = get_option_value('i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
+
+$fetcher = new TwitterStatusFetcher($id);
 $fetcher->runOnce();
 
index e35ea81ea435750df35e5bac89efc018671651a9..b0b576eb44ff3a2295f479ad62fe3f5f7dc3d1ad 100644 (file)
  * 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/>.
  */
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
 
-# 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();
-}
+$helptext = <<<ENDOFHELP
+uncache_users.php <idfile>
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
+Uncache users listed in an ID file, default 'ids.txt'.
 
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
+ENDOFHELP;
 
-require_once(INSTALLDIR . '/lib/common.php');
+require_once INSTALLDIR.'/scripts/commandline.inc';
 
-$id_file = ($argc > 1) ? $argv[1] : 'ids.txt';
+$id_file = (count($args) > 1) ? $args[0] : 'ids.txt';
 
 common_log(LOG_INFO, 'Updating user inboxes.');
 
 $ids = file($id_file);
 
+$memc = common_memcache();
+
 foreach ($ids as $id) {
-       
+
        $user = User::staticGet('id', $id);
 
        if (!$user) {
@@ -51,9 +46,7 @@ foreach ($ids as $id) {
        }
 
     $user->decache();
-    
-    $memc = common_memcache();
-    
+
     $memc->delete(common_cache_key('user:notices_with_friends:'. $user->id));
     $memc->delete(common_cache_key('user:notices_with_friends:'. $user->id . ';last'));
 }
index 1fa361a36b8a300c178e0f741675f1db4311bc8e..d6821ddefae03affd7595396f14489f6a74f8571 100755 (executable)
  * 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/jabber.php');
-require_once(INSTALLDIR . '/lib/xmppqueuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_JABBER_HELP
+Daemon script for pushing new confirmations to Jabber users.
+
+    -i --id           Identity (default none)
+
+END_OF_JABBER_HELP;
 
-set_error_handler('common_error_handler');
+require_once INSTALLDIR.'/scripts/commandline.inc';
+require_once INSTALLDIR . '/lib/jabber.php';
+require_once INSTALLDIR . '/lib/xmppqueuehandler.php';
 
 define('CLAIM_TIMEOUT', 1200);
 
 class XmppConfirmHandler extends XmppQueueHandler
 {
-
     var $_id = 'confirm';
-    
+
     function class_name()
     {
         return 'XmppConfirmHandler';
     }
-    
+
     function run()
     {
         if (!$this->start()) {
@@ -147,14 +147,17 @@ if (common_config('xmpp','enabled')==false) {
     exit();
 }
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
-
-$resource = ($argc > 1) ? $argv[1] : (common_config('xmpp', 'resource').'-confirm');
+if (have_option('i')) {
+    $id = get_option_value('i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
-$handler = new XmppConfirmHandler($resource);
+$handler = new XmppConfirmHandler($id);
 
 $handler->runOnce();
 
index 528df5d2fc8423fd854f0153d45c2e1780522cac..bd1918ca98aa90b8fbe56e2cb29c75c3577bf348 100755 (executable)
  * 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/jabber.php');
-require_once(INSTALLDIR . '/lib/daemon.php');
+$shortoptions = 'fi::';
+$longoptions = array('id::', 'foreground');
+
+$helptext = <<<END_OF_XMPP_HELP
+Daemon script for receiving new notices from Jabber users.
+
+    -i --id           Identity (default none)
+    -f --foreground   Stay in the foreground (default background)
 
-set_error_handler('common_error_handler');
+END_OF_XMPP_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+require_once INSTALLDIR . '/lib/common.php';
+require_once INSTALLDIR . '/lib/jabber.php';
+require_once INSTALLDIR . '/lib/daemon.php';
 
 # This is kind of clunky; we create a class to call the global functions
 # in jabber.php, which create a new XMPP class. A more elegant (?) solution
@@ -39,9 +43,10 @@ set_error_handler('common_error_handler');
 
 class XMPPDaemon extends Daemon
 {
-
-    function XMPPDaemon($resource=null)
+    function __construct($resource=null, $daemonize=true)
     {
+        parent::__construct($daemonize);
+
         static $attrs = array('server', 'port', 'user', 'password', 'host');
 
         foreach ($attrs as $attr)
@@ -50,7 +55,7 @@ class XMPPDaemon extends Daemon
         }
 
         if ($resource) {
-            $this->resource = $resource;
+            $this->resource = $resource . 'daemon';
         } else {
             $this->resource = common_config('xmpp', 'resource') . 'daemon';
         }
@@ -60,7 +65,6 @@ class XMPPDaemon extends Daemon
 
     function connect()
     {
-
         $connect_to = ($this->host) ? $this->host : $this->server;
 
         $this->log(LOG_INFO, "Connecting to $connect_to on port $this->port");
@@ -71,10 +75,17 @@ class XMPPDaemon extends Daemon
             return false;
         }
 
+        $this->log(LOG_INFO, "Connected");
+
         $this->conn->setReconnectTimeout(600);
 
+        $this->log(LOG_INFO, "Sending initial presence.");
+
         jabber_send_presence("Send me a message to post a notice", 'available',
                              null, 'available', 100);
+
+        $this->log(LOG_INFO, "Done connecting.");
+
         return !$this->conn->isDisconnected();
     }
 
@@ -87,18 +98,29 @@ class XMPPDaemon extends Daemon
     {
         if ($this->connect()) {
 
+            $this->log(LOG_DEBUG, "Initializing stanza handlers.");
+
             $this->conn->addEventHandler('message', 'handle_message', $this);
             $this->conn->addEventHandler('presence', 'handle_presence', $this);
             $this->conn->addEventHandler('reconnect', 'handle_reconnect', $this);
 
+            $this->log(LOG_DEBUG, "Beginning processing loop.");
+
             $this->conn->process();
         }
     }
 
     function handle_reconnect(&$pl)
     {
+        $this->log(LOG_DEBUG, "Got reconnection callback.");
         $this->conn->processUntil('session_start');
+        $this->log(LOG_DEBUG, "Sending reconnection presence.");
         $this->conn->presence('Send me a message to post a notice', 'available', null, 'available', 100);
+        unset($pl['xml']);
+        $pl['xml'] = null;
+
+        $pl = null;
+        unset($pl);
     }
 
     function get_user($from)
@@ -109,21 +131,27 @@ class XMPPDaemon extends Daemon
 
     function handle_message(&$pl)
     {
+        $from = jabber_normalize_jid($pl['from']);
+
         if ($pl['type'] != 'chat') {
+            $this->log(LOG_WARNING, "Ignoring message of type ".$pl['type']." from $from.");
             return;
         }
+
         if (mb_strlen($pl['body']) == 0) {
+            $this->log(LOG_WARNING, "Ignoring message with empty body from $from.");
             return;
         }
 
-        $from = jabber_normalize_jid($pl['from']);
-
         # Forwarded from another daemon (probably a broadcaster) for
         # us to handle
 
         if ($this->is_self($from)) {
+            $this->log(LOG_INFO, "Got forwarded notice from self ($from).");
             $from = $this->get_ofrom($pl);
+            $this->log(LOG_INFO, "Originally sent by $from.");
             if (is_null($from) || $this->is_self($from)) {
+                $this->log(LOG_INFO, "Ignoring notice originally sent by $from.");
                 return;
             }
         }
@@ -138,6 +166,7 @@ class XMPPDaemon extends Daemon
             return;
         }
         if ($this->handle_command($user, $pl['body'])) {
+            $this->log(LOG_INFO, "Command messag by $from handled.");
             return;
         } else if ($this->is_autoreply($pl['body'])) {
             $this->log(LOG_INFO, 'Ignoring auto reply from ' . $from);
@@ -146,17 +175,31 @@ class XMPPDaemon extends Daemon
             $this->log(LOG_INFO, 'Ignoring OTR from ' . $from);
             return;
         } else if ($this->is_direct($pl['body'])) {
+            $this->log(LOG_INFO, 'Got a direct message ' . $from);
+
             preg_match_all('/d[\ ]*([a-z0-9]{1,64})/', $pl['body'], $to);
 
             $to = preg_replace('/^d([\ ])*/', '', $to[0][0]);
             $body = preg_replace('/d[\ ]*('. $to .')[\ ]*/', '', $pl['body']);
+
+            $this->log(LOG_INFO, 'Direct message from '. $user->nickname . ' to ' . $to);
+
             $this->add_direct($user, $body, $to, $from);
         } else {
+
+            $this->log(LOG_INFO, 'Posting a notice from ' . $user->nickname);
+
             $this->add_notice($user, $pl);
         }
 
         $user->free();
         unset($user);
+
+        unset($pl['xml']);
+        $pl['xml'] = null;
+
+        $pl = null;
+        unset($pl);
     }
 
     function is_self($from)
@@ -259,6 +302,7 @@ class XMPPDaemon extends Daemon
         $notice = Notice::saveNew($user->id, $content_shortened, 'xmpp');
         if (is_string($notice)) {
             $this->log(LOG_ERR, $notice);
+            $this->from_site($user->jabber, $notice);
             return;
         }
         common_broadcast_notice($notice);
@@ -301,11 +345,23 @@ class XMPPDaemon extends Daemon
             }
             break;
         }
+        unset($pl['xml']);
+        $pl['xml'] = null;
+
+        $pl = null;
+        unset($pl);
     }
 
     function log($level, $msg)
     {
-        common_log($level, 'XMPPDaemon('.$this->resource.'): '.$msg);
+        $text = 'XMPPDaemon('.$this->resource.'): '.$msg;
+        common_log($level, $text);
+        if (!$this->daemonize)
+        {
+            $line = common_log_line($level, $text);
+            echo $line;
+            echo "\n";
+        }
     }
 
     function subscribed($to)
@@ -321,13 +377,16 @@ if (common_config('xmpp','enabled')==false) {
     exit();
 }
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
+if (have_option('i', 'id')) {
+    $id = get_option_value('i', 'id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
-$resource = ($argc > 1) ? $argv[1] : (common_config('xmpp','resource') . '-listen');
+$foreground = have_option('f', 'foreground');
 
-$daemon = new XMPPDaemon($resource);
+$daemon = new XMPPDaemon($id, !$foreground);
 
 $daemon->runOnce();
index b79adf15c0e08140a2aac575aaa699ab582d380d..8204b9db6c61708aeadb86d7bcf05284a6101188 100644 (file)
@@ -4,68 +4,68 @@
 
 source src1
 {
-       type                                    = mysql
-       sql_host                                = localhost
-       sql_user                                = USERNAME
-       sql_pass                                = PASSWORD
-       sql_db                                  = identi_ca
-       sql_port                                = 3306
-       sql_query                               = SELECT id, UNIX_TIMESTAMP(created) as created_ts, nickname, fullname, location, bio, homepage FROM profile
-       sql_query_info                  = SELECT * FROM profile where id = $id
-       sql_attr_timestamp              = created_ts
+    type                    = mysql
+    sql_host                = localhost
+    sql_user                = USERNAME
+    sql_pass                = PASSWORD
+    sql_db                  = identi_ca
+    sql_port                = 3306
+    sql_query               = SELECT id, UNIX_TIMESTAMP(created) as created_ts, nickname, fullname, location, bio, homepage FROM profile
+    sql_query_info          = SELECT * FROM profile where id = $id
+    sql_attr_timestamp      = created_ts
 }
 
 
 source src2
 {
-       type                                    = mysql
-       sql_host                                = localhost
-       sql_user                                = USERNAME
-       sql_pass                                = PASSWORD
-       sql_db                                  = identi_ca
-       sql_port                                = 3306
-       sql_query                               = SELECT id, UNIX_TIMESTAMP(created) as created_ts, content FROM notice
-       sql_query_info                  = SELECT * FROM notice where id = $id
-       sql_attr_timestamp              = created_ts
+    type                    = mysql
+    sql_host                = localhost
+    sql_user                = USERNAME
+    sql_pass                = PASSWORD
+    sql_db                  = identi_ca
+    sql_port                = 3306
+    sql_query               = SELECT id, UNIX_TIMESTAMP(created) as created_ts, content FROM notice
+    sql_query_info          = SELECT * FROM notice where notice.id = $id AND notice.is_local != -2
+    sql_attr_timestamp      = created_ts
 }
 
 index identica_notices
 {
-       source                                  = src2
-       path                                    = DIRECTORY/data/identica_notices
-       docinfo                                 = extern
-       charset_type                    = utf-8
-       min_word_len                    = 3
-       stopwords                               = DIRECTORY/data/stopwords-en.txt
+    source                  = src2
+    path                    = DIRECTORY/data/identica_notices
+    docinfo                 = extern
+    charset_type            = utf-8
+    min_word_len            = 3
+    stopwords               = DIRECTORY/data/stopwords-en.txt
 }
 
 
 index identica_people
 {
-       source                                  = src1
-       path                                    = DIRECTORY/data/identica_people
-       docinfo                                 = extern
-       charset_type                    = utf-8
-       min_word_len                    = 3
-       stopwords                               = DIRECTORY/data/stopwords-en.txt
+    source                  = src1
+    path                    = DIRECTORY/data/identica_people
+    docinfo                 = extern
+    charset_type            = utf-8
+    min_word_len            = 3
+    stopwords               = DIRECTORY/data/stopwords-en.txt
 }
 
 indexer
 {
-       mem_limit                               = 32M
+    mem_limit               = 32M
 }
 
 searchd
 {
-       port                                    = 3312
-       log                                             = DIRECTORY/log/searchd.log
-       query_log                               = DIRECTORY/log/query.log
-       read_timeout                    = 5
-       max_children                    = 30
-       pid_file                                = DIRECTORY/log/searchd.pid
-       max_matches                             = 1000
-       seamless_rotate                 = 1
-       preopen_indexes                 = 0
-       unlink_old                              = 1
+    port                    = 3312
+    log                     = DIRECTORY/log/searchd.log
+    query_log               = DIRECTORY/log/query.log
+    read_timeout            = 5
+    max_children            = 30
+    pid_file                = DIRECTORY/log/searchd.pid
+    max_matches             = 1000
+    seamless_rotate         = 1
+    preopen_indexes         = 0
+    unlink_old              = 1
 }
 
index 8957a5b40142e071a0aa3a41dc6cb590fc8c6341..e9f4beaae355318c50e4e75230cc8ed8e68d7b56 100644 (file)
@@ -206,7 +206,10 @@ border-radius:4px;
 padding:0 7px;
 }
 
-
+.form_settings input.form_action-default {
+margin-right:11px;
+}
+.form_settings input.form_action-default,
 .form_settings input.form_action-primary {
 padding:0;
 }
@@ -270,10 +273,9 @@ clear:both;
 margin-bottom:18px;
 }
 
-
 #anon_notice {
 float:left;
-width:43.2%;
+width:42.4%;
 padding:1.1%;
 border-radius:7px;
 -moz-border-radius:7px;
@@ -285,7 +287,6 @@ font-size:1.1em;
 font-weight:bold;
 }
 
-
 #footer {
 float:left;
 width:64%;
@@ -395,7 +396,7 @@ margin-bottom:1em;
 }
 
 #content {
-width:64.009%;
+width:63.311%;
 min-height:259px;
 padding:1.795%;
 float:left;
@@ -421,7 +422,7 @@ float:left;
 width:27.917%;
 min-height:259px;
 float:left;
-margin-left:0.5%;
+margin-left:0.699%;
 padding:1.795%;
 border-radius:7px;
 -moz-border-radius:7px;
@@ -431,7 +432,7 @@ border-style:solid;
 }
 
 #form_notice {
-width:45.664%;
+width:45%;
 float:left;
 position:relative;
 line-height:1;
@@ -470,12 +471,12 @@ cursor:pointer;
 }
 #form_notice label[for=notice_data-attach] {
 text-indent:-9999px;
-left:394px;
+left:86%;
 width:16px;
 height:16px;
 }
 #form_notice #notice_data-attach {
-left:183px;
+left:40.6%;
 padding:0;
 height:16px;
 }
@@ -510,13 +511,26 @@ margin-bottom:7px;
 margin-left:18px;
 float:left;
 }
-#form_notice .error {
+#form_notice .error,
+#form_notice .success {
 float:left;
 clear:both;
-width:96.9%;
+width:81.5%;
 margin-bottom:0;
 line-height:1.618;
 }
+#form_notice #notice_data-attach_selected code {
+float:left;
+width:90%;
+display:block;
+font-size:1.1em;
+line-height:1.8;
+overflow:auto;
+}
+#form_notice #notice_data-attach_selected button {
+float:right;
+font-size:0.8em;
+}
 
 /* entity_profile */
 .entity_profile {
@@ -548,7 +562,8 @@ margin-bottom:18px;
 .entity_profile .entity_location,
 .entity_profile .entity_url,
 .entity_profile .entity_note,
-.entity_profile .entity_tags {
+.entity_profile .entity_tags,
+.entity_profile .entity_aliases {
 margin-left:113px;
 margin-bottom:4px;
 }
@@ -574,16 +589,16 @@ font-weight:normal;
 content: ")";
 font-weight:normal;
 }
-
-.entity_profile dt {
-display:none;
-}
+.entity_profile dt,
 .entity_profile h2 {
 display:none;
 }
+.entity_profile .role {
+margin-left:11px;
+font-style:italic;
+}
 /* entity_profile */
 
-
 /*entity_actions*/
 .entity_actions {
 float:right;
@@ -712,7 +727,6 @@ margin-bottom:0;
 min-height:60px;
 }
 
-
 .profile .form_group_join legend,
 .profile .form_group_leave legend,
 .profile .form_user_subscribe legend,
@@ -747,13 +761,11 @@ display:inline;
 margin-right:11px;
 }
 
-
 .profile .entity_profile .form_subscription_edit label {
 font-weight:normal;
 margin-right:11px;
 }
 
-
 /* NOTICE */
 .notice,
 .profile {
@@ -771,12 +783,11 @@ list-style-type:none;
 }
 .notices .notices {
 margin-top:7px;
-margin-left:5%;
-width:95%;
+margin-left:2%;
+width:98%;
 float:left;
 }
 
-
 /* NOTICES */
 #notices_primary {
 float:left;
@@ -948,7 +959,6 @@ border:0;
 padding:0;
 }
 
-
 .notice .attachment {
 position:relative;
 padding-left:16px;
@@ -1017,7 +1027,14 @@ border-radius:7px;
 -moz-border-radius:7px;
 -webkit-border-radius:7px;
 }
-
+#jOverlayLoading {
+top:22.5%;
+left:40%;
+}
+#attachment_view img {
+max-width:480px;
+max-height:480px;
+}
 #attachment_view #oembed_info {
 margin-top:11px;
 }
@@ -1045,7 +1062,6 @@ margin-bottom:18px;
 padding-left:20px;
 }
 
-
 #filter_tags {
 margin-bottom:11px;
 float:left;
@@ -1091,8 +1107,6 @@ top:3px;
 left:3px;
 }
 
-
-
 .pagination {
 float:left;
 clear:both;
@@ -1138,7 +1152,6 @@ padding-right:30px;
 }
 /* END: NOTICE */
 
-
 .hentry .entry-content p {
 margin-bottom:18px;
 }
@@ -1155,7 +1168,6 @@ margin-bottom:18px;
 margin-left:18px;
 }
 
-
 /* TOP_POSTERS */
 .section tbody td {
 padding-right:18px;
@@ -1183,7 +1195,6 @@ margin-right:0;
 display:none;
 }
 
-
 /* tagcloud */
 .tag-cloud {
 list-style-type:none;
@@ -1266,6 +1277,11 @@ clear:both;
 margin-bottom:0;
 }
 
+#form_settings_design #settings_design_background-image img {
+max-width:480px;
+max-height:480px;
+}
+
 #form_settings_design #settings_design_color .form_data,
 #form_settings_design #color-picker {
 float:left;
index da200388eb57d8c704005f86123c0e7aab5f8458..3e128b84ed607f38df446a4c88f273edc412bcb9 100644 (file)
@@ -9,7 +9,7 @@ width:78%;
 #form_notice .form_note + label {
 position:absolute;
 top:25px;
-left:380px;
+left:83%;
 text-indent:-9999px;
 height:16px;
 width:16px;
@@ -19,10 +19,12 @@ display:block;
 width:17%;
 max-width:17%;
 }
-#anon_notice {
-max-width:39%;
+#form_notice #notice_data-attach_selected {
+width:78.5%;
+}
+#form_notice #notice_data-attach_selected button {
+padding:0 4px;
 }
-
 .notice-options input.submit {
 font-size:0;
 margin-top:3px;
@@ -43,6 +45,6 @@ z-index:1;
 .notice:hover {
 z-index:9999;
 }
-.notice .thumbnail img  {
+.notice .thumbnail img {
 z-index:9999;
-}
\ No newline at end of file
+}
index 76a82c0042d32841784b46fd0bb50d340ddd16d8..dde4d6fc77081906566f6f7ec0cebea44c30dacc 100644 (file)
@@ -5,6 +5,12 @@ margin-left:7px;
 address .fn {
 display:none;
 }
+
+#wrap {
+width:1003px;
+margin:0 auto;
+}
+
 #content {
 width:70%;
 }
@@ -26,5 +32,6 @@ margin-bottom:123px;
 width:20%;
 }
 .notice div.entry-content {
-width:63%;
+width:50%;
+margin-left:30px;
 }
index 7e8b84b4cc231c6b62b5585b1695bdc3373573b5..251d6706bee4de44330563b2b1279f4baf484ce7 100644 (file)
@@ -11,7 +11,7 @@
 
 body,
 a:active {
-background-color:#C3D6DF;
+background-color:#CEE1E9;
 }
 body {
 font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
@@ -29,7 +29,7 @@ input, textarea, select,
 border-color:#AAAAAA;
 }
 #filter_tags ul li {
-border-color:#C3D6DF;
+border-color:#DDDDDD;
 }
 
 .form_settings input.form_action-primary {
@@ -40,12 +40,12 @@ input.submit,
 #form_notice.warning #notice_text-count,
 .form_settings .form_note,
 .entity_remote_subscribe {
-background-color:#A9BF4F;
+background-color:#9BB43E;
 }
 
 input:focus, textarea:focus, select:focus,
 #form_notice.warning #notice_data-text {
-border-color:#A9BF4F;
+border-color:#9BB43E;
 box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
 -moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
 -webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
@@ -71,14 +71,14 @@ color:#002E6E;
 
 .notice,
 .profile {
-border-top-color:#D1D9E4;
+border-top-color:#C8D1D5;
 }
 .section .profile {
-border-top-color:#C3D6DF;
+border-top-color:#87B4C8;
 }
 
 #aside_primary {
-background-color:#CEE1E9;
+background-color:#C8D1D5;
 }
 
 #notice_text-count {
@@ -115,12 +115,14 @@ border-color:transparent;
 background-color:#FFFFFF;
 }
 
-#site_nav_local_views a {
-background-color:rgba(194, 194, 194, 0.5);
+#site_nav_local_views li {
 box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
 -moz-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
 -webkit-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
 }
+#site_nav_local_views a {
+background-color:rgba(194, 194, 194, 0.5);
+}
 #site_nav_local_views a:hover {
 background-color:rgba(255, 255, 255, 0.7);
 }
@@ -136,13 +138,13 @@ background-color:#EFF3DC;
 }
 
 #anon_notice {
-background-color:#C3D6DF;
+background-color:#87B4C8;
 color:#FFFFFF;
 border-color:#FFFFFF;
 }
 
 #showstream #anon_notice {
-background-color:#A9BF4F;
+background-color:#9BB43E;
 }
 
 #export_data li a {
@@ -176,13 +178,13 @@ background-color:transparent;
 .form_group_leave input.submit
 .form_user_subscribe input.submit,
 .form_user_unsubscribe input.submit {
-background-color:#A9BF4F;
+background-color:#9BB43E;
 color:#FFFFFF;
 }
 .form_user_unsubscribe input.submit,
 .form_group_leave input.submit,
 .form_user_authorization input.reject {
-background-color:#C3D6DF;
+background-color:#87B4C8;
 }
 
 .entity_edit a {
@@ -245,7 +247,7 @@ div.notice-options input {
 font-family:sans-serif;
 }
 #content .notices li:hover {
-background-color:#FCFCFC;
+background-color:rgba(240, 240, 240, 0.2);
 }
 #conversation .notices li:hover {
 background-color:transparent;
@@ -272,7 +274,7 @@ background:transparent url(../../base/images/icons/twotone/green/news.gif) no-re
 .pagination .nav_prev a,
 .pagination .nav_next a {
 background-repeat:no-repeat;
-border-color:#D1D9E4;
+border-color:#C8D1D5;
 }
 .pagination .nav_prev a {
 background-image:url(../../base/images/icons/twotone/green/arrow-left.gif);
index 6501f4e48e9451d6ca5cac962a2a0fc6b4af6372..cbbd49ce6ca6a87a7d928834d158d764ad1a55af 100644 (file)
@@ -1,14 +1,14 @@
 /* IE specific styles */
 
 .notice-options input.submit {
-color:#fff;
+color:#FFFFFF;
 }
 #site_nav_local_views a {
-background-color:#ACCCDA;
+background-color:#C8D1D5;
 }
 #form_notice .form_note + label {
 background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no-repeat 0 45%;
 }
 #form_notice #notice_data-attach {
 filter: alpha(opacity=0);
-}
\ No newline at end of file
+}
index 09ad4c9724a2f072dc540ce18cc6f686f6062f04..42a4573a741af070a7ff4452969ab57e77ae75de 100644 (file)
@@ -115,12 +115,14 @@ border-color:transparent;
 background-color:#FFFFFF;
 }
 
-#site_nav_local_views a {
-background-color:rgba(194, 194, 194, 0.5);
+#site_nav_local_views li {
 box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
 -moz-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
 -webkit-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
 }
+#site_nav_local_views a {
+background-color:rgba(194, 194, 194, 0.5);
+}
 #site_nav_local_views a:hover {
 background-color:rgba(255, 255, 255, 0.7);
 }
@@ -245,7 +247,7 @@ div.notice-options input {
 font-family:sans-serif;
 }
 #content .notices li:hover {
-background-color:#FCFCFC;
+background-color:rgba(240, 240, 240, 0.2);
 }
 #conversation .notices li:hover {
 background-color:transparent;
index 69db16aad080bb1a0dc37386e1f184a6ec68c32b..97cabc30a534a505d2b1ea92c6407aedd087493a 100644 (file)
@@ -1,14 +1,14 @@
 /* IE specific styles */
 
 .notice-options input.submit {
-color:#fff;
+color:#FFFFFF;
 }
 #site_nav_local_views a {
-background-color:#D0DFE7;
+background-color:#D9DADB;
 }
 #form_notice .form_note + label {
 background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no-repeat 0 45%;
 }
 #form_notice #notice_data-attach {
 filter: alpha(opacity=0);
-}
\ No newline at end of file
+}