]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch '0.8.x' into queuemanager
authorEvan Prodromou <evan@controlyourself.ca>
Thu, 2 Jul 2009 12:51:10 +0000 (08:51 -0400)
committerEvan Prodromou <evan@controlyourself.ca>
Thu, 2 Jul 2009 12:51:10 +0000 (08:51 -0400)
55 files changed:
README
actions/attachment_ajax.php
actions/attachment_thumbnail.php
actions/conversation.php
actions/disfavor.php
actions/favor.php
actions/groupdesignsettings.php
actions/groupmembers.php
actions/grouprss.php
actions/groups.php
actions/invite.php
actions/noticesearchrss.php
actions/public.php
actions/showfavorites.php
actions/showgroup.php
actions/shownotice.php
actions/sup.php
actions/twitapisearchatom.php
actions/twitapisearchjson.php
actions/userdesignsettings.php
classes/Notice.php
classes/Status_network.php
classes/User_group.php
config.php.sample
extlib/Mail/mimeDecode.php [new file with mode: 0644]
extlib/XMPPHP/BOSH.php
extlib/XMPPHP/XMLStream.php
extlib/XMPPHP/XMPP.php
install.php
js/jquery.joverlay.js [new file with mode: 0644]
js/jquery.joverlay.min.js
js/util.js
lib/Shorturl_api.php
lib/action.php
lib/common.php
lib/daemon.php
lib/designsettings.php
lib/facebookaction.php
lib/groupeditform.php
lib/jsonsearchresultslist.php
lib/language.php
lib/profileaction.php
lib/profileminilist.php
lib/queuehandler.php
lib/subgroupnav.php
lib/util.php
plugins/FBConnect/FBConnectAuth.php
plugins/FBConnect/FBConnectPlugin.css
plugins/FBConnect/FBConnectPlugin.php
scripts/commandline.inc
scripts/fixup_conversations.php
scripts/showcache.php [new file with mode: 0644]
scripts/twitterstatusfetcher.php
scripts/xmppdaemon.php
theme/base/css/display.css

diff --git a/README b/README
index c8c529ed86d2b22cc72cec4a503fa565c69f93a1..0f1b5a43b485e17445599fea76b126e96503acdb 100644 (file)
--- a/README
+++ b/README
@@ -906,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
 --
@@ -1081,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
 ---
 
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 654a670f54ca6113ddcc31ad34503c29cfdf1791..cd6f263294270466328f28f283675f38c2432016 100644 (file)
@@ -107,17 +107,11 @@ class ConversationAction extends Action
 
     function showContent()
     {
-        $offset = ($this->page-1) * NOTICES_PER_PAGE;
-        $limit  = NOTICES_PER_PAGE + 1;
-
-        $notices = Notice::conversationStream($this->id, $offset, $limit);
+        $notices = Notice::conversationStream($this->id, 0, 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));
     }
 }
 
@@ -146,8 +140,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();
 
@@ -169,18 +180,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 79c192ac466bceeb9b0988a67ade4a9ae2ab7aad..6c1c052cba10b130abeea09b54d283acb1539149 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;
 
@@ -273,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__);
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 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 9851285c4821367288b1bd8c5797d3a4912ca840..ef9ef0d1ab0dc4ea451d29f1c1a016359533501c 100644 (file)
@@ -182,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 b723924a5e0a323209f0daf09888933841b0b9b6..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;
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 0d89af5acc40b81849ee4d4fb2b7ca80fdbe53fd..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));
     }
 
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 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 6e745e96f8dd9614da4743822a95bf1937062500..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)) {
@@ -184,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__);
@@ -203,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 502cc57b81812cc5ed29d89b255c22012d90118a..2ba2f31b1a62f47202f45caece88121b9f0a14e3 100644 (file)
@@ -795,7 +795,7 @@ class Notice extends Memcached_DataObject
         $notice->selectAdd(); // clears it
         $notice->selectAdd('id');
 
-        $notice->whereAdd('conversation = '.$id);
+        $notice->conversation = $id;
 
         $notice->orderBy('id DESC');
 
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 9b4b01ead7424f60ba7bae7db2df4152ee2ab335..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 =
index d42bac9a6920be6fd97332b5553f364f8b96fd76..a23b41b3192cec813849f0608828ad94fd28b7af 100644 (file)
@@ -86,6 +86,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/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
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 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 e7c54b74accb245d8397105efe30cd3af19ecb67..638104c1c58045842c653292fb8a0784b4384972 100644 (file)
@@ -272,21 +272,23 @@ 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%'}
     };
 
     $('#content .notice a.attachment').click(function() {
-        $().jOverlay({url: $('address .url')[0].href+'/attachment/' + ($(this).attr('id').substring('attachment'.length + 1)) + '/ajax'});
+        $().jOverlay({url: $('address .url')[0].href+'attachment/' + ($(this).attr('id').substring('attachment'.length + 1)) + '/ajax'});
         return false;
     });
-    
+
     var t;
     $("body:not(#shownotice) #content .notice a.thumbnail").hover(
         function() {
@@ -296,7 +298,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);
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 89a8c8f4d3aa1c5af24440a528f865db5287c6f9..3bfa6ba15bd78d4b0818d457ded8635effe9e0c8 100644 (file)
@@ -247,7 +247,6 @@ class Action extends HTMLOutputter // lawsuit
                                                'src' => common_path('js/jquery.joverlay.min.js')),
                                ' ');
 
-
                 Event::handle('EndShowJQueryScripts', array($this));
             }
             if (Event::handle('StartShowLaconicaScripts', array($this))) {
@@ -422,11 +421,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');
             }
@@ -964,12 +965,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 e2936f0751d245a1179b5028d9b2ef25c84c56f8..5d451463b9ed9f4e794f67bc980becf80ea60fb8 100644 (file)
@@ -120,6 +120,7 @@ $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
@@ -175,6 +176,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',
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 9650679ac5b620c51f356c1d57d07e3235abfbac..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)));
@@ -136,7 +159,7 @@ class DesignSettingsAction extends AccountSettingsAction
             $this->elementStart('li');
             $this->checkbox('design_background-image_repeat',
                             _('Tile background image'),
-                            ($design->disposition & BACKGROUND_TILE) ? true : false );
+                            ($design->disposition & BACKGROUND_TILE) ? true : false);
             $this->elementEnd('li');
         }
 
@@ -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,7 +419,14 @@ 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
@@ -403,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 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 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 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 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 045432ae52b6f61c970a6f8632687c834d02de43..ddb47a28e947478a97a3ca988ffa81583459d787 100644 (file)
@@ -31,8 +31,10 @@ 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);
         }
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 656374516182ba15d5faacec5a5860b19fad78f1..86a0316ea6c8a5def9d74652b1a5b34fd803fb13 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),
@@ -145,7 +145,6 @@ function common_ensure_session()
     }
     if (!common_have_session()) {
         if (common_config('sessions', 'handle')) {
-            common_log(LOG_INFO, "Using our own session handler");
             Session::setSaveHandler();
         }
         @session_start();
@@ -500,17 +499,19 @@ 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' => $longurl, 'rel' => 'external');
+    $attrs = array('href' => $canon, 'rel' => 'external');
 
     $is_attachment = false;
     $attachment_id = null;
@@ -528,13 +529,13 @@ function common_linkify($url) {
         }
     }
 
-// 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;
@@ -564,7 +565,7 @@ function common_linkify($url) {
         $attrs['id'] = "attachment-{$attachment_id}";
     }
 
-    return XMLStringer::estring('a', $attrs, $display);
+    return XMLStringer::estring('a', $attrs, $url);
 }
 
 function common_shorten_links($text)
@@ -982,15 +983,20 @@ function common_ensure_syslog()
     }
 }
 
+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);
         }
@@ -1221,18 +1227,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
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;
+
+    }
+
 }
index 4a7757fb98f13e43c47bf55cdd0841da848304fd..3b6ef60987185560c280d7554a4d609e41d49ebd 100644 (file)
@@ -63,14 +63,21 @@ if (isset($longoptions)) {
 
 $parser = new Console_Getopt();
 
-list($options, $args) = $parser->getopt($argv, $shortoptions, $longoptions);
+$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:
+      General options:
 
     -q --quiet           Quiet (little output)
     -v --verbose         Verbose (lots of output)
@@ -80,11 +87,11 @@ General options:
     -h --help            Show this message and quit.
 
 END_OF_DEFAULT;
-        if (isset($helptext)) {
-            print $helptext;
-        }
-        print $_default_help_text;
-        exit(0);
+    if (isset($helptext)) {
+        print $helptext;
+    }
+    print $_default_help_text;
+    exit(0);
 }
 
 foreach ($options as $option) {
@@ -115,24 +122,53 @@ require_once INSTALLDIR . '/lib/common.php';
 
 set_error_handler('common_error_handler');
 
-function have_option($str)
+function _make_matches($opt, $alt)
 {
-   global $options;
-   foreach ($options as $option) {
-       if ($option[0] == $str) {
-          return true;
-       }
-   }
-   return false;
+    $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 get_option_value($str)
+function have_option($opt, $alt=null)
 {
-   global $options;
-   foreach ($options as $option) {
-       if ($option[0] == $str) {
-          return $option[1];
-       }
-   }
-   return null;
-}
\ No newline at end of file
+    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 2cfa422e659f859806c7cdea78fba016963062ad..0be0b4bac583df8f06d2ceadd0a6f2f947316fc6 100755 (executable)
@@ -24,22 +24,17 @@ require_once INSTALLDIR.'/scripts/commandline.inc';
 
 common_log(LOG_INFO, 'Fixing up conversations.');
 
-$notice = new Notice();
-$notice->whereAdd('conversation is null');
-$notice->orderBy('id');
+$nid = new Notice();
+$nid->query('select id, reply_to from notice where conversation is null');
 
-$cnt = $notice->find();
+while ($nid->fetch()) {
 
-print "Found $cnt notices.\n";
-
-while ($notice->fetch()) {
-
-    print "$notice->id =>";
-
-    $orig = clone($notice);
-
-    if (empty($notice->reply_to)) {
-        $notice->conversation = $notice->id;
+    $cid = null;
+    
+    $notice = new Notice();
+    
+    if (empty($nid->reply_to)) {
+        $cid = $nid->id;
     } else {
         $reply = Notice::staticGet('id', $notice->reply_to);
 
@@ -52,6 +47,9 @@ while ($notice->fetch()) {
         } else {
             $notice->conversation = $reply->conversation;
         }
+       
+       unset($reply);
+       $reply = null;
     }
 
     print "$notice->conversation";
@@ -63,5 +61,10 @@ while ($notice->fetch()) {
         continue;
     }
 
+    $notice = null;
+    $orig = null;
+    unset($notice);
+    unset($orig);
+    
     print ".\n";
 }
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 5ffdda58fafb8fdcd2ad643bfa1ca4bd918d1a3c..8b10bfbadda5c5396f82c38c923da5a0526288ec 100755 (executable)
@@ -25,9 +25,14 @@ define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
 define('MAXCHILDREN', 2);
 define('POLL_INTERVAL', 60); // in seconds
 
+$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';
@@ -64,7 +69,7 @@ class TwitterStatusFetcher extends Daemon
 
     function name()
     {
-        return ('twitterstatusfetcher.generic');
+        return ('twitterstatusfetcher.'.$this->_id);
     }
 
     /**
@@ -625,6 +630,16 @@ class TwitterStatusFetcher extends Daemon
 
 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 3eecfec29a13725aa46684428b30dc1d301b3052..ca62181203a6952167e33e4c690318e59c582d42 100755 (executable)
 
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
 
-$shortoptions = 'i::';
-$longoptions = array('id::');
+$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)
 
 END_OF_XMPP_HELP;
 
@@ -42,8 +43,10 @@ require_once INSTALLDIR . '/lib/daemon.php';
 
 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)
@@ -62,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");
@@ -73,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();
     }
 
@@ -89,17 +98,23 @@ 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);
     }
 
@@ -111,21 +126,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;
             }
         }
@@ -140,6 +161,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);
@@ -148,12 +170,20 @@ 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);
         }
 
@@ -261,6 +291,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);
@@ -307,7 +338,14 @@ class XMPPDaemon extends Daemon
 
     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)
@@ -323,16 +361,16 @@ if (common_config('xmpp','enabled')==false) {
     exit();
 }
 
-if (have_option('i')) {
-    $id = get_option_value('i');
-} else if (have_option('--id')) {
-    $id = get_option_value('--id');
+if (have_option('i', 'id')) {
+    $id = get_option_value('i', 'id');
 } else if (count($args) > 0) {
     $id = $args[0];
 } else {
     $id = null;
 }
 
-$daemon = new XMPPDaemon($id);
+$foreground = have_option('f', 'foreground');
+
+$daemon = new XMPPDaemon($id, !$foreground);
 
 $daemon->runOnce();
index 78fcd7ecefcca0187e3a620f6dcd6eb8c4cf4d91..f2b200376d0875d74eb9ead74698df487afd0f93 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,7 +273,6 @@ clear:both;
 margin-bottom:18px;
 }
 
-
 #anon_notice {
 float:left;
 width:43.2%;
@@ -285,7 +287,6 @@ font-size:1.1em;
 font-weight:bold;
 }
 
-
 #footer {
 float:left;
 width:64%;
@@ -588,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;
@@ -726,7 +727,6 @@ margin-bottom:0;
 min-height:60px;
 }
 
-
 .profile .form_group_join legend,
 .profile .form_group_leave legend,
 .profile .form_user_subscribe legend,
@@ -761,13 +761,11 @@ display:inline;
 margin-right:11px;
 }
 
-
 .profile .entity_profile .form_subscription_edit label {
 font-weight:normal;
 margin-right:11px;
 }
 
-
 /* NOTICE */
 .notice,
 .profile {
@@ -790,7 +788,6 @@ width:95%;
 float:left;
 }
 
-
 /* NOTICES */
 #notices_primary {
 float:left;
@@ -962,7 +959,6 @@ border:0;
 padding:0;
 }
 
-
 .notice .attachment {
 position:relative;
 padding-left:16px;
@@ -1031,7 +1027,13 @@ border-radius:7px;
 -moz-border-radius:7px;
 -webkit-border-radius:7px;
 }
-
+#jOverlayContent #content img {
+max-width:480px;
+}
+#jOverlayLoading {
+top:22.5%;
+left:40%;
+}
 #attachment_view #oembed_info {
 margin-top:11px;
 }
@@ -1059,7 +1061,6 @@ margin-bottom:18px;
 padding-left:20px;
 }
 
-
 #filter_tags {
 margin-bottom:11px;
 float:left;
@@ -1105,8 +1106,6 @@ top:3px;
 left:3px;
 }
 
-
-
 .pagination {
 float:left;
 clear:both;
@@ -1152,7 +1151,6 @@ padding-right:30px;
 }
 /* END: NOTICE */
 
-
 .hentry .entry-content p {
 margin-bottom:18px;
 }
@@ -1169,7 +1167,6 @@ margin-bottom:18px;
 margin-left:18px;
 }
 
-
 /* TOP_POSTERS */
 .section tbody td {
 padding-right:18px;
@@ -1197,7 +1194,6 @@ margin-right:0;
 display:none;
 }
 
-
 /* tagcloud */
 .tag-cloud {
 list-style-type:none;
@@ -1280,6 +1276,10 @@ clear:both;
 margin-bottom:0;
 }
 
+#form_settings_design #settings_design_background-image img {
+max-width:480px;
+}
+
 #form_settings_design #settings_design_color .form_data,
 #form_settings_design #color-picker {
 float:left;