]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch '0.8.x' into group-rss-empty
authorJeffery To <jeffery.to@gmail.com>
Fri, 26 Jun 2009 07:30:24 +0000 (15:30 +0800)
committerJeffery To <jeffery.to@gmail.com>
Fri, 26 Jun 2009 07:30:24 +0000 (15:30 +0800)
225 files changed:
.gitignore
README
actions/accesstoken.php
actions/all.php
actions/allrss.php
actions/api.php
actions/attachment.php
actions/attachment_ajax.php
actions/avatarbynickname.php
actions/block.php
actions/blockedfromgroup.php [new file with mode: 0644]
actions/conversation.php
actions/designsettings.php [deleted file]
actions/disfavor.php
actions/doc.php
actions/editgroup.php
actions/facebookhome.php
actions/facebookinvite.php
actions/facebooklogin.php
actions/facebookremove.php
actions/facebooksettings.php
actions/favor.php
actions/favoritesrss.php
actions/featured.php
actions/file.php
actions/finishopenidlogin.php
actions/finishremotesubscribe.php
actions/foaf.php
actions/groupblock.php [new file with mode: 0644]
actions/groupdesignsettings.php [new file with mode: 0644]
actions/grouplogo.php
actions/groupmembers.php
actions/groups.php
actions/groupsearch.php
actions/groupunblock.php [new file with mode: 0644]
actions/invite.php
actions/joingroup.php
actions/logout.php
actions/makeadmin.php [new file with mode: 0644]
actions/microsummary.php
actions/newgroup.php
actions/newnotice.php
actions/noticesearch.php
actions/noticesearchrss.php
actions/nudge.php
actions/openidlogin.php
actions/opensearch.php
actions/peoplesearch.php
actions/peopletag.php
actions/postnotice.php
actions/public.php
actions/publicrss.php
actions/publicxrds.php
actions/recoverpassword.php
actions/remotesubscribe.php
actions/replies.php
actions/repliesrss.php
actions/requesttoken.php
actions/showfavorites.php
actions/showgroup.php
actions/shownotice.php
actions/showstream.php
actions/subedit.php
actions/subscribe.php
actions/subscribers.php
actions/subscriptions.php
actions/sup.php
actions/tag.php
actions/tagother.php
actions/tagrss.php
actions/twitapiaccount.php
actions/twitapiblocks.php
actions/twitapidirect_messages.php
actions/twitapifavorites.php
actions/twitapifriendships.php
actions/twitapihelp.php
actions/twitapinotifications.php
actions/twitapistatuses.php
actions/twitapiusers.php
actions/unblock.php
actions/unsubscribe.php
actions/updateprofile.php
actions/userauthorization.php
actions/userbyid.php
actions/userdesignsettings.php [new file with mode: 0644]
actions/usergroups.php
actions/userrss.php
actions/xrds.php
background/.gitignore [new file with mode: 0644]
classes/Avatar.php
classes/Design.php [new file with mode: 0644]
classes/Fave.php
classes/File.php
classes/File_oembed.php
classes/File_redirection.php
classes/File_thumbnail.php
classes/File_to_post.php
classes/Foreign_user.php
classes/Group_alias.php [new file with mode: 0644]
classes/Group_block.php [new file with mode: 0644]
classes/Group_inbox.php
classes/Memcached_DataObject.php
classes/Notice.php
classes/Notice_inbox.php
classes/Notice_tag.php
classes/Profile.php
classes/Profile_block.php
classes/Remote_profile.php
classes/Status_network.php
classes/Subscription.php
classes/User.php
classes/User_group.php
classes/laconica.ini [changed mode: 0644->0755]
classes/statusnet.ini [changed mode: 0755->0644]
config.php.sample
db/074to080.sql [new file with mode: 0644]
db/innodb.sql [new file with mode: 0644]
db/laconica.sql
db/site.sql
extlib/Console/Getopt.php [new file with mode: 0644]
extlib/System/Command.php [new file with mode: 0644]
index.php
install.php
js/farbtastic/farbtastic.go.js [deleted file]
js/farbtastic/farbtastic.js
js/userdesign.go.js [new file with mode: 0644]
js/util.js
lib/Shorturl_api.php
lib/accountsettingsaction.php
lib/arraywrapper.php
lib/attachmentlist.php
lib/channel.php
lib/clienterroraction.php
lib/command.php
lib/commandinterpreter.php
lib/common.php
lib/currentuserdesignaction.php [new file with mode: 0644]
lib/daemon.php
lib/dberroraction.php
lib/designsettings.php [new file with mode: 0644]
lib/error.php
lib/facebookutil.php
lib/galleryaction.php
lib/groupdesignaction.php [new file with mode: 0644]
lib/groupeditform.php
lib/grouplist.php
lib/groupnav.php
lib/grouptagcloudsection.php
lib/imagefile.php
lib/mailbox.php
lib/noticeform.php
lib/noticelist.php
lib/oauthstore.php
lib/omb.php
lib/openid.php
lib/ownerdesignaction.php [new file with mode: 0644]
lib/peoplesearchresults.php
lib/personal.php [deleted file]
lib/profileaction.php
lib/profilelist.php
lib/profileminilist.php
lib/queuehandler.php
lib/router.php
lib/rssaction.php
lib/search_engines.php
lib/searchaction.php
lib/servererroraction.php
lib/settingsaction.php
lib/stream.php [deleted file]
lib/subs.php
lib/subscriptionlist.php [new file with mode: 0644]
lib/theme.php
lib/twitter.php
lib/twitterapi.php
lib/util.php
lib/webcolor.php [new file with mode: 0644]
lib/xmppqueuehandler.php
scripts/allsites.php [new file with mode: 0755]
scripts/commandline.inc [new file with mode: 0644]
scripts/decache.php
scripts/delete_status_network.sh [new file with mode: 0755]
scripts/enjitqueuehandler.php
scripts/facebookqueuehandler.php
scripts/fixup_conversations.php [new file with mode: 0755]
scripts/fixup_hashtags.php
scripts/fixup_inboxes.php
scripts/fixup_notices_rendered.php
scripts/fixup_replies.php
scripts/fixup_utf8.php
scripts/getpiddir.php
scripts/getvaliddaemons.php
scripts/inbox_users.php
scripts/jabberqueuehandler.php
scripts/maildaemon.php
scripts/ombqueuehandler.php
scripts/pingqueuehandler.php
scripts/publicqueuehandler.php
scripts/reportsnapshot.php
scripts/setpassword.php
scripts/setup.cfg.sample [new file with mode: 0644]
scripts/setup_status_network.sh [new file with mode: 0755]
scripts/sitemap.php
scripts/smsqueuehandler.php
scripts/sphinx-cron.sh
scripts/sphinx-indexer.sh
scripts/startdaemons.sh
scripts/stopdaemons.sh
scripts/synctwitterfriends.php
scripts/triminboxes.php
scripts/twitterqueuehandler.php
scripts/twitterstatusfetcher.php
scripts/uncache_users.php
scripts/xmppconfirmhandler.php
scripts/xmppdaemon.php
sphinx.conf.sample
theme/base/css/display.css
theme/base/css/ie.css
theme/base/css/ie6.css
theme/base/images/icons/twotone/green/admin.gif [new file with mode: 0644]
theme/default/css/display.css
theme/default/css/ie.css
theme/identica/css/display.css
theme/identica/css/ie.css
theme/pigeonthoughts/css/base.css
theme/pigeonthoughts/css/display.css

index 3418d8ee54d1178448469dd417721c9831c1ea2d..f4c2bba5f7af504503ab4a03fa2d3faf50d99b2e 100644 (file)
@@ -1,4 +1,5 @@
 avatar/*
+background/*
 files/*
 file/*
 _darcs/*
@@ -22,3 +23,4 @@ config-*.php
 good-config.php
 lac08.log
 php.log
+config.php.*
diff --git a/README b/README
index 2099f94d62f46f953bd13716ef9b3abc0d98cd00..1a57d6a80e53d6ed09e78862f26e52893b0a8c04 100644 (file)
--- a/README
+++ b/README
@@ -178,8 +178,9 @@ and the URLs are listed here for your convenience.
 - Facebook library. Used for the Facebook application.
 - PEAR Services_oEmbed. Used for some multimedia integration.
 - PEAR HTTP_Request is an oEmbed dependency.
-- PEAR Validat is an oEmbed dependency.e
-- PEAR Net_URL is an oEmbed dependency.2
+- PEAR Validate is an oEmbed dependency.
+- PEAR Net_URL2 is an oEmbed dependency.
+- Console_GetOpt for parsing command-line options.
 
 A design goal of Laconica is that the basic Web functionality should
 work on even the most restrictive commercial hosting services.
@@ -1008,6 +1009,12 @@ avatar
 
 For configuring avatar access.
 
+dir:    Directory to look for avatar files and to put them into.
+       Defaults to avatar subdirectory of install directory; if
+       you change it, make sure to change path, too.
+path:  Path to avatars. Defaults to path for avatar subdirectory,
+       but you can change it if you wish. Note that this will
+       be included with the avatar server, too.
 server: If set, defines another server where avatars are stored in the
        root directory. Note that the 'avatar' subdir still has to be
        writeable. You'd typically use this to split HTTP requests on
@@ -1033,9 +1040,16 @@ theme
 -----
 
 server: Like avatars, you can speed up page loading by pointing the
-       theme file lookup to another server (virtual or real). The
-       theme server's root path should map to the Laconica "theme"
-       subdirectory. Defaults to NULL.
+       theme file lookup to another server (virtual or real).
+       Defaults to NULL, meaning to use the site server.
+dir:    Directory where theme files are stored. Used to determine
+       whether to show parts of a theme file. Defaults to the theme
+       subdirectory of the install directory.
+path:  Path part of theme URLs, before the theme name. Relative to the
+       theme server. It may make sense to change this path when upgrading,
+       (using version numbers as the path) to make sure that all files are
+       reloaded by caching clients or proxies. Defaults to null,
+       which means to use the site path + '/theme'.
 
 xmpp
 ----
@@ -1100,6 +1114,13 @@ database data in memcached <http://www.danga.com/memcached/>.
 enabled: Set to true to enable. Default false.
 server: a string with the hostname of the memcached server. Can also
        be an array of hostnames, if you've got more than one server.
+base: memcached uses key-value pairs to store data. We build long,
+      funny-looking keys to make sure we don't have any conflicts. The
+      base of the key is usually a simplified version of the site name
+      (like "Identi.ca" => "identica"), but you can overwrite this if
+      you need to. You can safely ignore it if you only have one
+      Laconica site using your memcached server.
+port: Port to connect to; defaults to 11211.
 
 sphinx
 ------
@@ -1196,7 +1217,6 @@ reporturl: URL to post statistics to. Defaults to Laconica developers'
            set 'run' to 'never' than to set this value to something
            nonsensical.
 
-
 attachments
 -----------
 
@@ -1211,6 +1231,11 @@ supported: an array of mime types you accept to store and distribute,
            like 'image/gif', 'video/mpeg', 'audio/mpeg', etc. Make sure you
            setup your server to properly reckognize the types you want to
            support.
+uploads:   false to disable uploading files with notices (true by default).
+filecommand: The required MIME_Type library may need to use the 'file'
+            command. It tries the one in the Web server's path, but if
+            you're having problems with uploads, try setting this to the
+            correct value. Note: 'file' must accept '-b' and '-i' options.
 
 For quotas, be sure you've set the upload_max_filesize and post_max_size
 in php.ini to be large enough to handle your upload. In httpd.conf
@@ -1226,6 +1251,32 @@ user_quota: total size in bytes a user can store on this server. Each user
 monthly_quota: total size permitted in the current month. This is the total
             size in bytes that a user can upload each month.
 
+group
+-----
+
+Options for group functionality.
+
+maxaliases: maximum number of aliases a group can have. Default 3. Set
+            to 0 or less to prevent aliases in a group.
+
+oohembed
+--------
+
+oEmbed endpoint for multimedia attachments (links in posts).
+
+endpoint: oohembed endpoint using http://oohembed.com/ software.
+
+search
+------
+
+Some stuff for search.
+
+type: type of search. Ignored if PostgreSQL or Sphinx are enabled. Can either
+      be 'fulltext' (default) or 'like'. The former is faster and more efficient
+      but requires the lame old MyISAM engine for MySQL. The latter
+      will work with InnoDB but could be miserably slow on large
+      systems. We'll probably add another type sometime in the future,
+      with our own indexing system (maybe like MediaWiki's).
 
 Troubleshooting
 ===============
index 46b43c70216e106a5722434c38f365d56c0d3cc5..2a8cd17134c84833ae1446fa8957c35ac5e06031 100644 (file)
@@ -12,7 +12,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 03179a2468cfa0396f28e43e7f15c3ea4bf19764..f06ead2a8c4776f70bd89b5f8d20a9683ede9450 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -98,7 +98,13 @@ class AllAction extends ProfileAction
 
     function showContent()
     {
-        $notice = $this->user->noticesWithFriends(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
+        $cur = common_current_user();
+
+        if (!empty($cur) && $cur->id == $this->user->id) {
+            $notice = $this->user->noticeInbox(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
+        } else {
+            $notice = $this->user->noticesWithFriends(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
+        }
 
         $nl = new NoticeList($notice, $this);
 
index 45f3946a61cfda010765ae3b54ca94360dc4d0a7..885a67f6188af13b07a0ea7e6bf9a8302078e476 100644 (file)
@@ -13,7 +13,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -81,6 +81,14 @@ class AllrssAction extends Rss10Action
      */
     function getNotices($limit=0)
     {
+        $cur = common_current_user();
+
+        if (!empty($cur) && $cur->id == $user->id) {
+            $notice = $this->user->noticeInbox(0, $limit);
+        } else {
+            $notice = $this->user->noticesWithFriends(0, $limit);
+        }
+
         $user    = $this->user;
         $notice  = $user->noticesWithFriends(0, $limit);
         $notices = array();
index b8da852b536d469682f6fcee277894247696695e..1fe5875ad6b586d6ec2fe7f2938ceab0110797dd 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 16ee723d96bb35c000008b64f0d8ee13a19cc786..e4dc0e054e27be9a238ad842eb823a8575e8d989 100644 (file)
@@ -98,48 +98,6 @@ class AttachmentAction extends Action
         return $a->title();
     }
 
-    /**
-     * 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)) . '"';
-    }
-*/
-
-
     /**
      * Handle input
      *
index 3d83393c51613c22f976974d52be67fd73083319..5d6773010f87668ad9f51fe6b01aa8580bb29d10 100644 (file)
@@ -74,46 +74,5 @@ class Attachment_ajaxAction extends AttachmentAction
         }
         $this->elementEnd('div');
     }
-
-    /**
-     * 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 e92a993722366ec1c9551857e11a369aec210f6f..3e615261fefbcd5272ca34433bbf936f607eea9d 100644 (file)
@@ -12,7 +12,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 34f991dc6126024451004e3115ad45c88106b1eb..06f92254e031b0e9cb52cfa34a8f71ffe3d4c3ca 100644 (file)
@@ -12,7 +12,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -125,16 +125,18 @@ class BlockAction extends Action
     function areYouSureForm()
     {
         $id = $this->profile->id;
+        $this->elementStart('form', array('id' => 'block-' . $id,
+                                           'method' => 'post',
+                                           'class' => 'form_settings form_entity_block',
+                                           'action' => common_local_url('block')));
+        $this->elementStart('fieldset');
+        $this->hidden('token', common_session_token());
+        $this->element('legend', _('Block user'));
         $this->element('p', null,
                        _('Are you sure you want to block this user? '.
                          'Afterwards, they will be unsubscribed from you, '.
                          'unable to subscribe to you in the future, and '.
                          'you will not be notified of any @-replies from them.'));
-        $this->elementStart('form', array('id' => 'block-' . $id,
-                                           'method' => 'post',
-                                           'class' => 'block',
-                                           'action' => common_local_url('block')));
-        $this->hidden('token', common_session_token());
         $this->element('input', array('id' => 'blockto-' . $id,
                                       'name' => 'blockto',
                                       'type' => 'hidden',
@@ -144,8 +146,9 @@ class BlockAction extends Action
                 $this->hidden($k, $v);
             }
         }
-        $this->submit('no', _('No'));
-        $this->submit('yes', _('Yes'));
+        $this->submit('form_action-no', _('No'), 'submit form_action-primary', 'no', _("Do not block this user from this group"));
+        $this->submit('form_action-yes', _('Yes'), 'submit form_action-secondary', 'yes', _('Block this user from this group'));
+        $this->elementEnd('fieldset');
         $this->elementEnd('form');
     }
 
@@ -180,7 +183,7 @@ class BlockAction extends Action
         if ($action) {
             common_redirect(common_local_url($action, $args), 303);
         } else {
-            common_redirect(common_local_url('subscriptions',
+            common_redirect(common_local_url('subscribers',
                                              array('nickname' => $cur->nickname)),
                             303);
         }
diff --git a/actions/blockedfromgroup.php b/actions/blockedfromgroup.php
new file mode 100644 (file)
index 0000000..5c1eab3
--- /dev/null
@@ -0,0 +1,315 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * List of group members
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Group
+ * @package   Laconica
+ * @author    Evan Prodromou <evan@controlyourself.ca>
+ * @copyright 2008-2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * List of profiles blocked from this group
+ *
+ * @category Group
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://laconi.ca/
+ */
+
+class BlockedfromgroupAction extends GroupDesignAction
+{
+    var $page = null;
+
+    function isReadOnly($args)
+    {
+        return true;
+    }
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+        $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
+
+        $nickname_arg = $this->arg('nickname');
+        $nickname = common_canonical_nickname($nickname_arg);
+
+        // Permanent redirect on non-canonical nickname
+
+        if ($nickname_arg != $nickname) {
+            $args = array('nickname' => $nickname);
+            if ($this->page != 1) {
+                $args['page'] = $this->page;
+            }
+            common_redirect(common_local_url('blockedfromgroup', $args), 301);
+            return false;
+        }
+
+        if (!$nickname) {
+            $this->clientError(_('No nickname'), 404);
+            return false;
+        }
+
+        $this->group = User_group::staticGet('nickname', $nickname);
+
+        if (!$this->group) {
+            $this->clientError(_('No such group'), 404);
+            return false;
+        }
+
+        return true;
+    }
+
+    function title()
+    {
+        if ($this->page == 1) {
+            return sprintf(_('%s blocked profiles'),
+                           $this->group->nickname);
+        } else {
+            return sprintf(_('%s blocked profiles, page %d'),
+                           $this->group->nickname,
+                           $this->page);
+        }
+    }
+
+    function handle($args)
+    {
+        parent::handle($args);
+        $this->showPage();
+    }
+
+    function showPageNotice()
+    {
+        $this->element('p', 'instructions',
+                       _('A list of the users blocked from joining this group.'));
+    }
+
+    function showLocalNav()
+    {
+        $nav = new GroupNav($this, $this->group);
+        $nav->show();
+    }
+
+    function showContent()
+    {
+        $offset = ($this->page-1) * PROFILES_PER_PAGE;
+        $limit =  PROFILES_PER_PAGE + 1;
+
+        $cnt = 0;
+
+        $blocked = $this->group->getBlocked($offset, $limit);
+
+        if ($blocked) {
+            $blocked_list = new GroupBlockList($blocked, $this->group, $this);
+            $cnt = $blocked_list->show();
+        }
+
+        $blocked->free();
+
+        $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
+                          $this->page, 'blockedfromgroup',
+                          array('nickname' => $this->group->nickname));
+    }
+}
+
+class GroupBlockList extends ProfileList
+{
+    var $group = null;
+
+    function __construct($profile, $group, $action)
+    {
+        parent::__construct($profile, $action);
+
+        $this->group = $group;
+    }
+
+    function newListItem($profile)
+    {
+        return new GroupBlockListItem($profile, $this->group, $this->action);
+    }
+}
+
+class GroupBlockListItem extends ProfileListItem
+{
+    var $group = null;
+
+    function __construct($profile, $group, $action)
+    {
+        parent::__construct($profile, $action);
+
+        $this->group = $group;
+    }
+
+    function showActions()
+    {
+        $this->startActions();
+        $this->showGroupUnblockForm();
+        $this->endActions();
+    }
+
+    function showGroupUnblockForm()
+    {
+        $user = common_current_user();
+
+        if (!empty($user) && $user->id != $this->profile->id && $user->isAdmin($this->group)) {
+            $this->out->elementStart('li', 'entity_block');
+            $bf = new GroupUnblockForm($this->out, $this->profile, $this->group,
+                                       array('action' => 'blockedfromgroup',
+                                             'nickname' => $this->group->nickname));
+            $bf->show();
+            $this->out->elementEnd('li');
+        }
+    }
+}
+
+/**
+ * Form for unblocking a user from a group
+ *
+ * @category Form
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@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/
+ *
+ * @see      UnblockForm
+ */
+
+class GroupUnblockForm extends Form
+{
+    /**
+     * Profile of user to block
+     */
+
+    var $profile = null;
+
+    /**
+     * Group to block the user from
+     */
+
+    var $group = null;
+
+    /**
+     * Return-to args
+     */
+
+    var $args = null;
+
+    /**
+     * Constructor
+     *
+     * @param HTMLOutputter $out     output channel
+     * @param Profile       $profile profile of user to block
+     * @param User_group    $group   group to block user from
+     * @param array         $args    return-to args
+     */
+
+    function __construct($out=null, $profile=null, $group=null, $args=null)
+    {
+        parent::__construct($out);
+
+        $this->profile = $profile;
+        $this->group   = $group;
+        $this->args    = $args;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+
+    function id()
+    {
+        // This should be unique for the page.
+        return 'unblock-' . $this->profile->id;
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+
+    function formClass()
+    {
+        return 'form_group_unblock';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+
+    function action()
+    {
+        return common_local_url('groupunblock');
+    }
+
+    /**
+     * Legend of the Form
+     *
+     * @return void
+     */
+    function formLegend()
+    {
+        $this->out->element('legend', null, _('Unblock user from group'));
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+
+    function formData()
+    {
+        $this->out->hidden('unblockto-' . $this->profile->id,
+                           $this->profile->id,
+                           'unblockto');
+        $this->out->hidden('unblockgroup-' . $this->group->id,
+                           $this->group->id,
+                           'unblockgroup');
+        if ($this->args) {
+            foreach ($this->args as $k => $v) {
+                $this->out->hidden('returnto-' . $k, $v);
+            }
+        }
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+
+    function formActions()
+    {
+        $this->out->submit('submit', _('Unblock'), 'submit', null, _('Unblock this user'));
+    }
+}
index 20c68986cbf15b8cfae58756ac9214abe0f79229..654a670f54ca6113ddcc31ad34503c29cfdf1791 100644 (file)
@@ -63,6 +63,7 @@ class ConversationAction extends Action
         if (empty($this->id)) {
             return false;
         }
+        $this->id = $this->id+0;
         $this->page = $this->trimmed('page');
         if (empty($this->page)) {
             $this->page = 1;
@@ -106,18 +107,10 @@ class ConversationAction extends Action
 
     function showContent()
     {
-        // FIXME this needs to be a tree, not a list
-
-        $qry = 'SELECT * FROM notice WHERE conversation = %s ';
-
         $offset = ($this->page-1) * NOTICES_PER_PAGE;
         $limit  = NOTICES_PER_PAGE + 1;
 
-        $txt = sprintf($qry, $this->id);
-
-        $notices = Notice::getStream($txt,
-                                     'notice:conversation:'.$this->id,
-                                     $offset, $limit);
+        $notices = Notice::conversationStream($this->id, $offset, $limit);
 
         $ct = new ConversationTree($notices, $this);
 
@@ -126,7 +119,6 @@ class ConversationAction extends Action
         $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
                           $this->page, 'conversation', array('id' => $this->id));
     }
-
 }
 
 /**
@@ -217,6 +209,8 @@ class ConversationTree extends NoticeList
 
             $this->out->elementStart('ol', array('class' => 'notices'));
 
+            sort($children);
+
             foreach ($children as $child) {
                 $this->showNoticePlus($child);
             }
diff --git a/actions/designsettings.php b/actions/designsettings.php
deleted file mode 100644 (file)
index 5774b85..0000000
+++ /dev/null
@@ -1,264 +0,0 @@
-<?php
-/**
- * Laconica, the distributed open-source microblogging tool
- *
- * Change user password
- *
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Settings
- * @package   Laconica
- * @author    Sarven Capadisli <csarven@controlyourself.ca>
- * @copyright 2008-2009 Control Yourself, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://laconi.ca/
- */
-
-if (!defined('LACONICA')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/lib/accountsettingsaction.php';
-
-
-
-class DesignsettingsAction extends AccountSettingsAction
-{
-    /**
-     * Title of the page
-     *
-     * @return string Title of the page
-     */
-
-    function title()
-    {
-        return _('Profile design');
-    }
-
-    /**
-     * Instructions for use
-     *
-     * @return instructions for use
-     */
-
-    function getInstructions()
-    {
-        return _('Customize the way your profile looks with a background image and a colour palette of your choice.');
-    }
-
-    /**
-     * Content area of the page
-     *
-     * Shows a form for changing the password
-     *
-     * @return void
-     */
-
-    function showContent()
-    {
-        $user = common_current_user();
-        $this->elementStart('form', array('method' => 'post',
-                                          'id' => 'form_settings_design',
-                                          'class' => 'form_settings',
-                                          'action' =>
-                                          common_local_url('designsettings')));
-        $this->elementStart('fieldset');
-        $this->hidden('token', common_session_token());
-
-        $this->elementStart('fieldset', array('id' => 'settings_design_background-image'));
-        $this->element('legend', null, _('Change background image'));
-        $this->elementStart('ul', 'form_data');
-        $this->elementStart('li');
-        $this->element('label', array('for' => 'design_background-image_file'), 
-                                _('Upload file'));
-        $this->element('input', array('name' => 'design_background-image_file',
-                                      'type' => 'file',
-                                      'id' => 'design_background-image_file'));
-        $this->element('p', 'form_guide', _('You can upload your personal background image. The maximum file size is 2Mb.'));
-        $this->element('input', array('name' => 'MAX_FILE_SIZE',
-                                      'type' => 'hidden',
-                                      'id' => 'MAX_FILE_SIZE',
-                                      'value' => ImageFile::maxFileSizeInt()));
-        $this->elementEnd('li');
-        $this->elementEnd('ul');
-        $this->elementEnd('fieldset');
-
-        $this->elementStart('fieldset', array('id' => 'settings_design_color'));
-        $this->element('legend', null, _('Change colours'));
-        $this->elementStart('ul', 'form_data');
-
-        //This is a JSON object in the DB field. Here for testing. Remove later.
-        $userSwatch = '{"body":{"background-color":"#F0F2F5"},
-                        "#content":{"background-color":"#FFFFFF"},
-                        "#aside_primary":{"background-color":"#CEE1E9"},
-                        "html body":{"color":"#000000"},
-                        "a":{"color":"#002E6E"}}';
-
-        //Default theme swatch -- Where should this be stored?
-        $defaultSwatch = array('body' => array('background-color' => '#F0F2F5'),
-                               '#content' => array('background-color' => '#FFFFFF'),
-                               '#aside_primary' => array('background-color' => '#CEE1E9'),
-                               'html body' => array('color' => '#000000'),
-                               'a' => array('color' => '#002E6E'));
-
-        $userSwatch = ($userSwatch) ? json_decode($userSwatch, true) : $defaultSwatch;
-
-        $s = 0;
-        $labelSwatch = array('Background',
-                             'Content',
-                             'Sidebar',
-                             'Text',
-                             'Links');
-        foreach($userSwatch as $propertyvalue => $value) {
-            $foo = array_values($value);
-            $this->elementStart('li');
-            $this->element('label', array('for' => 'swatch-'.$s), _($labelSwatch[$s]));
-            $this->element('input', array('name' => 'swatch-'.$s, //prefer swatch[$s] ?
-                                          'type' => 'text',
-                                          'id' => 'swatch-'.$s,
-                                          'class' => 'swatch',
-                                          'maxlength' => '7',
-                                          'size' => '7',
-                                          'value' => $foo[0]));
-            $this->elementEnd('li');
-            $s++;
-        }
-
-        $this->elementEnd('ul');
-        $this->elementEnd('fieldset');
-
-        $this->element('input', array('id' => 'settings_design_reset',
-                                      'type' => 'reset',
-                                      'value' => 'Reset',
-                                      'class' => 'submit form_action-primary',
-                                      'title' => _('Reset back to default')));
-        $this->submit('save', _('Save'), 'submit form_action-secondary', 'save', _('Save design'));
-
-/*TODO: Check submitted form values: 
-json_encode(form values)
-if submitted Swatch == DefaultSwatch, don't store in DB.
-else store in BD
-*/
-        $this->elementEnd('fieldset');
-        $this->elementEnd('form');
-
-    }
-
-    /**
-     * Handle a post
-     *
-     * Validate input and save changes. Reload the form with a success
-     * or error message.
-     *
-     * @return void
-     */
-
-    function handlePost()
-    {
-    /*
-        // CSRF protection
-
-        $token = $this->trimmed('token');
-        if (!$token || $token != common_session_token()) {
-            $this->showForm(_('There was a problem with your session token. '.
-                               'Try again, please.'));
-            return;
-        }
-
-        $user = common_current_user();
-        assert(!is_null($user)); // should already be checked
-
-        // FIXME: scrub input
-
-        $newpassword = $this->arg('newpassword');
-        $confirm     = $this->arg('confirm');
-
-        # Some validation
-
-        if (strlen($newpassword) < 6) {
-            $this->showForm(_('Password must be 6 or more characters.'));
-            return;
-        } else if (0 != strcmp($newpassword, $confirm)) {
-            $this->showForm(_('Passwords don\'t match.'));
-            return;
-        }
-
-        if ($user->password) {
-            $oldpassword = $this->arg('oldpassword');
-
-            if (!common_check_user($user->nickname, $oldpassword)) {
-                $this->showForm(_('Incorrect old password'));
-                return;
-            }
-        }
-
-        $original = clone($user);
-
-        $user->password = common_munge_password($newpassword, $user->id);
-
-        $val = $user->validate();
-        if ($val !== true) {
-            $this->showForm(_('Error saving user; invalid.'));
-            return;
-        }
-
-        if (!$user->update($original)) {
-            $this->serverError(_('Can\'t save new password.'));
-            return;
-        }
-
-        $this->showForm(_('Password saved.'), true);
-        */
-    }
-
-
-    /**
-     * Add the Farbtastic stylesheet
-     *
-     * @return void
-     */
-
-    function showStylesheets()
-    {
-        parent::showStylesheets();
-        $farbtasticStyle =
-          common_path('theme/base/css/farbtastic.css?version='.LACONICA_VERSION);
-
-        $this->element('link', array('rel' => 'stylesheet',
-                                     'type' => 'text/css',
-                                     'href' => $farbtasticStyle,
-                                     'media' => 'screen, projection, tv'));
-    }
-
-    /**
-     * Add the Farbtastic scripts
-     *
-     * @return void
-     */
-
-    function showScripts()
-    {
-        parent::showScripts();
-
-        $farbtasticPack = common_path('js/farbtastic/farbtastic.js');
-        $farbtasticGo   = common_path('js/farbtastic/farbtastic.go.js');
-
-        $this->element('script', array('type' => 'text/javascript',
-                                       'src' => $farbtasticPack));
-        $this->element('script', array('type' => 'text/javascript',
-                                       'src' => $farbtasticGo));
-    }
-}
index bc13b09da5ea931224a08cc07834034771167fd4..740f7de9333707458259037dc25aba62b883b04d 100644 (file)
@@ -13,7 +13,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index e6508030b658770e3009df0d47959886d4b89d52..54ae1380362b618f2ea22e0bfe78c1dd0aaca77e 100644 (file)
@@ -13,7 +13,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 39dad0465eb070ced615f205661e1115cb7f3b39..6aa6f8b11f20d82d59b46a57bf8129d81e9b327e 100644 (file)
@@ -23,6 +23,7 @@
  * @package   Laconica
  * @author    Evan Prodromou <evan@controlyourself.ca>
  * @author    Sarven Capadisli <csarven@controlyourself.ca>
+ * @author   Zach Copley <zach@controlyourself.ca>
  * @copyright 2008-2009 Control Yourself, Inc.
  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link      http://laconi.ca/
@@ -40,14 +41,15 @@ if (!defined('LACONICA')) {
  * @category Group
  * @package  Laconica
  * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @author   Zach Copley <zach@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 EditgroupAction extends Action
+class EditgroupAction extends GroupDesignAction
 {
+
     var $msg;
-    var $group = null;
 
     function title()
     {
@@ -171,6 +173,7 @@ class EditgroupAction extends Action
         $homepage    = $this->trimmed('homepage');
         $description = $this->trimmed('description');
         $location    = $this->trimmed('location');
+        $aliasstring = $this->trimmed('aliases');
 
         if (!Validate::string($nickname, array('min_length' => 1,
                                                'max_length' => 64,
@@ -201,6 +204,39 @@ class EditgroupAction extends Action
             return;
         }
 
+        if (!empty($aliasstring)) {
+            $aliases = array_map('common_canonical_nickname', array_unique(preg_split('/[\s,]+/', $aliasstring)));
+        } else {
+            $aliases = array();
+        }
+
+        if (count($aliases) > common_config('group', 'maxaliases')) {
+            $this->showForm(sprintf(_('Too many aliases! Maximum %d.'),
+                                    common_config('group', 'maxaliases')));
+            return;
+        }
+
+        foreach ($aliases as $alias) {
+            if (!Validate::string($alias, array('min_length' => 1,
+                                                'max_length' => 64,
+                                                'format' => NICKNAME_FMT))) {
+                $this->showForm(sprintf(_('Invalid alias: "%s"'), $alias));
+                return;
+            }
+            if ($this->nicknameExists($alias)) {
+                $this->showForm(sprintf(_('Alias "%s" already in use. Try another one.'),
+                                        $alias));
+                return;
+            }
+            // XXX assumes alphanum nicknames
+            if (strcmp($alias, $nickname) == 0) {
+                $this->showForm(_('Alias can\'t be the same as nickname.'));
+                return;
+            }
+        }
+
+        $this->group->query('BEGIN');
+
         $orig = clone($this->group);
 
         $this->group->nickname    = $nickname;
@@ -217,6 +253,14 @@ class EditgroupAction extends Action
             $this->serverError(_('Could not update group.'));
         }
 
+        $result = $this->group->setAliases($aliases);
+
+        if (!$result) {
+            $this->serverError(_('Could not create aliases.'));
+        }
+
+        $this->group->query('COMMIT');
+
         if ($this->group->nickname != $orig->nickname) {
             common_redirect(common_local_url('editgroup',
                                              array('nickname' => $nickname)),
@@ -229,9 +273,20 @@ class EditgroupAction extends Action
     function nicknameExists($nickname)
     {
         $group = User_group::staticGet('nickname', $nickname);
-        return (!is_null($group) &&
-                $group != false &&
-                $group->id != $this->group->id);
+
+        if (!empty($group) &&
+            $group->id != $this->group->id) {
+            return true;
+        }
+
+        $alias = Group_alias::staticGet('alias', $nickname);
+
+        if (!empty($alias) &&
+            $alias->group_id != $this->group->id) {
+            return true;
+        }
+
+        return false;
     }
 }
 
index 00b35ef6868aa238c50a8e1b8a9d3a1b13b64cd5..34989c9786aa9814f9396d49bcba0db3934e9af3 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -21,29 +21,28 @@ if (!defined('LACONICA')) { exit(1); }
 
 require_once INSTALLDIR.'/lib/facebookaction.php';
 
-
 class FacebookhomeAction extends FacebookAction
 {
 
     var $page = null;
-    
+
     function prepare($argarray)
-    {        
+    {
         parent::prepare($argarray);
-        
+
         $this->page = $this->trimmed('page');
-       
+
         if (!$this->page) {
             $this->page = 1;
         }
-        
+
         return true;
     }
 
     function handle($args)
     {
-        parent::handle($args);        
-                
+        parent::handle($args);
+
         // If the user has opted not to initially allow the app to have
         // Facebook status update permission, store that preference. Only
         // promt the user the first time she uses the app
@@ -73,7 +72,7 @@ class FacebookhomeAction extends FacebookAction
                  $this->updateProfileBox($notice);
              }
 
-             if ($this->arg('status_submit') == 'Send') {            
+             if ($this->arg('status_submit') == 'Send') {
                 $this->saveNewNotice();
              }
 
@@ -81,7 +80,7 @@ class FacebookhomeAction extends FacebookAction
             // Facebook status update permission? Then show the main page
             // of the app
             $this->showPage();
-            
+
         } else {
 
             // User hasn't authenticated yet, prompt for creds
@@ -89,12 +88,12 @@ class FacebookhomeAction extends FacebookAction
         }
 
     }
-    
+
     function login()
     {
-        
+
         $this->showStylesheets();
-        
+
         $nickname = common_canonical_nickname($this->trimmed('nickname'));
         $password = $this->arg('password');
 
@@ -141,13 +140,12 @@ class FacebookhomeAction extends FacebookAction
         $this->facebook->api_client->data_setUserPreference(
             FACEBOOK_PROMPTED_UPDATE_PREF, 'false');
     }
-    
 
     function showNoticeForm()
     {
         $post_action = "$this->app_uri/index.php";
-        
-        $notice_form = new FacebookNoticeForm($this, $post_action, null, 
+
+        $notice_form = new FacebookNoticeForm($this, $post_action, null,
             $post_action, $this->user);
         $notice_form->show();
     }
@@ -163,9 +161,8 @@ class FacebookhomeAction extends FacebookAction
 
     function showContent()
     {
-        $notice = $this->user->noticesWithFriends(($this->page-1) *
-            NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
-        
+        $notice = $this->user->noticeInbox(($this->page-1) * NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
+
         $nl = new NoticeList($notice, $this);
 
         $cnt = $nl->show();
@@ -176,7 +173,7 @@ class FacebookhomeAction extends FacebookAction
 
     function showNoticeList($notice)
     {
-                
+
         $nl = new NoticeList($notice, $this);
         return $nl->show();
     }
@@ -201,16 +198,16 @@ class FacebookhomeAction extends FacebookAction
 
         $this->elementStart('ul', array('id' => 'fb-permissions-list'));
         $this->elementStart('li', array('id' => 'fb-permissions-item'));
-        
+
         $next = urlencode("$this->app_uri/index.php");
         $api_key = common_config('facebook', 'apikey');
-        
+
         $auth_url = 'http://www.facebook.com/authorize.php?api_key=' .
-            $api_key . '&v=1.0&ext_perm=status_update&next=' . $next . 
+            $api_key . '&v=1.0&ext_perm=status_update&next=' . $next .
             '&next_cancel=' . $next . '&submit=skip';
-        
+
         $this->elementStart('span', array('class' => 'facebook-button'));
-        $this->element('a', array('href' => $auth_url), 
+        $this->element('a', array('href' => $auth_url),
             sprintf(_('Okay, do it!'), $this->app_name));
         $this->elementEnd('span');
 
@@ -225,7 +222,7 @@ class FacebookhomeAction extends FacebookAction
         $this->elementEnd('div');
 
     }
-    
+
     /**
      * Generate pagination links
      *
@@ -239,11 +236,11 @@ class FacebookhomeAction extends FacebookAction
      */
     function pagination($have_before, $have_after, $page, $action, $args=null)
     {
-                
+
         // Does a little before-after block for next/prev page
-     
+
         // XXX: Fix so this uses common_local_url() if possible.
-     
+
         if ($have_before || $have_after) {
             $this->elementStart('div', array('class' => 'pagination'));
             $this->elementStart('dl', null);
@@ -254,7 +251,7 @@ class FacebookhomeAction extends FacebookAction
         if ($have_before) {
             $pargs   = array('page' => $page-1);
             $newargs = $args ? array_merge($args, $pargs) : $pargs;
-            $this->elementStart('li', array('class' => 'nav_prev'));            
+            $this->elementStart('li', array('class' => 'nav_prev'));
             $this->element('a', array('href' => "$action?page=$newargs[page]", 'rel' => 'prev'),
                            _('After'));
             $this->elementEnd('li');
@@ -274,6 +271,5 @@ class FacebookhomeAction extends FacebookAction
             $this->elementEnd('div');
         }
     }
-    
 
 }
index 2207580f778781e07de381b697fd08473c7a22a3..f43d04e27f422a957a88901969983e071c6077d8 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 94d494a82cb42a5f718aecd33ff021bb8f9fb52b..22007da4fa6940315795058ce016b177a5747416 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 376e12a2e9ddfb2d64ae8f282945197c86d567b3..9ca7a77a84413c0aee266656fccb25fdd113811e 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 227e123160900d22a0b8d4d7563f229de4984e55..ee2c279ab5a95b2289e02148d587d043f7666fe9 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 3b7d979ebcc8139b67abaafb36904e0244629505..ec86b17e699a6bab1a5edd6a99942a58f91fa7f6 100644 (file)
@@ -15,7 +15,7 @@
 
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 6b46b8dec7ecf94b94667aa81173a907b66b9257..c439a9a62b5cc34d7b37ec7533f230b31e7b00e7 100644 (file)
@@ -13,7 +13,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 79eba2aa67b9985372a2e23a1e977306a62439ca..04365687d763c3e3b3dac39c60e5e3ff5266791f 100644 (file)
@@ -32,7 +32,7 @@ if (!defined('LACONICA')) {
     exit(1);
 }
 
-require_once(INSTALLDIR.'/lib/profilelist.php');
+require_once INSTALLDIR.'/lib/profilelist.php';
 require_once INSTALLDIR.'/lib/publicgroupnav.php';
 
 /**
@@ -107,7 +107,6 @@ class FeaturedAction extends Action
 
         $featured_nicks = common_config('nickname', 'featured');
 
-
         if (count($featured_nicks) > 0) {
 
             $quoted = array();
@@ -136,7 +135,7 @@ class FeaturedAction extends Action
             $cnt = $profile->find();
 
             if ($cnt > 0) {
-                $featured = new ProfileList($profile, null, $this);
+                $featured = new ProfileList($profile, $this);
                 $featured->show();
             }
 
index 1179dbe9ac97c7b826f85a1fc9aeb0f931c7c221..271f57ab9634aeaa4a6d675d30ac46a2849f30c8 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -21,20 +21,40 @@ if (!defined('LACONICA')) { exit(1); }
 
 require_once(INSTALLDIR.'/actions/shownotice.php');
 
-class FileAction extends ShowNoticeAction
+class FileAction extends Action
 {
-    function showPage() {
-        $source_url = common_local_url('file', array('notice' => $this->notice->id));
-        $query = "select file_redirection.url as url from file join file_redirection on file.id = file_redirection.file_id where file.url = '$source_url'";
-        $file = new File_redirection;
-        $file->query($query);
-        $file->fetch();
-        if (empty($file->url)) {
-            die('nothing attached here');
-        } else {
-            header("Location: {$file->url}");
-            die();
+    var $id = null;
+    var $filerec = null;
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+        $this->id = $this->trimmed('notice');
+        if (empty($this->id)) {
+            $this->clientError(_('No notice id'));
+        }
+        $notice = Notice::staticGet('id', $this->id);
+        if (empty($notice)) {
+            $this->clientError(_('No notice'));
+        }
+        $atts = $notice->attachments();
+        if (empty($atts)) {
+            $this->clientError(_('No attachments'));
+        }
+        foreach ($atts as $att) {
+            if (!empty($att->filename)) {
+                $this->filerec = $att;
+                break;
+            }
         }
+        if (empty($this->filerec)) {
+            $this->clientError(_('No uploaded attachments'));
+        }
+        return true;
+    }
+
+    function handle() {
+        common_redirect($this->filerec->url);
     }
 }
 
index b08b96df6c2afd017cecf9852fc663ec41b65e67..e9f7c746bb9195888f4bab95e558dcd3db06fc23 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 3e3a817154ddef6c5a81bb413a15817cf7956be8..5c764aeb0d320605fcd95b5865af89b1965c0ece 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index fb0172eb90d4236137501c407b6366b8d28fa4c0..b481b24377139ebbff5e5929f6eb9c95503947bb 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/actions/groupblock.php b/actions/groupblock.php
new file mode 100644 (file)
index 0000000..ce2c6c1
--- /dev/null
@@ -0,0 +1,215 @@
+<?php
+/**
+ * Block a user from a group action class.
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://laconi.ca/
+ *
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Block a user from a group
+ *
+ * @category Action
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://laconi.ca/
+ */
+
+class GroupblockAction extends Action
+{
+    var $profile = null;
+    var $group = null;
+
+    /**
+     * Take arguments for running
+     *
+     * @param array $args $_REQUEST args
+     *
+     * @return boolean success flag
+     */
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+        if (!common_logged_in()) {
+            $this->clientError(_('Not logged in.'));
+            return false;
+        }
+        $token = $this->trimmed('token');
+        if (empty($token) || $token != common_session_token()) {
+            $this->clientError(_('There was a problem with your session token. Try again, please.'));
+            return;
+        }
+        $id = $this->trimmed('blockto');
+        if (empty($id)) {
+            $this->clientError(_('No profile specified.'));
+            return false;
+        }
+        $this->profile = Profile::staticGet('id', $id);
+        if (empty($this->profile)) {
+            $this->clientError(_('No profile with that ID.'));
+            return false;
+        }
+        $group_id = $this->trimmed('blockgroup');
+        if (empty($group_id)) {
+            $this->clientError(_('No group specified.'));
+            return false;
+        }
+        $this->group = User_group::staticGet('id', $group_id);
+        if (empty($this->group)) {
+            $this->clientError(_('No such group.'));
+            return false;
+        }
+        $user = common_current_user();
+        if (!$user->isAdmin($this->group)) {
+            $this->clientError(_('Only an admin can block group members.'), 401);
+            return false;
+        }
+        if (Group_block::isBlocked($this->group, $this->profile)) {
+            $this->clientError(_('User is already blocked from group.'));
+            return false;
+        }
+        // XXX: could have proactive blocks, but we don't have UI for it.
+        if (!$this->profile->isMember($this->group)) {
+            $this->clientError(_('User is not a member of group.'));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Handle request
+     *
+     * Shows a page with list of favorite notices
+     *
+     * @param array $args $_REQUEST args; handled in prepare()
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        parent::handle($args);
+        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            if ($this->arg('no')) {
+                common_redirect(common_local_url('groupmembers',
+                                                 array('nickname' => $this->group->nickname)),
+                                303);
+            } elseif ($this->arg('yes')) {
+                $this->blockProfile();
+            } elseif ($this->arg('blockto')) {
+                $this->showPage();
+            }
+        }
+    }
+
+    function showContent() {
+        $this->areYouSureForm();
+    }
+
+    function title() {
+        return _('Block user from group');
+    }
+
+    function showNoticeForm() {
+        // nop
+    }
+
+    /**
+     * Confirm with user.
+     *
+     * Shows a confirmation form.
+     *
+     * @return void
+     */
+
+    function areYouSureForm()
+    {
+        $id = $this->profile->id;
+        $this->element('p', null,
+                       sprintf(_('Are you sure you want to block user "%s" from the group "%s"? '.
+                                 'They will be removed from the group, unable to post, and '.
+                                 'unable to subscribe to the group in the future.'),
+                               $this->profile->getBestName(),
+                               $this->group->getBestName()));
+        $this->elementStart('form', array('id' => 'block-' . $id,
+                                           'method' => 'post',
+                                           'class' => 'block',
+                                           'action' => common_local_url('groupblock')));
+        $this->hidden('token', common_session_token());
+        $this->hidden('blockto-' . $this->profile->id,
+                      $this->profile->id,
+                      'blockto');
+        $this->hidden('blockgroup-' . $this->group->id,
+                      $this->group->id,
+                      'blockgroup');
+        foreach ($this->args as $k => $v) {
+            if (substr($k, 0, 9) == 'returnto-') {
+                $this->hidden($k, $v);
+            }
+        }
+        $this->submit('no', _('No'));
+        $this->submit('yes', _('Yes'));
+        $this->elementEnd('form');
+    }
+
+    /**
+     * Actually block a user.
+     *
+     * @return void
+     */
+
+    function blockProfile()
+    {
+        $block = Group_block::blockProfile($this->group, $this->profile,
+                                           common_current_user());
+
+        if (empty($block)) {
+            $this->serverError(_("Database error blocking user from group."));
+            return false;
+        }
+
+        // Now, gotta figure where we go back to
+        foreach ($this->args as $k => $v) {
+            if ($k == 'returnto-action') {
+                $action = $v;
+            } elseif (substr($k, 0, 9) == 'returnto-') {
+                $args[substr($k, 9)] = $v;
+            }
+        }
+
+        if ($action) {
+            common_redirect(common_local_url($action, $args), 303);
+        } else {
+            common_redirect(common_local_url('groupmembers',
+                                             array('nickname' => $this->group->nickname)),
+                            303);
+        }
+    }
+}
+
diff --git a/actions/groupdesignsettings.php b/actions/groupdesignsettings.php
new file mode 100644 (file)
index 0000000..79c192a
--- /dev/null
@@ -0,0 +1,328 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Change user password
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Settings
+ * @package   Laconica
+ * @author    Sarven Capadisli <csarven@controlyourself.ca>
+ * @author    Zach Copley <zach@controlyourself.ca>
+ * @copyright 2008-2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/lib/designsettings.php';
+
+class GroupDesignSettingsAction extends DesignSettingsAction
+{
+    var $group = null;
+
+    /**
+     * Prepare to run
+     */
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        if (!common_config('inboxes','enabled')) {
+            $this->serverError(_('Inboxes must be enabled for groups to work'));
+            return false;
+        }
+
+        if (!common_logged_in()) {
+            $this->clientError(_('You must be logged in to edit a group.'));
+            return false;
+        }
+
+        $nickname_arg = $this->trimmed('nickname');
+        $nickname = common_canonical_nickname($nickname_arg);
+
+        // Permanent redirect on non-canonical nickname
+
+        if ($nickname_arg != $nickname) {
+            $args = array('nickname' => $nickname);
+            common_redirect(common_local_url('groupdesignsettings', $args), 301);
+            return false;
+        }
+
+        if (!$nickname) {
+            $this->clientError(_('No nickname'), 404);
+            return false;
+        }
+
+        $groupid = $this->trimmed('groupid');
+
+        if ($groupid) {
+            $this->group = User_group::staticGet('id', $groupid);
+        } else {
+            $this->group = User_group::staticGet('nickname', $nickname);
+        }
+
+        if (!$this->group) {
+            $this->clientError(_('No such group'), 404);
+            return false;
+        }
+
+        $cur = common_current_user();
+
+        if (!$cur->isAdmin($this->group)) {
+            $this->clientError(_('You must be an admin to edit the group'), 403);
+            return false;
+        }
+
+        $this->submitaction = common_local_url('groupdesignsettings',
+            array('nickname' => $this->group->nickname));
+
+        return true;
+    }
+
+    /**
+     * A design for this action
+     *
+     * if the group attribute has been set, returns that group's
+     * design.
+     *
+     * @return Design a design object to use
+     */
+
+    function getDesign()
+    {
+
+        if (empty($this->group)) {
+            return null;
+        }
+
+        return $this->group->getDesign();
+    }
+
+    /**
+     * Title of the page
+     *
+     * @return string Title of the page
+     */
+
+    function title()
+    {
+        return _('Group design');
+    }
+
+    /**
+     * Instructions for use
+     *
+     * @return instructions for use
+     */
+
+    function getInstructions()
+    {
+        return _('Customize the way your group looks ' .
+        'with a background image and a colour palette of your choice.');
+    }
+
+    /**
+     * Override to show group nav stuff
+     *
+     * @return nothing
+     */
+
+    function showLocalNav()
+    {
+        $nav = new GroupNav($this, $this->group);
+        $nav->show();
+    }
+
+    /**
+     * Get the design we want to edit
+     *
+     * @return Design
+     */
+
+    function getWorkingDesign() {
+
+        $design = null;
+
+        if (isset($this->group)) {
+            $design = $this->group->getDesign();
+        }
+
+        if (empty($design)) {
+            $design = $this->defaultDesign();
+        }
+
+        return $design;
+    }
+
+    /**
+     * Content area of the page
+     *
+     * Shows a form for changing the design
+     *
+     * @return void
+     */
+
+    function showContent()
+    {
+        $this->showDesignForm($this->getWorkingDesign());
+    }
+
+    /**
+     * Save or update the group's design settings
+     *
+     * @return void
+     */
+
+    function saveDesign()
+    {
+        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;
+        }
+
+        $onoff = $this->arg('design_background-image_onoff');
+
+        $on   = false;
+        $off  = false;
+        $tile = false;
+
+        if ($onoff == 'on') {
+            $on = true;
+        } else {
+            $off = true;
+        }
+
+        $repeat = $this->boolean('design_background-image_repeat');
+
+        if ($repeat) {
+            $tile = true;
+        }
+
+        $design = $this->group->getDesign();
+
+        if (!empty($design)) {
+
+            // update design
+
+            $original = clone($design);
+
+            $design->backgroundcolor = $bgcolor->intValue();
+            $design->contentcolor    = $ccolor->intValue();
+            $design->sidebarcolor    = $sbcolor->intValue();
+            $design->textcolor       = $tcolor->intValue();
+            $design->linkcolor       = $lcolor->intValue();
+
+            $design->setDisposition($on, $off, $tile);
+
+            $result = $design->update($original);
+
+            if ($result === false) {
+                common_log_db_error($design, 'UPDATE', __FILE__);
+                $this->showForm(_('Couldn\'t update your design.'));
+                return;
+            }
+
+        } else {
+
+            $this->group->query('BEGIN');
+
+            // save new design
+
+            $design = new Design();
+
+            $design->backgroundcolor = $bgcolor->intValue();
+            $design->contentcolor    = $ccolor->intValue();
+            $design->sidebarcolor    = $sbcolor->intValue();
+            $design->textcolor       = $tcolor->intValue();
+            $design->linkcolor       = $lcolor->intValue();
+
+            $design->setDisposition($on, $off, $tile);
+
+            $id = $design->insert();
+
+            if (empty($id)) {
+                common_log_db_error($id, 'INSERT', __FILE__);
+                $this->showForm(_('Unable to save your design settings!'));
+                return;
+            }
+
+            $original = clone($this->group);
+            $this->group->design_id = $id;
+            $result = $this->group->update($original);
+
+            if (empty($result)) {
+                common_log_db_error($original, 'UPDATE', __FILE__);
+                $this->showForm(_('Unable to save your design settings!'));
+                $this->group->query('ROLLBACK');
+                return;
+            }
+
+            $this->group->query('COMMIT');
+
+        }
+
+        $this->saveBackgroundImage($design);
+
+        $this->showForm(_('Design preferences saved.'), true);
+    }
+
+    /**
+     * Handle input and output a page (overrided)
+     *
+     * @param array $args $_REQUEST arguments
+     *
+     * @return void
+     */
+
+    function handle($args)
+    {
+        parent::handle($args);
+        if (!common_logged_in()) {
+            $this->clientError(_('Not logged in.'));
+            return;
+        } else if (!common_is_real_login()) {
+            // Cookie theft means that automatic logins can't
+            // change important settings or see private info, and
+            // _all_ our settings are important
+            common_set_returnto($this->selfUrl());
+            $user = common_current_user();
+            if ($user->hasOpenID()) {
+                common_redirect(common_local_url('openidlogin'), 303);
+            } else {
+                common_redirect(common_local_url('login'), 303);
+            }
+        } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            $this->handlePost();
+        } else {
+            $this->showForm();
+        }
+    }
+
+}
index fe6127da296243a0c13f15625a8fc57f162f5c83..8f6158dacaafbbd9626b525284745d3451c3f548 100644 (file)
@@ -50,7 +50,7 @@ define('MAX_ORIGINAL', 480);
  * @link     http://laconi.ca/
  */
 
-class GrouplogoAction extends Action
+class GrouplogoAction extends GroupDesignAction
 {
     var $mode = null;
     var $imagefile = null;
index 21e5ebbaa19c443b0bc81f8f9163b827eb488655..d132cdf9670512d5f5083a713726af77b014d90b 100644 (file)
@@ -44,7 +44,7 @@ require_once INSTALLDIR.'/lib/publicgroupnav.php';
  * @link     http://laconi.ca/
  */
 
-class GroupmembersAction extends Action
+class GroupmembersAction extends GroupDesignAction
 {
     var $page = null;
 
@@ -127,7 +127,7 @@ class GroupmembersAction extends Action
         $members = $this->group->getMembers($offset, $limit);
 
         if ($members) {
-            $member_list = new ProfileList($members, null, $this);
+            $member_list = new GroupMemberList($members, $this->group, $this);
             $cnt = $member_list->show();
         }
 
@@ -138,3 +138,326 @@ class GroupmembersAction extends Action
                           array('nickname' => $this->group->nickname));
     }
 }
+
+class GroupMemberList extends ProfileList
+{
+    var $group = null;
+
+    function __construct($profile, $group, $action)
+    {
+        parent::__construct($profile, $action);
+
+        $this->group = $group;
+    }
+
+    function newListItem($profile)
+    {
+        return new GroupMemberListItem($profile, $this->group, $this->action);
+    }
+}
+
+class GroupMemberListItem extends ProfileListItem
+{
+    var $group = null;
+
+    function __construct($profile, $group, $action)
+    {
+        parent::__construct($profile, $action);
+
+        $this->group = $group;
+    }
+
+    function showActions()
+    {
+        $this->startActions();
+        $this->showSubscribeButton();
+        $this->showMakeAdminForm();
+        $this->showGroupBlockForm();
+        $this->endActions();
+    }
+
+    function showMakeAdminForm()
+    {
+        $user = common_current_user();
+
+        if (!empty($user) && $user->id != $this->profile->id && $user->isAdmin($this->group) &&
+            !$this->profile->isAdmin($this->group)) {
+            $this->out->elementStart('li', 'entity_make_admin');
+            $maf = new MakeAdminForm($this->out, $this->profile, $this->group,
+                                     array('action' => 'groupmembers',
+                                           'nickname' => $this->group->nickname));
+            $maf->show();
+            $this->out->elementEnd('li');
+        }
+
+    }
+    function showGroupBlockForm()
+    {
+        $user = common_current_user();
+
+        if (!empty($user) && $user->id != $this->profile->id && $user->isAdmin($this->group)) {
+            $this->out->elementStart('li', 'entity_block');
+            $bf = new GroupBlockForm($this->out, $this->profile, $this->group,
+                                array('action' => 'groupmembers',
+                                      'nickname' => $this->group->nickname));
+            $bf->show();
+            $this->out->elementEnd('li');
+        }
+
+    }
+}
+
+/**
+ * Form for blocking a user from a group
+ *
+ * @category Form
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@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/
+ *
+ * @see      BlockForm
+ */
+
+class GroupBlockForm extends Form
+{
+    /**
+     * Profile of user to block
+     */
+
+    var $profile = null;
+
+    /**
+     * Group to block the user from
+     */
+
+    var $group = null;
+
+    /**
+     * Return-to args
+     */
+
+    var $args = null;
+
+    /**
+     * Constructor
+     *
+     * @param HTMLOutputter $out     output channel
+     * @param Profile       $profile profile of user to block
+     * @param User_group    $group   group to block user from
+     * @param array         $args    return-to args
+     */
+
+    function __construct($out=null, $profile=null, $group=null, $args=null)
+    {
+        parent::__construct($out);
+
+        $this->profile = $profile;
+        $this->group   = $group;
+        $this->args    = $args;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+
+    function id()
+    {
+        // This should be unique for the page.
+        return 'block-' . $this->profile->id;
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+
+    function formClass()
+    {
+        return 'form_group_block';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+
+    function action()
+    {
+        return common_local_url('groupblock');
+    }
+
+    /**
+     * Legend of the Form
+     *
+     * @return void
+     */
+    function formLegend()
+    {
+        $this->out->element('legend', null, _('Block user from group'));
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+
+    function formData()
+    {
+        $this->out->hidden('blockto-' . $this->profile->id,
+                           $this->profile->id,
+                           'blockto');
+        $this->out->hidden('blockgroup-' . $this->group->id,
+                           $this->group->id,
+                           'blockgroup');
+        if ($this->args) {
+            foreach ($this->args as $k => $v) {
+                $this->out->hidden('returnto-' . $k, $v);
+            }
+        }
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+
+    function formActions()
+    {
+        $this->out->submit('submit', _('Block'), 'submit', null, _('Block this user'));
+    }
+}
+
+/**
+ * Form for making a user an admin for a group
+ *
+ * @category Form
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@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 MakeAdminForm extends Form
+{
+    /**
+     * Profile of user to block
+     */
+
+    var $profile = null;
+
+    /**
+     * Group to block the user from
+     */
+
+    var $group = null;
+
+    /**
+     * Return-to args
+     */
+
+    var $args = null;
+
+    /**
+     * Constructor
+     *
+     * @param HTMLOutputter $out     output channel
+     * @param Profile       $profile profile of user to block
+     * @param User_group    $group   group to block user from
+     * @param array         $args    return-to args
+     */
+
+    function __construct($out=null, $profile=null, $group=null, $args=null)
+    {
+        parent::__construct($out);
+
+        $this->profile = $profile;
+        $this->group   = $group;
+        $this->args    = $args;
+    }
+
+    /**
+     * ID of the form
+     *
+     * @return int ID of the form
+     */
+
+    function id()
+    {
+        // This should be unique for the page.
+        return 'makeadmin-' . $this->profile->id;
+    }
+
+    /**
+     * class of the form
+     *
+     * @return string class of the form
+     */
+
+    function formClass()
+    {
+        return 'form_make_admin';
+    }
+
+    /**
+     * Action of the form
+     *
+     * @return string URL of the action
+     */
+
+    function action()
+    {
+        return common_local_url('makeadmin', array('nickname' => $this->group->nickname));
+    }
+
+    /**
+     * Legend of the Form
+     *
+     * @return void
+     */
+
+    function formLegend()
+    {
+        $this->out->element('legend', null, _('Make user an admin of the group'));
+    }
+
+    /**
+     * Data elements of the form
+     *
+     * @return void
+     */
+
+    function formData()
+    {
+        $this->out->hidden('profileid-' . $this->profile->id,
+                           $this->profile->id,
+                           'profileid');
+        $this->out->hidden('groupid-' . $this->group->id,
+                           $this->group->id,
+                           'groupid');
+        if ($this->args) {
+            foreach ($this->args as $k => $v) {
+                $this->out->hidden('returnto-' . $k, $v);
+            }
+        }
+    }
+
+    /**
+     * Action elements
+     *
+     * @return void
+     */
+
+    function formActions()
+    {
+        $this->out->submit('submit', _('Make Admin'), 'submit', null, _('Make this user an admin'));
+    }
+}
index 26b52a5fcd4dde75618205ccb3716900a8a4d6b8..b49d80f3779b96cc47401be41e8cc529d093efcf 100644 (file)
@@ -100,11 +100,13 @@ class GroupsAction extends Action
 
     function showContent()
     {
-        $this->elementStart('p', array('id' => 'new_group'));
-        $this->element('a', array('href' => common_local_url('newgroup'),
-                                  'class' => 'more'),
-                       _('Create a new group'));
-        $this->elementEnd('p');
+        if (common_logged_in()) {
+            $this->elementStart('p', array('id' => 'new_group'));
+            $this->element('a', array('href' => common_local_url('newgroup'),
+                                      'class' => 'more'),
+                           _('Create a new group'));
+            $this->elementEnd('p');
+        }
 
         $offset = ($this->page-1) * GROUPS_PER_PAGE;
         $limit =  GROUPS_PER_PAGE + 1;
index 06b4a77550cf33bd672207ace03e135e31754c93..c50466ce62a4f65f77ec730b58b2784756f66613 100644 (file)
@@ -12,7 +12,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/actions/groupunblock.php b/actions/groupunblock.php
new file mode 100644 (file)
index 0000000..6beb463
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+/**
+ * Block a user from a group action class.
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://laconi.ca/
+ *
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Unlock a user from a group
+ *
+ * @category Action
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://laconi.ca/
+ */
+
+class GroupunblockAction extends Action
+{
+    var $profile = null;
+    var $group = null;
+
+    /**
+     * Take arguments for running
+     *
+     * @param array $args $_REQUEST args
+     *
+     * @return boolean success flag
+     */
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+        if (!common_logged_in()) {
+            $this->clientError(_('Not logged in.'));
+            return false;
+        }
+        $token = $this->trimmed('token');
+        if (empty($token) || $token != common_session_token()) {
+            $this->clientError(_('There was a problem with your session token. Try again, please.'));
+            return;
+        }
+        $id = $this->trimmed('unblockto');
+        if (empty($id)) {
+            $this->clientError(_('No profile specified.'));
+            return false;
+        }
+        $this->profile = Profile::staticGet('id', $id);
+        if (empty($this->profile)) {
+            $this->clientError(_('No profile with that ID.'));
+            return false;
+        }
+        $group_id = $this->trimmed('unblockgroup');
+        if (empty($group_id)) {
+            $this->clientError(_('No group specified.'));
+            return false;
+        }
+        $this->group = User_group::staticGet('id', $group_id);
+        if (empty($this->group)) {
+            $this->clientError(_('No such group.'));
+            return false;
+        }
+        $user = common_current_user();
+        if (!$user->isAdmin($this->group)) {
+            $this->clientError(_('Only an admin can unblock group members.'), 401);
+            return false;
+        }
+        if (!Group_block::isBlocked($this->group, $this->profile)) {
+            $this->clientError(_('User is not blocked from group.'));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Handle request
+     *
+     * @param array $args $_REQUEST args; handled in prepare()
+     *
+     * @return void
+     */
+
+    function handle($args)
+    {
+        parent::handle($args);
+        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            $this->unblockProfile();
+        }
+    }
+
+    /**
+     * Unblock a user.
+     *
+     * @return void
+     */
+
+    function unblockProfile()
+    {
+        $result = Group_block::unblockProfile($this->group, $this->profile);
+
+        if (!$result) {
+            $this->serverError(_('Error removing the block.'));
+            return;
+        }
+
+        foreach ($this->args as $k => $v) {
+            if ($k == 'returnto-action') {
+                $action = $v;
+            } else if (substr($k, 0, 9) == 'returnto-') {
+                $args[substr($k, 9)] = $v;
+            }
+        }
+
+        if ($action) {
+            common_redirect(common_local_url($action, $args), 303);
+        } else {
+            common_redirect(common_local_url('blockedfromgroup',
+                                             array('nickname' => $this->group->nickname)),
+                            303);
+        }
+    }
+}
+
index 7e52cdbcc6af6d20b88be1944f038a306d999c0c..5dcc836526317e7b124dd4c03ea2bca6606cdb2f 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -19,7 +19,7 @@
 
 if (!defined('LACONICA')) { exit(1); }
 
-class InviteAction extends Action
+class InviteAction extends CurrentUserDesignAction
 {
     var $mode = null;
     var $error = null;
index a5d82ddc7780e3afeda2f38f9c16030773931fd1..0e4f96eaf5002e9bb2fd749beb7c49df8f9f1bad 100644 (file)
@@ -96,6 +96,11 @@ class JoingroupAction extends Action
             return false;
         }
 
+        if (Group_block::isBlocked($this->group, $cur->getProfile())) {
+            $this->clientError(_('You have been blocked from that group by the admin.'), 403);
+            return false;
+        }
+
         return true;
     }
 
index c34b10987a76985e08c15ad8adc8ac8f5a3211bb..3fcfb4f4ef2570352d1c28f888c09329182feced 100644 (file)
@@ -12,7 +12,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/actions/makeadmin.php b/actions/makeadmin.php
new file mode 100644 (file)
index 0000000..6fc2cf9
--- /dev/null
@@ -0,0 +1,166 @@
+<?php
+/**
+ * Make another user an admin of a group
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://laconi.ca/
+ *
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Make another user an admin of a group
+ *
+ * @category Action
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://laconi.ca/
+ */
+
+class MakeadminAction extends Action
+{
+    var $profile = null;
+    var $group = null;
+
+    /**
+     * Take arguments for running
+     *
+     * @param array $args $_REQUEST args
+     *
+     * @return boolean success flag
+     */
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+        if (!common_logged_in()) {
+            $this->clientError(_('Not logged in.'));
+            return false;
+        }
+        $token = $this->trimmed('token');
+        if (empty($token) || $token != common_session_token()) {
+            $this->clientError(_('There was a problem with your session token. Try again, please.'));
+            return;
+        }
+        $id = $this->trimmed('profileid');
+        if (empty($id)) {
+            $this->clientError(_('No profile specified.'));
+            return false;
+        }
+        $this->profile = Profile::staticGet('id', $id);
+        if (empty($this->profile)) {
+            $this->clientError(_('No profile with that ID.'));
+            return false;
+        }
+        $group_id = $this->trimmed('groupid');
+        if (empty($group_id)) {
+            $this->clientError(_('No group specified.'));
+            return false;
+        }
+        $this->group = User_group::staticGet('id', $group_id);
+        if (empty($this->group)) {
+            $this->clientError(_('No such group.'));
+            return false;
+        }
+        $user = common_current_user();
+        if (!$user->isAdmin($this->group)) {
+            $this->clientError(_('Only an admin can make another user an admin.'), 401);
+            return false;
+        }
+        if ($this->profile->isAdmin($this->group)) {
+            $this->clientError(sprintf(_('%s is already an admin for group "%s".'),
+                                       $this->profile->getBestName(),
+                                       $this->group->getBestName()),
+                               401);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Handle request
+     *
+     * @param array $args $_REQUEST args; handled in prepare()
+     *
+     * @return void
+     */
+
+    function handle($args)
+    {
+        parent::handle($args);
+        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            $this->makeAdmin();
+        }
+    }
+
+    /**
+     * Make user an admin
+     *
+     * @return void
+     */
+
+    function makeAdmin()
+    {
+        $member = Group_member::pkeyGet(array('group_id' => $this->group->id,
+                                              'profile_id' => $this->profile->id));
+
+        if (empty($member)) {
+            $this->serverError(_('Can\'t get membership record for %s in group %s'),
+                               $this->profile->getBestName(),
+                               $this->group->getBestName());
+        }
+
+        $orig = clone($member);
+
+        $member->is_admin = 1;
+
+        $result = $member->update($orig);
+
+        if (!$result) {
+            common_log_db_error($member, 'UPDATE', __FILE__);
+            $this->serverError(_('Can\'t make %s an admin for group %s'),
+                               $this->profile->getBestName(),
+                               $this->group->getBestName());
+        }
+
+        foreach ($this->args as $k => $v) {
+            if ($k == 'returnto-action') {
+                $action = $v;
+            } else if (substr($k, 0, 9) == 'returnto-') {
+                $args[substr($k, 9)] = $v;
+            }
+        }
+
+        if ($action) {
+            common_redirect(common_local_url($action, $args), 303);
+        } else {
+            common_redirect(common_local_url('groupmembers',
+                                             array('nickname' => $this->group->nickname)),
+                            303);
+        }
+    }
+}
index 0b408ec953aff275b5523fbd3281b0f917de0564..6884a919a89eb82a343183c4c8e8844be2c96d40 100644 (file)
@@ -12,7 +12,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 67cd6b2f18005f3efc3878a71bec65cbe92a5365..0289e77c2511a7aebd096a4d78e66d54ac72dbe3 100644 (file)
@@ -123,6 +123,7 @@ class NewgroupAction extends Action
         $homepage    = $this->trimmed('homepage');
         $description = $this->trimmed('description');
         $location    = $this->trimmed('location');
+        $aliasstring = $this->trimmed('aliases');
 
         if (!Validate::string($nickname, array('min_length' => 1,
                                                'max_length' => 64,
@@ -153,6 +154,37 @@ class NewgroupAction extends Action
             return;
         }
 
+        if (!empty($aliasstring)) {
+            $aliases = array_map('common_canonical_nickname', array_unique(preg_split('/[\s,]+/', $aliasstring)));
+        } else {
+            $aliases = array();
+        }
+
+        if (count($aliases) > common_config('group', 'maxaliases')) {
+            $this->showForm(sprintf(_('Too many aliases! Maximum %d.'),
+                                    common_config('group', 'maxaliases')));
+            return;
+        }
+
+        foreach ($aliases as $alias) {
+            if (!Validate::string($alias, array('min_length' => 1,
+                                                'max_length' => 64,
+                                                'format' => NICKNAME_FMT))) {
+                $this->showForm(sprintf(_('Invalid alias: "%s"'), $alias));
+                return;
+            }
+            if ($this->nicknameExists($alias)) {
+                $this->showForm(sprintf(_('Alias "%s" already in use. Try another one.'),
+                                        $alias));
+                return;
+            }
+            // XXX assumes alphanum nicknames
+            if (strcmp($alias, $nickname) == 0) {
+                $this->showForm(_('Alias can\'t be the same as nickname.'));
+                return;
+            }
+        }
+
         $cur = common_current_user();
 
         // Checked in prepare() above
@@ -177,6 +209,12 @@ class NewgroupAction extends Action
             $this->serverError(_('Could not create group.'));
         }
 
+        $result = $group->setAliases($aliases);
+
+        if (!$result) {
+            $this->serverError(_('Could not create aliases.'));
+        }
+
         $member = new Group_member();
 
         $member->group_id   = $group->id;
@@ -199,7 +237,18 @@ class NewgroupAction extends Action
     function nicknameExists($nickname)
     {
         $group = User_group::staticGet('nickname', $nickname);
-        return (!is_null($group) && $group != false);
+
+        if (!empty($group)) {
+            return true;
+        }
+
+        $alias = Group_alias::staticGet('alias', $nickname);
+
+        if (!empty($alias)) {
+            return true;
+        }
+
+        return false;
     }
 }
 
index 02976a2ae2c20f84d44a3db0660d4bcf5226ffbb..15caff6eaab0042783314c8cc1e6d80e7f9977cc 100644 (file)
@@ -116,6 +116,9 @@ class NewnoticeAction extends Action
     function getUploadedFileType() {
         require_once 'MIME/Type.php';
 
+        $cmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd');
+        $cmd = common_config('attachments', 'filecommand');
+
         $filetype = MIME_Type::autoDetect($_FILES['attach']['tmp_name']);
         if (in_array($filetype, common_config('attachments', 'supported'))) {
             return $filetype;
@@ -221,17 +224,35 @@ class NewnoticeAction extends Action
             }
         }
 
+        if (isset($mimetype)) {
+            $filename = $this->saveFile($mimetype);
+            if (empty($filename)) {
+                $this->clientError(_('Couldn\'t save file.'));
+            }
+            $fileurl = File::url($filename);
+            $short_fileurl = common_shorten_url($fileurl);
+            $content_shortened .= ' ' . $short_fileurl;
+            if (mb_strlen($content_shortened) > 140) {
+                $this->deleteFile($filename);
+                $this->clientError(_('Max notice size is 140 chars, including attachment URL.'));
+            }
+            $fileRecord = $this->rememberFile($filename, $mimetype, $short_fileurl);
+        }
+
         $notice = Notice::saveNew($user->id, $content_shortened, 'web', 1,
                                   ($replyto == 'false') ? null : $replyto);
 
         if (is_string($notice)) {
+            if (isset($filename)) {
+                $this->deleteFile($filename);
+            }
             $this->clientError($notice);
         }
 
         if (isset($mimetype)) {
-            $this->storeFile($notice, $mimetype);
+            $this->attachFile($notice, $fileRecord);
         }
-        $this->saveUrls($notice);
+
         common_broadcast_notice($notice);
 
         if ($this->boolean('ajax')) {
@@ -257,49 +278,82 @@ class NewnoticeAction extends Action
         }
     }
 
-    function storeFile($notice, $mimetype) {
-        $filename = basename($_FILES['attach']['name']);
-        $destination = "file/{$notice->id}-$filename";
-        if (move_uploaded_file($_FILES['attach']['tmp_name'], INSTALLDIR . "/$destination")) {
-            $file = new File;
-            $file->url = common_local_url('file', array('notice' => $notice->id));
-            $file->size = filesize(INSTALLDIR . "/$destination");
-            $file->date = time();
-            $file->mimetype = $mimetype;
-            if ($file_id = $file->insert()) {
-                $file_redir = new File_redirection;
-                $file_redir->url = common_path($destination);
-                $file_redir->file_id = $file_id;
-                $file_redir->insert();
-
-                $f2p = new File_to_post;
-                $f2p->file_id = $file_id; 
-                $f2p->post_id = $notice->id; 
-                $f2p->insert();
-            } else {
-                $this->clientError(_('There was a database error while saving your file. Please try again.'));
-            }
+    function saveFile($mimetype) {
+
+        $cur = common_current_user();
+
+        if (empty($cur)) {
+            $this->serverError(_('Somehow lost the login in saveFile'));
+        }
+
+        $basename = basename($_FILES['attach']['name']);
+
+        $filename = File::filename($cur->getProfile(), $basename, $mimetype);
+
+        $filepath = File::path($filename);
+
+        if (move_uploaded_file($_FILES['attach']['tmp_name'], $filepath)) {
+            return $filename;
         } else {
             $this->clientError(_('File could not be moved to destination directory.'));
         }
     }
 
-    /** save all urls in the notice to the db
-     *
-     * follow redirects and save all available file information
-     * (mimetype, date, size, oembed, etc.)
-     *
-     * @param class $notice Notice to pull URLs from
-     *
-     * @return void
-     */
-    function saveUrls($notice, $uploaded = null) {
-        common_replace_urls_callback($notice->content, array($this, 'saveUrl'), $notice->id);
+    function deleteFile($filename)
+    {
+        $filepath = File::path($filename);
+        @unlink($filepath);
+    }
+
+    function rememberFile($filename, $mimetype, $short)
+    {
+        $file = new File;
+        $file->filename = $filename;
+
+        $file->url = File::url($filename);
+
+        $filepath = File::path($filename);
+
+        $file->size = filesize($filepath);
+        $file->date = time();
+        $file->mimetype = $mimetype;
+
+        $file_id = $file->insert();
+
+        if (!$file_id) {
+            common_log_db_error($file, "INSERT", __FILE__);
+            $this->clientError(_('There was a database error while saving your file. Please try again.'));
+        }
+
+        $this->maybeAddRedir($file_id, $short);
+
+        return $file;
+    }
+
+    function maybeAddRedir($file_id, $url)
+    {
+        $file_redir = File_redirection::staticGet('url', $url);
+
+        if (empty($file_redir)) {
+            $file_redir = new File_redirection;
+            $file_redir->url = $url;
+            $file_redir->file_id = $file_id;
+
+            $result = $file_redir->insert();
+
+            if (!$result) {
+                common_log_db_error($file_redir, "INSERT", __FILE__);
+                $this->clientError(_('There was a database error while saving your file. Please try again.'));
+            }
+        }
     }
 
-    function saveUrl($data) {
-        list($url, $notice_id) = $data;
-        $zzz = File::processNew($url, $notice_id);
+    function attachFile($notice, $filerec)
+    {
+        File_to_post::processNew($filerec->id, $notice->id);
+
+        $this->maybeAddRedir($filerec->id,
+            common_local_url('file', array('notice' => $notice->id)));
     }
 
     /**
index d996998fc6026328cc6a8271756393203b2202fa..49b473d9e9cd542c03be950ff5fc93a60afb111b 100644 (file)
@@ -13,7 +13,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index f6da969ee47bc29a0257ac3772320874a943299c..c1bf3bf5f21db9311e3b9b60b6b170c592f50a9d 100644 (file)
@@ -12,7 +12,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index c23d3e64356f4af88ea166663e9d61d56cc95c09..78c0ee566b55923b7fbcddbaf07f81d86fae1f44 100644 (file)
@@ -13,7 +13,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 1a4372d73e76722dfe5f60d30b220b6ba191b3f0..a8d052096ca470712c43b78e0a2988c6546a5605 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index d1f4895ce4a2d569aa2263eaf3bce37a9d8f0ede..4fe95c93b5cf318443d1c663638e1bb525021c61 100644 (file)
@@ -13,7 +13,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 65d970dd159a7fea183763d9fd8488fdde4cb2ab..c61e0e273957f5779a9730650bc7fec073d78c1b 100644 (file)
@@ -12,7 +12,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 5add75485808d08f704fa6dfa73faa34593605d6..dd3c1c0899a7befec824afeae83e4c8f19434633 100644 (file)
@@ -124,7 +124,7 @@ class PeopletagAction extends Action
 
         $profile->query(sprintf($qry, $this->tag, $lim));
 
-        $pl  = new ProfileList($profile, null, $this);
+        $pl  = new ProfileList($profile, $this);
         $cnt = $pl->show();
 
         $this->pagination($this->page > 1,
index 3e98b3cd5535efe46c946df4cc1e664dc7fbc6f2..eb2d63b61cf08119ba2911b130a6aaf50ee508fd 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 27153f13159daf2c990ba7937cc697e0724ad27f..9851285c4821367288b1bd8c5797d3a4912ca840 100644 (file)
@@ -35,6 +35,10 @@ require_once INSTALLDIR.'/lib/publicgroupnav.php';
 require_once INSTALLDIR.'/lib/noticelist.php';
 require_once INSTALLDIR.'/lib/feedlist.php';
 
+// Farther than any human will go
+
+define('MAX_PUBLIC_PAGE', 100);
+
 /**
  * Action for displaying the public stream
  *
@@ -74,6 +78,10 @@ class PublicAction extends Action
         parent::prepare($args);
         $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
 
+        if ($this->page > MAX_PUBLIC_PAGE) {
+            $this->clientError(sprintf(_("Beyond the page limit (%s)"), MAX_PUBLIC_PAGE));
+        }
+
         common_set_returnto($this->selfUrl());
 
         return true;
index bc52f29522778483220c7994605b5b552c928502..7e8df9625113345e521b7083b68176b469c51e59 100644 (file)
@@ -13,7 +13,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 283a932ca56772b4191a5bc6a5fa7e2c5618085c..0a14215502e1201a720ec4b9f2c737d7a4c0f292 100644 (file)
@@ -13,7 +13,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 82263fcd59903cc921e42f4c55bc14c3cf2c94a4..2afd052a78f3fbb8e5d00c63339aadf56aa3712e 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 0b117489621d7307776b5266177e21e7b35be788..e658f8d3748ed290159222c5ed13867187e3be3e 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index eac4d0a3aeeda3622eec4bcbaec8a787fbbe82ac..d7ed440e9237d0a32fd814eb1dd99269ad6393f9 100644 (file)
@@ -45,9 +45,8 @@ require_once INSTALLDIR.'/lib/feedlist.php';
  * @link     http://laconi.ca/
  */
 
-class RepliesAction extends Action
+class RepliesAction extends OwnerDesignAction
 {
-    var $user = null;
     var $page = null;
 
     /**
index 2017c43094a5f40f176d48efb3c3ff4601aba8c0..a87e2870dc090c3bae2f23cc7aac1b128b9e0287 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 9507e3d6c9c3a2c897276d31a794188a1620d0bb..8d1e3f004342ea399a9a35efb80386b57063bc58 100644 (file)
@@ -13,7 +13,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 865045337aa9c07e8421f4801aeaa4f584f7e718..b723924a5e0a323209f0daf09888933841b0b9b6 100644 (file)
@@ -45,7 +45,7 @@ require_once INSTALLDIR.'/lib/feedlist.php';
  * @link     http://laconi.ca/
  */
 
-class ShowfavoritesAction extends Action
+class ShowfavoritesAction extends CurrentUserDesignAction
 {
     /** User we're getting the faves of */
     var $user = null;
@@ -191,10 +191,21 @@ class ShowfavoritesAction extends Action
 
     function showContent()
     {
-        $notice = $this->user->favoriteNotices(($this->page-1)*NOTICES_PER_PAGE,
-                                               NOTICES_PER_PAGE + 1);
+        $cur = common_current_user();
 
-        if (!$notice) {
+        if (!empty($cur) && $cur->id == $this->user->id) {
+
+            // Show imported/gateway notices as well as local if
+            // the user is looking at his own favorites
+
+            $notice = $this->user->favoriteNotices(($this->page-1)*NOTICES_PER_PAGE,
+                                                   NOTICES_PER_PAGE + 1, true);
+        } else {
+            $notice = $this->user->favoriteNotices(($this->page-1)*NOTICES_PER_PAGE,
+                                                   NOTICES_PER_PAGE + 1, false);
+        }
+
+        if (empty($notice)) {
             $this->serverError(_('Could not retrieve favorite notices.'));
             return;
         }
index 29b6fa1e61be25ee576a7256a4d89eb1bb88daa3..b6a0f4844e78a69752cd6c690203b54d869ede07 100644 (file)
@@ -47,10 +47,9 @@ define('MEMBERS_PER_SECTION', 27);
  * @link     http://laconi.ca/
  */
 
-class ShowgroupAction extends Action
+class ShowgroupAction extends GroupDesignAction
 {
-    /** group we're viewing. */
-    var $group = null;
+
     /** page we're viewing. */
     var $page = null;
 
@@ -272,6 +271,17 @@ class ShowgroupAction extends Action
             $this->elementEnd('dl');
         }
 
+        if (common_config('group', 'maxaliases') > 0) {
+            $aliases = $this->group->getAliases();
+
+            if (!empty($aliases)) {
+                $this->elementStart('dl', 'entity_aliases');
+                $this->element('dt', null, _('Aliases'));
+                $this->element('dd', 'aliases', implode(' ', $aliases));
+                $this->elementEnd('dl');
+            }
+        }
+
         $this->elementEnd('div');
 
         $this->elementStart('div', 'entity_actions');
@@ -283,7 +293,7 @@ class ShowgroupAction extends Action
             if ($cur->isMember($this->group)) {
                 $lf = new LeaveForm($this, $this->group);
                 $lf->show();
-            } else {
+            } else if (!Group_block::isBlocked($this->group, $cur->getProfile())) {
                 $jf = new JoinForm($this, $this->group);
                 $jf->show();
             }
@@ -344,7 +354,7 @@ class ShowgroupAction extends Action
 
         $this->element('h2', null, _('Members'));
 
-        $pml = new ProfileMiniList($member, null, $this);
+        $pml = new ProfileMiniList($member, $this);
         $cnt = $pml->show();
         if ($cnt == 0) {
              $this->element('p', null, _('(None)'));
index b0d973a991cad8e61cfa2d8ce632013e89f3201b..0d89af5acc40b81849ee4d4fb2b7ca80fdbe53fd 100644 (file)
@@ -209,7 +209,7 @@ class ShownoticeAction extends Action
     function showContent()
     {
         $this->elementStart('ol', array('class' => 'notices xoxo'));
-        $nli = new NoticeListItem($this->notice, $this);
+        $nli = new SingleNoticeItem($this->notice, $this);
         $nli->show();
         $this->elementEnd('ol');
     }
@@ -264,3 +264,29 @@ class ShownoticeAction extends Action
         }
     }
 }
+
+class SingleNoticeItem extends NoticeListItem
+{
+    /**
+     * recipe function for displaying a single notice.
+     *
+     * We overload to show attachments.
+     *
+     * @return void
+     */
+
+    function show()
+    {
+        $this->showStart();
+        $this->showNotice();
+        $this->showNoticeAttachments();
+        $this->showNoticeInfo();
+        $this->showNoticeOptions();
+        $this->showEnd();
+    }
+
+    function showNoticeAttachments() {
+        $al = new AttachmentList($this->notice, $this->out);
+        $al->show();
+    }
+}
index e2f4e24d435ea7476132f7a22a16fc9cbed6743e..cd5d4bb7013bfb84f87d8eca580ddc0ba8c74bb6 100644 (file)
@@ -320,10 +320,14 @@ class ShowstreamAction extends ProfileAction
             $blocked = $cur->hasBlocked($this->profile);
             $this->elementStart('li', 'entity_block');
             if ($blocked) {
-                $ubf = new UnblockForm($this, $this->profile);
+                $ubf = new UnblockForm($this, $this->profile,
+                                       array('action' => 'showstream',
+                                             'nickname' => $this->profile->nickname));
                 $ubf->show();
             } else {
-                $bf = new BlockForm($this, $this->profile);
+                $bf = new BlockForm($this, $this->profile,
+                                    array('action' => 'showstream',
+                                          'nickname' => $this->profile->nickname));
                 $bf->show();
             }
             $this->elementEnd('li');
@@ -366,7 +370,7 @@ class ShowstreamAction extends ProfileAction
     {
         $notice = empty($this->tag)
             ? $this->user->getNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1)
-            : $this->user->getTaggedNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1, 0, 0, null, $this->tag);
+            : $this->user->getTaggedNotices($this->tag, ($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1, 0, 0, null);
 
         $pnl = new ProfileNoticeList($notice, $this);
         $cnt = $pnl->show();
index 8ca2d791465ce34ce9e067051c18df0f1682321f..2e1bf553880144a0519dc1e1fed98c29ce196afe 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 0bc522867e39ca4a62ffd12a5ebf417a85441ba5..15b89a3122f95eeecce93fec73cff71f858141eb 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 4482de9a7c74279b000e042012fbb927a86b5dbd..66ac00fb19aa26d64da5441d07fafec8bc08f36a 100644 (file)
@@ -130,18 +130,34 @@ class SubscribersAction extends GalleryAction
     }
 }
 
-class SubscribersList extends ProfileList
+class SubscribersList extends SubscriptionList
 {
-    function showBlockForm()
+    function newListItem($profile)
     {
-        $bf = new BlockForm($this->out, $this->profile,
-                            array('action' => 'subscribers',
-                                  'nickname' => $this->owner->nickname));
-        $bf->show();
+        return new SubscribersListItem($profile, $this->owner, $this->action);
     }
+}
 
-    function isReadOnly($args)
+class SubscribersListItem extends SubscriptionListItem
+{
+    function showActions()
     {
-        return true;
+        $this->startActions();
+        $this->showSubscribeButton();
+        // Relevant code!
+        $this->showBlockForm();
+        $this->endActions();
+    }
+
+    function showBlockForm()
+    {
+        $user = common_current_user();
+
+        if (!empty($user) && $this->owner->id == $user->id) {
+            $bf = new BlockForm($this->out, $this->profile,
+                                array('action' => 'subscribers',
+                                      'nickname' => $this->owner->nickname));
+            $bf->show();
+        }
     }
 }
index 095b18ad87db49b97f1c16746d3a7366f6b0f7a4..4124abea4d2e77e82dbde71c296f0cbff526a02c 100644 (file)
@@ -137,22 +137,46 @@ class SubscriptionsAction extends GalleryAction
     }
 }
 
-class SubscriptionsList extends ProfileList
+// XXX SubscriptionsList and SubscriptionList are dangerously close
+
+class SubscriptionsList extends SubscriptionList
 {
-    function showOwnerControls($profile)
+    function newListItem($profile)
+    {
+        return new SubscriptionsListItem($profile, $this->owner, $this->action);
+    }
+}
+
+class SubscriptionsListItem extends SubscriptionListItem
+{
+    function showProfile()
+    {
+        $this->startProfile();
+        $this->showAvatar();
+        $this->showFullName();
+        $this->showLocation();
+        $this->showHomepage();
+        $this->showBio();
+        $this->showTags();
+        // Relevant portion!
+        $this->showOwnerControls();
+        $this->endProfile();
+    }
+
+    function showOwnerControls()
     {
         $sub = Subscription::pkeyGet(array('subscriber' => $this->owner->id,
-                                           'subscribed' => $profile->id));
+                                           'subscribed' => $this->profile->id));
         if (!$sub) {
             return;
         }
 
-        $this->out->elementStart('form', array('id' => 'subedit-' . $profile->id,
+        $this->out->elementStart('form', array('id' => 'subedit-' . $this->profile->id,
                                           'method' => 'post',
                                           'class' => 'form_subscription_edit',
                                           'action' => common_local_url('subedit')));
         $this->out->hidden('token', common_session_token());
-        $this->out->hidden('profile', $profile->id);
+        $this->out->hidden('profile', $this->profile->id);
         $this->out->checkbox('jabber', _('Jabber'), $sub->jabber);
         $this->out->checkbox('sms', _('SMS'), $sub->sms);
         $this->out->submit('save', _('Save'));
index 691153d6a3bcd845c0f3c4765cc8ee9f3040b328..e446a7b0ddfc3ba0723e8c4eeb0622fcf4418d2a 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index d0ad797ebce74a836799105fb5fec3ce29a1f299..888aba0628cb39eef5236725a0aeca43bd00c236 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 0c5bb7cf3ecacac93f311a47c401e0715499641b..96246f79903185392df174fe35a574917d0add03 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 83cf3afe2f45b7aa594adb248291117dd4d87908..f69374fcac9d932e4a1394020422d2fba39f4301 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index b5e7b91da72a1da1ff531151a4fb0cfbb83510cd..f2a7534a2988ff18f6673cd61081c7337589dd3a 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 0e350916220ca936ea4638dc33a6caf25935de20..d8e72efb1600cb89c23c0b25a50473ddfef6cde6 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 85c788d6a6b941156121ff348bde3179b8ec7201..bd27e9d20abb25976bc8c7e74b6a578bc9fc1ae8 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 8656adbe862d5a8419852ea1980dfd7bb9e81b8f..8256668f3d22a0622de9e3708c64173d4eab4ad1 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -34,6 +34,11 @@ class TwitapifavoritesAction extends TwitterapiAction
         $user = $this->get_user($apidata['api_arg'], $apidata);
 
         if (empty($user)) {
+        if ($apidata['content-type'] == 'xml') {
+            $this->show_single_xml_status($notice);
+        } elseif ($apidata['content-type'] == 'json') {
+            $this->show_single_json_status($notice);
+        }
             $this->clientError('Not Found', 404, $apidata['content-type']);
             return;
         }
@@ -56,7 +61,11 @@ class TwitapifavoritesAction extends TwitterapiAction
         $since_id = (int)$this->arg('since_id', 0);
         $since    = $this->arg('since');
 
-        $notice = $user->favoriteNotices(($page-1)*$count, $count);
+        if (!empty($this->auth_user) && $this->auth_user->id == $user->id) {
+            $notice = $user->favoriteNotices(($page-1)*$count, $count, true);
+        } else {
+            $notice = $user->favoriteNotices(($page-1)*$count, $count, false);
+        }
 
         switch($apidata['content-type']) {
         case 'xml':
@@ -91,7 +100,6 @@ class TwitapifavoritesAction extends TwitterapiAction
 
         // Check for RESTfulness
         if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
-            // XXX: Twitter just prints the err msg, no XML / JSON.
             $this->clientError(_('This method requires a POST or DELETE.'),
                 400, $apidata['content-type']);
             return;
@@ -102,10 +110,9 @@ class TwitapifavoritesAction extends TwitterapiAction
             return;
         }
 
-        $user = $apidata['user']; // Always the auth user
-
+        $user      = $apidata['user']; // Always the auth user
         $notice_id = $apidata['api_arg'];
-        $notice = Notice::staticGet($notice_id);
+        $notice    = Notice::staticGet($notice_id);
 
         if (empty($notice)) {
             $this->clientError(_('No status found with that ID.'),
@@ -115,7 +122,7 @@ class TwitapifavoritesAction extends TwitterapiAction
 
         // XXX: Twitter lets you fave things repeatedly via api.
         if ($user->hasFave($notice)) {
-            $this->clientError(_('This notice is already a favorite!'),
+            $this->clientError(_('This status is already a favorite!'),
                 403, $apidata['content-type']);
             return;
         }
@@ -123,7 +130,7 @@ class TwitapifavoritesAction extends TwitterapiAction
         $fave = Fave::addNew($user, $notice);
 
         if (empty($fave)) {
-            $this->serverError(_('Could not create favorite.'));
+            $this->clientError(_('Could not create favorite.'));
             return;
         }
 
@@ -141,7 +148,55 @@ class TwitapifavoritesAction extends TwitterapiAction
     function destroy($args, $apidata)
     {
         parent::handle($args);
-        $this->serverError(_('API method under construction.'), $code=501);
+
+        // Check for RESTfulness
+        if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
+            $this->clientError(_('This method requires a POST or DELETE.'),
+                400, $apidata['content-type']);
+            return;
+        }
+
+        if (!in_array($apidata['content-type'], array('xml', 'json'))) {
+            $this->clientError(_('API method not found!'), $code = 404);
+            return;
+        }
+
+        $user      = $apidata['user']; // Always the auth user
+        $notice_id = $apidata['api_arg'];
+        $notice    = Notice::staticGet($notice_id);
+
+        if (empty($notice)) {
+            $this->clientError(_('No status found with that ID.'),
+                404, $apidata['content-type']);
+            return;
+        }
+
+        $fave            = new Fave();
+        $fave->user_id   = $this->id;
+        $fave->notice_id = $notice->id;
+
+        if (!$fave->find(true)) {
+            $this->clientError(_('That status is not a favorite!'),
+                403, $apidata['content-type']);
+            return;
+        }
+
+        $result = $fave->delete();
+
+        if (!$result) {
+            common_log_db_error($fave, 'DELETE', __FILE__);
+            $this->clientError(_('Could not delete favorite.'), 404);
+            return;
+        }
+
+        $user->blowFavesCache();
+
+        if ($apidata['content-type'] == 'xml') {
+            $this->show_single_xml_status($notice);
+        } elseif ($apidata['content-type'] == 'json') {
+            $this->show_single_json_status($notice);
+        }
+
     }
 
     // XXX: these two funcs swiped from faves.
index b1c277748a97dc9bac59cc9469ec7da9e56d4879..29eb4cc0ff007f49b5346789d60a9a4c0397c814 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index bdef1314af54dfee8f1dfe6161947c77448fcd79..dab2b34f9bd086b0db9c8bd8fc0bc0d63edd4dbb 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 411971af16ab536f1016b5af9a7128931c342bf0..09b11766bb30abece325435adc1691487f9a4f1a 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 94f624afb98deb7a8c14c32047d6313050133580..555c746cbcae6b041369627a89cc61dc1ef721d6 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -75,8 +75,8 @@ class TwitapistatusesAction extends TwitterapiAction
     {
         parent::handle($args);
 
+        $this->auth_user = $apidata['user'];
         $user = $this->get_user($apidata['api_arg'], $apidata);
-        $this->auth_user = $user;
 
         if (empty($user)) {
              $this->clientError(_('No such user!'), 404,
@@ -100,8 +100,13 @@ class TwitapistatusesAction extends TwitterapiAction
         $since_id = (int)$this->arg('since_id', 0);
         $since    = $this->arg('since');
 
-        $notice = $user->noticesWithFriends(($page-1)*$count,
-            $count, $since_id, $max_id,$since);
+        if (!empty($this->auth_user) && $this->auth_user->id == $user->id) {
+            $notice = $user->noticeInbox(($page-1)*$count,
+                $count, $since_id, $max_id, $since);
+        } else {
+            $notice = $user->noticesWithFriends(($page-1)*$count,
+                $count, $since_id, $max_id, $since);
+        }
 
         switch($apidata['content-type']) {
         case 'xml':
index 13a8746cd0301112915b8ec398edceafc825c287..4057b63e740527d82f5624b78eea2e3d56491d0e 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -58,7 +58,7 @@ class TwitapiusersAction extends TwitterapiAction
             return;
         }
 
-        $twitter_user = $this->twitter_user_array($profile, true);
+        $twitter_user = $this->twitter_user_array($user->getProfile(), true);
 
         if ($apidata['content-type'] == 'xml') {
             $this->init_document('xml');
index 8573b2a873e2285492e45ee8fec5e8cbbac9bf6a..05d57c60d5dcdbe59b6b21ba93b7b6775c5827a9 100644 (file)
@@ -12,7 +12,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -118,7 +118,7 @@ class UnblockAction extends Action
         if ($action) {
             common_redirect(common_local_url($action, $args), 303);
         } else {
-            common_redirect(common_local_url('subscriptions',
+            common_redirect(common_local_url('subscribers',
                                              array('nickname' => $cur->nickname)),
                             303);
         }
index 7dcab04c045d2bd3ba6e3bd2ac152e6571b184c2..19275041a81e438b6c4944f177fcb543b1fe5f69 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 08cb31ae037c77c55af08f5017156685ed0076a0..d8b62fb09051d59f55c5ad374b6937dec2f3b0c0 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 1680191495506c043d0cdc98df116d708d0e90c5..8dc2c808d6ff849f0da742e342de137203eeacd4 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 4a985fcd7220c842f6b99efc9dcc597f21e959bd..8b686ae106379be2ac4d5faded06e80123017eed 100644 (file)
@@ -13,7 +13,7 @@
  * @link     http://laconi.ca/
 
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/actions/userdesignsettings.php b/actions/userdesignsettings.php
new file mode 100644 (file)
index 0000000..6e745e9
--- /dev/null
@@ -0,0 +1,206 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Change user password
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Settings
+ * @package   Laconica
+ * @author    Sarven Capadisli <csarven@controlyourself.ca>
+ * @author    Zach Copley <zach@controlyourself.ca>
+ * @copyright 2008-2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/lib/designsettings.php';
+
+class UserDesignSettingsAction extends DesignSettingsAction
+{
+    function prepare($args)
+    {
+        parent::prepare($args);
+        $this->submitaction = common_local_url('userdesignsettings');
+        return true;
+    }
+     
+    /**
+     * Title of the page
+     *
+     * @return string Title of the page
+     */
+
+    function title()
+    {
+        return _('Profile design');
+    }
+
+    /**
+     * Instructions for use
+     *
+     * @return instructions for use
+     */
+
+    function getInstructions()
+    {
+        return _('Customize the way your profile looks ' .
+        'with a background image and a colour palette of your choice.');
+    }
+
+    /**
+     * Get the design we want to edit
+     *
+     * @return Design
+     */
+     
+    function getWorkingDesign() {
+        
+        $user = common_current_user();
+        $design = $user->getDesign();
+
+        if (empty($design)) {
+            $design = $this->defaultDesign();
+        }
+     
+        return $design;
+    }
+    
+    /**
+     * Content area of the page
+     *
+     * Shows a form for changing the design
+     *
+     * @return void
+     */
+     
+    function showContent()
+    {
+        $this->showDesignForm($this->getWorkingDesign());
+    }
+
+    /**
+     * Save or update the user's design settings
+     *
+     * @return void
+     */
+
+    function saveDesign()
+    {
+        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;
+        }
+
+        $onoff = $this->arg('design_background-image_onoff');
+
+        $on   = false;
+        $off  = false;
+        $tile = false;
+
+        if ($onoff == 'on') {
+            $on = true;
+        } else {
+            $off = true;
+        }
+
+        $repeat = $this->boolean('design_background-image_repeat');
+
+        if ($repeat) {
+            $tile = true;
+        }
+
+        $user = common_current_user();
+        $design = $user->getDesign();
+
+        if (!empty($design)) {
+
+            $original = clone($design);
+
+            $design->backgroundcolor = $bgcolor->intValue();
+            $design->contentcolor    = $ccolor->intValue();
+            $design->sidebarcolor    = $sbcolor->intValue();
+            $design->textcolor       = $tcolor->intValue();
+            $design->linkcolor       = $lcolor->intValue();
+
+            $design->setDisposition($on, $off, $tile);
+
+            $result = $design->update($original);
+
+            if ($result === false) {
+                common_log_db_error($design, 'UPDATE', __FILE__);
+                $this->showForm(_('Couldn\'t update your design.'));
+                return;
+            }
+
+            // update design
+        } else {
+
+            $user->query('BEGIN');
+
+            // save new design
+            $design = new Design();
+
+            $design->backgroundcolor = $bgcolor->intValue();
+            $design->contentcolor    = $ccolor->intValue();
+            $design->sidebarcolor    = $sbcolor->intValue();
+            $design->textcolor       = $tcolor->intValue();
+            $design->linkcolor       = $lcolor->intValue();
+
+            $design->setDisposition($on, $off, $tile);
+
+            $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(_('Design preferences saved.'), true);
+    }
+}
index e3088dcbd83d6e26dc51110ecd633f626e78becc..7ead6e6e49172e4941035e0a4c5d433c210f9026 100644 (file)
@@ -46,9 +46,8 @@ require_once INSTALLDIR.'/lib/grouplist.php';
  * @link     http://laconi.ca/
  */
 
-class UsergroupsAction extends Action
+class UsergroupsAction extends OwnerDesignAction
 {
-    var $user = null;
     var $page = null;
     var $profile = null;
 
index 2280509b22942c372be42745684cfbbe9b292c4b..8a940865f947f872e562faad8deb0bc2cb7d5fac 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 1335b6b806653119d6ab6c0ee96c7e4c86ab5eb0..9327a3c83145ecbdf1bc354f790f5e7597e8efdc 100644 (file)
@@ -13,7 +13,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/background/.gitignore b/background/.gitignore
new file mode 100644 (file)
index 0000000..e69de29
index db9d78e47fc1e8f2c137ac6eb9a67d707b2a15fb..5e8b315fe6b419998b3617710d8f01baa8584651 100644 (file)
@@ -55,19 +55,43 @@ class Avatar extends Memcached_DataObject
 
     static function path($filename)
     {
-        return INSTALLDIR . '/avatar/' . $filename;
+        $dir = common_config('avatar', 'dir');
+
+        if ($dir[strlen($dir)-1] != '/') {
+            $dir .= '/';
+        }
+
+        return $dir . $filename;
     }
 
     static function url($filename)
     {
-        return common_path('avatar/'.$filename);
+        $path = common_config('avatar', 'path');
+
+        if ($path[strlen($path)-1] != '/') {
+            $path .= '/';
+        }
+
+        if ($path[0] != '/') {
+            $path = '/'.$path;
+        }
+
+        $server = common_config('avatar', 'server');
+
+        if (empty($server)) {
+            $server = common_config('site', 'server');
+        }
+
+        // XXX: protocol
+
+        return 'http://'.$server.$path.$filename;
     }
 
     function displayUrl()
     {
         $server = common_config('avatar', 'server');
         if ($server) {
-            return 'http://'.$server.'/'.$this->filename;
+            return Avatar::url($this->filename);
         } else {
             return $this->url;
         }
diff --git a/classes/Design.php b/classes/Design.php
new file mode 100644 (file)
index 0000000..da4b670
--- /dev/null
@@ -0,0 +1,155 @@
+<?php
+/*
+ * Laconica - the distributed open-source microblogging tool
+ * Copyright (C) 2009, Control Yourself, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+define('BACKGROUND_ON', 1);
+define('BACKGROUND_OFF', 2);
+define('BACKGROUND_TILE', 4);
+
+/**
+ * Table Definition for design
+ */
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+require_once INSTALLDIR . '/lib/webcolor.php';
+
+class Design extends Memcached_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'design';                          // table name
+    public $id;                              // int(4)  primary_key not_null
+    public $backgroundcolor;                 // int(4)
+    public $contentcolor;                    // int(4)
+    public $sidebarcolor;                    // int(4)
+    public $textcolor;                       // int(4)
+    public $linkcolor;                       // int(4)
+    public $backgroundimage;                 // varchar(255)
+    public $disposition;                     // tinyint(1)   default_1
+
+    /* Static get */
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Design',$k,$v); }
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+
+    function showCSS($out)
+    {
+        try {
+
+            $bgcolor = new WebColor($this->backgroundcolor);
+            $ccolor  = new WebColor($this->contentcolor);
+            $sbcolor = new WebColor($this->sidebarcolor);
+            $tcolor  = new WebColor($this->textcolor);
+            $lcolor  = new WebColor($this->linkcolor);
+
+        } catch (WebColorException $e) {
+            // This shouldn't happen
+            common_log(LOG_ERR, "Unable to create color for design $id.",
+                __FILE__);
+        }
+
+        $css  = 'body { background-color: #' . $bgcolor->hexValue() . ' }' . "\n";
+        $css .= '#content, #site_nav_local_views .current a { background-color: #';
+        $css .= $ccolor->hexValue() . '} '."\n";
+        $css .= '#aside_primary { background-color: #'. $sbcolor->hexValue() . ' }' . "\n";
+        $css .= 'html body { color: #'. $tcolor->hexValue() . ' }'. "\n";
+        $css .= 'a { color: #' . $lcolor->hexValue() . ' }' . "\n";
+
+        if (!empty($this->backgroundimage) &&
+            $this->disposition & BACKGROUND_ON) {
+
+           $repeat = ($this->disposition & BACKGROUND_TILE) ?
+               'background-repeat:repeat;' :
+               'background-repeat:no-repeat;';
+
+            $css .= 'body { background-image:url(' .
+                Design::url($this->backgroundimage) .
+                '); ' . $repeat . ' }' . "\n";
+        }
+
+        $out->element('style', array('type' => 'text/css'), $css);
+
+    }
+
+    static function filename($id, $extension, $extra=null)
+    {
+        return $id . (($extra) ? ('-' . $extra) : '') . $extension;
+    }
+
+    static function path($filename)
+    {
+        $dir = common_config('background', 'dir');
+
+        if ($dir[strlen($dir)-1] != '/') {
+            $dir .= '/';
+        }
+
+        return $dir . $filename;
+    }
+
+    static function url($filename)
+    {
+        $path = common_config('background', 'path');
+
+        if ($path[strlen($path)-1] != '/') {
+            $path .= '/';
+        }
+
+        if ($path[0] != '/') {
+            $path = '/'.$path;
+        }
+
+        $server = common_config('background', 'server');
+
+        if (empty($server)) {
+            $server = common_config('site', 'server');
+        }
+
+        // XXX: protocol
+
+        return 'http://'.$server.$path.$filename;
+    }
+
+    function setDisposition($on, $off, $tile)
+    {
+        if ($on) {
+            $this->disposition |= BACKGROUND_ON;
+        } else {
+            $this->disposition &= ~BACKGROUND_ON;
+        }
+
+        if ($off) {
+            $this->disposition |= BACKGROUND_OFF;
+        } else {
+            $this->disposition &= ~BACKGROUND_OFF;
+        }
+
+        if ($tile) {
+            $this->disposition |= BACKGROUND_TILE;
+        } else {
+            $this->disposition &= ~BACKGROUND_TILE;
+        }
+    }
+
+}
index 572334ce4f8cf17e8811cf0eab51cd77ed922549..f4cf6256ff314af7aca2b5b7867c5dca011b8177 100644 (file)
@@ -37,52 +37,62 @@ class Fave extends Memcached_DataObject
         return Memcached_DataObject::pkeyGet('Fave', $kv);
     }
 
-    function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE)
+    function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $own=false)
     {
         $ids = Notice::stream(array('Fave', '_streamDirect'),
-                              array($user_id),
-                              'fave:ids_by_user:'.$user_id,
+                              array($user_id, $own),
+                              ($own) ? 'fave:ids_by_user_own:'.$user_id :
+                              'fave:by_user:'.$user_id,
                               $offset, $limit);
         return $ids;
     }
 
-    function _streamDirect($user_id, $offset, $limit, $since_id, $max_id, $since)
+    function _streamDirect($user_id, $own, $offset, $limit, $since_id, $max_id, $since)
     {
         $fav = new Fave();
-
-        $fav->user_id = $user_id;
-
-        $fav->selectAdd();
-        $fav->selectAdd('notice_id');
+        $qry = null;
+
+        if ($own) {
+            $qry  = 'SELECT fave.* FROM fave ';
+            $qry .= 'WHERE fave.user_id = ' . $user_id . ' ';
+        } else {
+             $qry =  'SELECT fave.* FROM fave ';
+             $qry .= 'INNER JOIN notice ON fave.notice_id = notice.id ';
+             $qry .= 'WHERE fave.user_id = ' . $user_id . ' ';
+             $qry .= 'AND notice.is_local != ' . NOTICE_GATEWAY . ' ';
+        }
 
         if ($since_id != 0) {
-            $fav->whereAdd('notice_id > ' . $since_id);
+            $qry .= 'AND notice_id > ' . $since_id . ' ';
         }
 
         if ($max_id != 0) {
-            $fav->whereAdd('notice_id <= ' . $max_id);
+            $qry .= 'AND notice_id <= ' . $max_id . ' ';
         }
 
         if (!is_null($since)) {
-            $fav->whereAdd('modified > \'' . date('Y-m-d H:i:s', $since) . '\'');
+            $qry .= 'AND modified > \'' . date('Y-m-d H:i:s', $since) . '\' ';
         }
 
         // NOTE: we sort by fave time, not by notice time!
 
-        $fav->orderBy('modified DESC');
+        $qry .= 'ORDER BY modified DESC ';
 
         if (!is_null($offset)) {
-            $fav->limit($offset, $limit);
+            $qry .= "LIMIT $offset, $limit";
         }
 
+        $fav->query($qry);
+
         $ids = array();
 
-        if ($fav->find()) {
-            while ($fav->fetch()) {
-                $ids[] = $fav->notice_id;
-            }
+        while ($fav->fetch()) {
+            $ids[] = $fav->notice_id;
         }
 
+        $fav->free();
+        unset($fav);
+
         return $ids;
     }
 }
index 24ab11b8eb12d595469c08aa2092dca65d013a6b..5dd7cd8651d832faa6197000627190be09c3946f 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -30,22 +30,24 @@ require_once INSTALLDIR.'/classes/File_to_post.php';
  * Table Definition for file
  */
 
-class File extends Memcached_DataObject 
+class File extends Memcached_DataObject
 {
     ###START_AUTOCODE
     /* the code below is auto generated do not remove the above tag */
 
     public $__table = 'file';                            // table name
-    public $id;                              // int(11)  not_null primary_key group_by
+    public $id;                              // int(4)  primary_key not_null
     public $url;                             // varchar(255)  unique_key
-    public $mimetype;                        // varchar(50)  
-    public $size;                            // int(11)  group_by
-    public $title;                           // varchar(255)  
-    public $date;                            // int(11)  group_by
-    public $protected;                       // int(1)  group_by
+    public $mimetype;                        // varchar(50)
+    public $size;                            // int(4)
+    public $title;                           // varchar(255)
+    public $date;                            // int(4)
+    public $protected;                       // int(4)
+    public $filename;                        // varchar(255)
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
     /* Static get */
-    function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File',$k,$v); }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('File',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
@@ -79,7 +81,6 @@ class File extends Memcached_DataObject
             && ('text/html' === substr($redir_data['type'], 0, 9))
             && ($oembed_data = File_oembed::_getOembed($given_url))
             && isset($oembed_data['json'])) {
-
             File_oembed::saveNew($oembed_data['json'], $file_id);
         }
         return $x;
@@ -90,15 +91,15 @@ class File extends Memcached_DataObject
         $given_url = File_redirection::_canonUrl($given_url);
         if (empty($given_url)) return -1;   // error, no url to process
         $file = File::staticGet('url', $given_url);
-        if (empty($file->id)) {
+        if (empty($file)) {
             $file_redir = File_redirection::staticGet('url', $given_url);
-            if (empty($file_redir->id)) {
+            if (empty($file_redir)) {
+                common_debug("processNew() '$given_url' not a known redirect.\n");
                 $redir_data = File_redirection::where($given_url);
                 $redir_url = $redir_data['url'];
                 if ($redir_url === $given_url) {
                     $x = File::saveNew($redir_data, $given_url);
                     $file_id = $x->id;
-
                 } else {
                     $x = File::processNew($redir_url, $notice_id);
                     $file_id = $x->id;
@@ -116,7 +117,7 @@ class File extends Memcached_DataObject
             $x = File::staticGet($file_id);
             if (empty($x)) die('Impossible!');
         }
-       
+
         File_to_post::processNew($file_id, $notice_id);
         return $x;
     }
@@ -124,8 +125,8 @@ class File extends Memcached_DataObject
     function isRespectsQuota($user) {
         if ($_FILES['attach']['size'] > common_config('attachments', 'file_quota')) {
             return sprintf(_('No file may be larger than %d bytes ' .
-                'and the file you sent was %d bytes. Try to upload a smaller version.'),
-                common_config('attachments', 'file_quota'), $_FILES['attach']['size']);
+                             'and the file you sent was %d bytes. Try to upload a smaller version.'),
+                           common_config('attachments', 'file_quota'), $_FILES['attach']['size']);
         }
 
         $query = "select sum(size) as total from file join file_to_post on file_to_post.file_id = file.id join notice on file_to_post.post_id = notice.id where profile_id = {$user->id} and file.url like '%/notice/%/file'";
@@ -145,5 +146,52 @@ class File extends Memcached_DataObject
         }
         return true;
     }
+
+    // where should the file go?
+
+    static function filename($profile, $basename, $mimetype)
+    {
+        require_once 'MIME/Type/Extension.php';
+        $mte = new MIME_Type_Extension();
+        $ext = $mte->getExtension($mimetype);
+        $nickname = $profile->nickname;
+        $datestamp = strftime('%Y%m%dT%H%M%S', time());
+        $random = strtolower(common_confirmation_code(32));
+        return "$nickname-$datestamp-$random.$ext";
+    }
+
+    static function path($filename)
+    {
+        $dir = common_config('attachments', 'dir');
+
+        if ($dir[strlen($dir)-1] != '/') {
+            $dir .= '/';
+        }
+
+        return $dir . $filename;
+    }
+
+    static function url($filename)
+    {
+        $path = common_config('attachments', 'path');
+
+        if ($path[strlen($path)-1] != '/') {
+            $path .= '/';
+        }
+
+        if ($path[0] != '/') {
+            $path = '/'.$path;
+        }
+
+        $server = common_config('attachments', 'server');
+
+        if (empty($server)) {
+            $server = common_config('site', 'server');
+        }
+
+        // XXX: protocol
+
+        return 'http://'.$server.$path.$filename;
+    }
 }
 
index f1b2cb13c02d90a0b50f2c8bf7e2b0c4d522ebda..69230e4a487dcebd5514420bfebc85dcf4a80fe5 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -25,35 +25,39 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
  * Table Definition for file_oembed
  */
 
-class File_oembed extends Memcached_DataObject 
+class File_oembed extends Memcached_DataObject
 {
     ###START_AUTOCODE
     /* the code below is auto generated do not remove the above tag */
 
     public $__table = 'file_oembed';                     // table name
-    public $id;                              // int(11)  not_null primary_key group_by
-    public $file_id;                         // int(11)  unique_key group_by
-    public $version;                         // varchar(20)  
-    public $type;                            // varchar(20)  
-    public $provider;                        // varchar(50)  
-    public $provider_url;                    // varchar(255)  
-    public $width;                           // int(11)  group_by
-    public $height;                          // int(11)  group_by
-    public $html;                            // blob(65535)  blob
-    public $title;                           // varchar(255)  
-    public $author_name;                     // varchar(50)  
-    public $author_url;                      // varchar(255)  
-    public $url;                             // varchar(255)  
+    public $file_id;                         // int(4)  primary_key not_null
+    public $version;                         // varchar(20)
+    public $type;                            // varchar(20)
+    public $provider;                        // varchar(50)
+    public $provider_url;                    // varchar(255)
+    public $width;                           // int(4)
+    public $height;                          // int(4)
+    public $html;                            // text()
+    public $title;                           // varchar(255)
+    public $author_name;                     // varchar(50)
+    public $author_url;                      // varchar(255)
+    public $url;                             // varchar(255)
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
     /* Static get */
-    function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_oembed',$k,$v); }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('File_oembed',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
 
+    function sequenceKey()
+    {
+        return array(false, false, false);
+    }
 
     function _getOembed($url, $maxwidth = 500, $maxheight = 400, $format = 'json') {
-        $cmd = 'http://oohembed.com/oohembed/?url=' . urlencode($url);
+        $cmd = common_config('oohembed', 'endpoint') . '?url=' . urlencode($url);
         if (is_int($maxwidth)) $cmd .= "&maxwidth=$maxwidth";
         if (is_int($maxheight)) $cmd .= "&maxheight=$maxheight";
         if (is_string($format)) $cmd .= "&format=$format";
@@ -84,4 +88,3 @@ class File_oembed extends Memcached_DataObject
     }
 }
 
-
index 212cc36158ab3783fd6b72f7fbe9acf13eb28a7f..d6fa0bcb6286bc6d13b6527e40b99b92878606db 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -25,31 +25,28 @@ require_once INSTALLDIR.'/classes/File_oembed.php';
 
 define('USER_AGENT', 'Laconica user agent / file probe');
 
-
 /**
  * Table Definition for file_redirection
  */
 
-class File_redirection extends Memcached_DataObject 
+class File_redirection extends Memcached_DataObject
 {
     ###START_AUTOCODE
     /* the code below is auto generated do not remove the above tag */
 
     public $__table = 'file_redirection';                // table name
-    public $id;                              // int(11)  not_null primary_key group_by
-    public $url;                             // varchar(255)  unique_key
-    public $file_id;                         // int(11)  group_by
-    public $redirections;                    // int(11)  group_by
-    public $httpcode;                        // int(11)  group_by
+    public $url;                             // varchar(255)  primary_key not_null
+    public $file_id;                         // int(4)
+    public $redirections;                    // int(4)
+    public $httpcode;                        // int(4)
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
     /* Static get */
-    function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_redirection',$k,$v); }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('File_redirection',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
 
-
-
     function _commonCurl($url, $redirs) {
         $curlh = curl_init();
         curl_setopt($curlh, CURLOPT_URL, $url);
@@ -69,24 +66,18 @@ class File_redirection extends Memcached_DataObject
 
         // let's see if we know this...
         $a = File::staticGet('url', $short_url);
-        if (empty($a->id)) {
+
+        if (!empty($a)) {
+            // this is a direct link to $a->url
+            return $a->url;
+        } else {
             $b = File_redirection::staticGet('url', $short_url);
-            if (empty($b->id)) {
-                // we'll have to figure it out
-            } else {
+            if (!empty($b)) {
                 // this is a redirect to $b->file_id
-                $a = File::staticGet($b->file_id);
-                $url = $a->url;
+                $a = File::staticGet('id', $b->file_id);
+                return $a->url;
             }
-        } else {
-            // this is a direct link to $a->url
-            $url = $a->url;
         }
-        if (isset($url)) {
-            return $url;
-        }
-
-
 
         $curlh = File_redirection::_commonCurl($short_url, $redirs);
         // Don't include body in output
@@ -123,83 +114,22 @@ class File_redirection extends Memcached_DataObject
     }
 
     function makeShort($long_url) {
-        $long_url = File_redirection::_canonUrl($long_url);
-        // do we already know this long_url and have a short redirection for it?
-        $file       = new File;
-        $file_redir = new File_redirection;
-        $file->url  = $long_url;
-        $file->joinAdd($file_redir);
-        $file->selectAdd('length(file_redirection.url) as len');
-        $file->limit(1);
-        $file->orderBy('len');
-        $file->find(true);
-        if (!empty($file->url) && (strlen($file->url) < strlen($long_url))) {
-            return $file->url;
-        }
-
-        // if yet unknown, we must find a short url according to user settings
-        $short_url = File_redirection::_userMakeShort($long_url, common_current_user());
-        return $short_url;
-    }
-
-    function _userMakeShort($long_url, $user) {
-        if (empty($user)) {
-            // common current user does not find a user when called from the XMPP daemon
-            // therefore we'll set one here fix, so that XMPP given URLs may be shortened
-            $user->urlshorteningservice = 'ur1.ca';
-        }
-        $curlh = curl_init();
-        curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait
-        curl_setopt($curlh, CURLOPT_USERAGENT, 'Laconica');
-        curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true);
 
-        switch($user->urlshorteningservice) {
-            case 'ur1.ca':
-                require_once INSTALLDIR.'/lib/Shorturl_api.php';
-                $short_url_service = new LilUrl;
-                $short_url = $short_url_service->shorten($long_url);
-                break;
+        $canon = File_redirection::_canonUrl($long_url);
 
-            case '2tu.us':
-                $short_url_service = new TightUrl;
-                require_once INSTALLDIR.'/lib/Shorturl_api.php';
-                $short_url = $short_url_service->shorten($long_url);
-                break;
+        $short_url = File_redirection::_userMakeShort($canon);
 
-            case 'ptiturl.com':
-                require_once INSTALLDIR.'/lib/Shorturl_api.php';
-                $short_url_service = new PtitUrl;
-                $short_url = $short_url_service->shorten($long_url);
-                break;
-
-            case 'bit.ly':
-                curl_setopt($curlh, CURLOPT_URL, 'http://bit.ly/api?method=shorten&long_url='.urlencode($long_url));
-                $short_url = current(json_decode(curl_exec($curlh))->results)->hashUrl;
-                break;
-
-            case 'is.gd':
-                curl_setopt($curlh, CURLOPT_URL, 'http://is.gd/api.php?longurl='.urlencode($long_url));
-                $short_url = curl_exec($curlh);
-                break;
-            case 'snipr.com':
-                curl_setopt($curlh, CURLOPT_URL, 'http://snipr.com/site/snip?r=simple&link='.urlencode($long_url));
-                $short_url = curl_exec($curlh);
-                break;
-            case 'metamark.net':
-                curl_setopt($curlh, CURLOPT_URL, 'http://metamark.net/api/rest/simple?long_url='.urlencode($long_url));
-                $short_url = curl_exec($curlh);
-                break;
-            case 'tinyurl.com':
-                curl_setopt($curlh, CURLOPT_URL, 'http://tinyurl.com/api-create.php?url='.urlencode($long_url));
-                $short_url = curl_exec($curlh);
-                break;
-            default:
-                $short_url = false;
+        // Did we get one? Is it shorter?
+        if (!empty($short_url) && mb_strlen($short_url) < mb_strlen($long_url)) {
+            return $short_url;
+        } else {
+            return $long_url;
         }
+    }
 
-        curl_close($curlh);
-
-        if ($short_url) {
+    function _userMakeShort($long_url) {
+        $short_url = common_shorten_url($long_url);
+        if (!empty($short_url) && $short_url != $long_url) {
             $short_url = (string)$short_url;
             // store it
             $file = File::staticGet('url', $long_url);
@@ -222,7 +152,7 @@ class File_redirection extends Memcached_DataObject
             }
             return $short_url;
         }
-        return $long_url;
+        return null;
     }
 
     function _canonUrl($in_url, $default_scheme = 'http://') {
index 1a65b92c9018dc2464e82e3a9beb294bbd395cd9..44b92a2fadd005c4be6282a100a55f0ee12200fc 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -25,24 +25,29 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
  * Table Definition for file_thumbnail
  */
 
-class File_thumbnail extends Memcached_DataObject 
+class File_thumbnail extends Memcached_DataObject
 {
     ###START_AUTOCODE
     /* the code below is auto generated do not remove the above tag */
 
     public $__table = 'file_thumbnail';                  // table name
-    public $id;                              // int(11)  not_null primary_key group_by
-    public $file_id;                         // int(11)  unique_key group_by
+    public $file_id;                         // int(4)  primary_key not_null
     public $url;                             // varchar(255)  unique_key
-    public $width;                           // int(11)  group_by
-    public $height;                          // int(11)  group_by
+    public $width;                           // int(4)
+    public $height;                          // int(4)
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
     /* Static get */
-    function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_thumbnail',$k,$v); }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('File_thumbnail',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
 
+    function sequenceKey()
+    {
+        return array(false, false, false);
+    }
+
     function saveNew($data, $file_id) {
         $tn = new File_thumbnail;
         $tn->file_id = $file_id;
index 00ddebe6b87e748a705ece9f0962f56a4335c91d..d35febb77873af9503cdbe74460882d543e2a15f 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -25,18 +25,18 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
  * Table Definition for file_to_post
  */
 
-class File_to_post extends Memcached_DataObject 
+class File_to_post extends Memcached_DataObject
 {
     ###START_AUTOCODE
     /* the code below is auto generated do not remove the above tag */
 
     public $__table = 'file_to_post';                    // table name
-    public $id;                              // int(11)  not_null primary_key group_by
-    public $file_id;                         // int(11)  multiple_key group_by
-    public $post_id;                         // int(11)  group_by
+    public $file_id;                         // int(4)  primary_key not_null
+    public $post_id;                         // int(4)  primary_key not_null
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
     /* Static get */
-    function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('File_to_post',$k,$v); }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('File_to_post',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
@@ -44,17 +44,27 @@ class File_to_post extends Memcached_DataObject
     function processNew($file_id, $notice_id) {
         static $seen = array();
         if (empty($seen[$notice_id]) || !in_array($file_id, $seen[$notice_id])) {
-            $f2p = new File_to_post;
-            $f2p->file_id = $file_id;
-            $f2p->post_id = $notice_id;
-            $f2p->insert();
+
+            $f2p = File_to_post::pkeyGet(array('post_id' => $notice_id,
+                                               'file_id' => $file_id));
+            if (empty($f2p)) {
+                $f2p = new File_to_post;
+                $f2p->file_id = $file_id;
+                $f2p->post_id = $notice_id;
+                $f2p->insert();
+            }
+
             if (empty($seen[$notice_id])) {
                 $seen[$notice_id] = array($file_id);
             } else {
                 $seen[$notice_id][] = $file_id;
             }
         }
+    }
 
+    function &pkeyGet($kv)
+    {
+        return Memcached_DataObject::pkeyGet('File_to_post', $kv);
     }
 }
 
index 61727abe5ed5924d6a8a7bb41eb721a2d672cefc..8b3e03dfb33f3d7f5c1e39cdf2d04160c72d07de 100644 (file)
@@ -4,42 +4,41 @@
  */
 require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
 
-class Foreign_user extends Memcached_DataObject 
+class Foreign_user extends Memcached_DataObject
 {
     ###START_AUTOCODE
     /* the code below is auto generated do not remove the above tag */
 
     public $__table = 'foreign_user';                    // table name
-    public $id;                              // int(4)  primary_key not_null
+    public $id;                              // bigint(8)  primary_key not_null
     public $service;                         // int(4)  primary_key not_null
     public $uri;                             // varchar(255)  unique_key not_null
-    public $nickname;                        // varchar(255)  
+    public $nickname;                        // varchar(255)
     public $created;                         // datetime()   not_null
     public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
     /* Static get */
-    function staticGet($k,$v=null)
-    { return Memcached_DataObject::staticGet('Foreign_user',$k,$v); }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Foreign_user',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
-    
+
     // XXX:  This only returns a 1->1 single obj mapping.  Change?  Or make
     // a getForeignUsers() that returns more than one? --Zach
-    static function getForeignUser($id, $service) {        
+    static function getForeignUser($id, $service) {
         $fuser = new Foreign_user();
         $fuser->whereAdd("service = $service");
         $fuser->whereAdd("id = $id");
         $fuser->limit(1);
-        
+
         if ($fuser->find()) {
             $fuser->fetch();
             return $fuser;
         }
-        
-        return null;        
+
+        return null;
     }
-    
+
     function updateKeys(&$orig)
     {
         $parts = array();
@@ -68,5 +67,4 @@ class Foreign_user extends Memcached_DataObject
         return $result;
     }
 
-    
 }
diff --git a/classes/Group_alias.php b/classes/Group_alias.php
new file mode 100644 (file)
index 0000000..e801e50
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Table Definition for group_alias
+ *
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2009, Control Yourself, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('LACONICA')) { exit(1); }
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Group_alias extends Memcached_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'group_alias';                     // table name
+    public $alias;                           // varchar(64)  primary_key not_null
+    public $group_id;                        // int(4)   not_null
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    /* Static get */
+    function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('Group_alias',$k,$v); }
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+}
diff --git a/classes/Group_block.php b/classes/Group_block.php
new file mode 100644 (file)
index 0000000..7922c19
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+/**
+ * Table Definition for group_block
+ *
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('LACONICA')) { exit(1); }
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Group_block extends Memcached_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'group_block';                     // table name
+    public $group_id;                        // int(4)  primary_key not_null
+    public $blocked;                         // int(4)  primary_key not_null
+    public $blocker;                         // int(4)   not_null
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    /* Static get */
+    function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('Group_block',$k,$v); }
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+
+    function &pkeyGet($kv)
+    {
+        return Memcached_DataObject::pkeyGet('Group_block', $kv);
+    }
+
+    static function isBlocked($group, $profile)
+    {
+        $block = Group_block::pkeyGet(array('group_id' => $group->id,
+                                            'blocked' => $profile->id));
+        return !empty($block);
+    }
+
+    static function blockProfile($group, $profile, $blocker)
+    {
+        // Insert the block
+
+        $block = new Group_block();
+
+        $block->query('BEGIN');
+
+        $block->group_id = $group->id;
+        $block->blocked  = $profile->id;
+        $block->blocker  = $blocker->id;
+
+        $result = $block->insert();
+
+        if (!$result) {
+            common_log_db_error($block, 'INSERT', __FILE__);
+            return null;
+        }
+
+        // Delete membership if any
+
+        $member = new Group_member();
+
+        $member->group_id   = $group->id;
+        $member->profile_id = $profile->id;
+
+        if ($member->find(true)) {
+            $result = $member->delete();
+            if (!$result) {
+                common_log_db_error($member, 'DELETE', __FILE__);
+                return null;
+            }
+        }
+
+        // Commit, since both have been done
+
+        $block->query('COMMIT');
+
+        return $block;
+    }
+
+    static function unblockProfile($group, $profile)
+    {
+        $block = Group_block::pkeyGet(array('group_id' => $group->id,
+                                            'blocked' => $profile->id));
+
+        if (empty($block)) {
+            return null;
+        }
+
+        $result = $block->delete();
+
+        if (!$result) {
+            common_log_db_error($block, 'DELETE', __FILE__);
+            return null;
+        }
+
+        return true;
+    }
+
+}
index b80ba42729d8b84cfb0a9186584721406d6355d7..1af7439f7f749e668824773aa6363012e77e33f2 100644 (file)
@@ -14,8 +14,14 @@ class Group_inbox extends Memcached_DataObject
     public $created;                         // datetime()   not_null
 
     /* Static get */
+
     function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Group_inbox',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
+
+    function &pkeyGet($kv)
+    {
+        return Memcached_DataObject::pkeyGet('Group_inbox', $kv);
+    }
 }
index 33ac70dd045643a5a57ff87e58d383a0bc52bd2b..f7cbb9d5b6c375b50f9c7dbc46b3a8eb2496e80b 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -193,7 +193,14 @@ class Memcached_DataObject extends DB_DataObject
                 // unable to connect to sphinx' search daemon
                 if (!$connected) {
                     if ('mysql' === common_config('db', 'type')) {
-                        $search_engine = new MySQLSearch($this, $table);
+                        $type = common_config('search', 'type');
+                        if ($type == 'like') {
+                            $search_engine = new MySQLLikeSearch($this, $table);
+                        } else if ($type == 'fulltext') {
+                            $search_engine = new MySQLSearch($this, $table);
+                        } else {
+                            throw new ServerException('Unknown search type: ' . $type);
+                        }
                     } else {
                         $search_engine = new PGSearch($this, $table);
                     }
@@ -242,13 +249,16 @@ class Memcached_DataObject extends DB_DataObject
             if (common_config('db', 'type') == 'mysql' &&
                 common_config('db', 'utf8')) {
                 $conn = $DB->connection;
-                if ($DB instanceof DB_mysqli) {
-                    mysqli_set_charset($conn, 'utf8');
-                } else if ($DB instanceof DB_mysql) {
-                    mysql_set_charset('utf8', $conn);
+                if (!empty($conn)) {
+                    if ($DB instanceof DB_mysqli) {
+                        mysqli_set_charset($conn, 'utf8');
+                    } else if ($DB instanceof DB_mysql) {
+                        mysql_set_charset('utf8', $conn);
+                    }
                 }
             }
         }
         return $result;
     }
+
 }
index 1c4858149269c6d78a25006cff0b3a3796ec9f49..fdcef1bc2ed6de3e17a82e505abe90b2aeb5eebf 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -29,6 +29,11 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
 
 define('NOTICE_CACHE_WINDOW', 61);
 
+define('NOTICE_LOCAL_PUBLIC', 1);
+define('NOTICE_REMOTE_OMB', 0);
+define('NOTICE_LOCAL_NONPUBLIC', -1);
+define('NOTICE_GATEWAY', -2);
+
 class Notice extends Memcached_DataObject
 {
     ###START_AUTOCODE
@@ -125,7 +130,12 @@ class Notice extends Memcached_DataObject
 
         $profile = Profile::staticGet($profile_id);
 
-        $final =  common_shorten_links($content);
+        $final = common_shorten_links($content);
+
+        if (mb_strlen($final) > 140) {
+            common_log(LOG_INFO, 'Rejecting notice that is too long.');
+            return _('Problem saving notice. Too long.');
+        }
 
         if (!$profile) {
             common_log(LOG_ERR, 'Problem saving notice. Unknown user.');
@@ -212,6 +222,13 @@ class Notice extends Memcached_DataObject
 
             $notice->addToInboxes();
             $notice->saveGroups();
+            $notice->saveUrls();
+            $orig2 = clone($notice);
+               $notice->rendered = common_render_content($final, $notice);
+            if (!$notice->update($orig2)) {
+                common_log_db_error($notice, 'UPDATE', __FILE__);
+                return _('Problem saving notice.');
+            }
 
             $notice->query('COMMIT');
 
@@ -226,6 +243,22 @@ class Notice extends Memcached_DataObject
         return $notice;
     }
 
+    /** save all urls in the notice to the db
+     *
+     * follow redirects and save all available file information
+     * (mimetype, date, size, oembed, etc.)
+     *
+     * @return void
+     */
+    function saveUrls() {
+        common_replace_urls_callback($this->content, array($this, 'saveUrl'), $this->id);
+    }
+
+    function saveUrl($data) {
+        list($url, $notice_id) = $data;
+        File::processNew($url, $notice_id);
+    }
+
     static function checkDupes($profile_id, $content) {
         $profile = Profile::staticGet($profile_id);
         if (!$profile) {
@@ -298,6 +331,20 @@ class Notice extends Memcached_DataObject
         return $n_attachments;
     }
 
+    function attachments() {
+        // XXX: cache this
+        $att = array();
+        $f2p = new File_to_post;
+        $f2p->post_id = $this->id;
+        if ($f2p->find()) {
+            while ($f2p->fetch()) {
+                $f = File::staticGet($f2p->file_id);
+                $att[] = clone($f);
+            }
+        }
+        return $att;
+    }
+
     function blowCaches($blowLast=false)
     {
         $this->blowSubsCache($blowLast);
@@ -306,6 +353,19 @@ class Notice extends Memcached_DataObject
         $this->blowPublicCache($blowLast);
         $this->blowTagCache($blowLast);
         $this->blowGroupCache($blowLast);
+        $this->blowConversationCache($blowLast);
+    }
+
+    function blowConversationCache($blowLast=false)
+    {
+        $cache = common_memcache();
+        if ($cache) {
+            $ck = common_cache_key('notice:conversation_ids:'.$this->conversation);
+            $cache->delete($ck);
+            if ($blowLast) {
+                $cache->delete($ck.';last');
+            }
+        }
     }
 
     function blowGroupCache($blowLast=false)
@@ -346,6 +406,12 @@ class Notice extends Memcached_DataObject
             if ($tag->find()) {
                 while ($tag->fetch()) {
                     $tag->blowCache($blowLast);
+                    $ck = 'profile:notice_ids_tagged:' . $this->profile_id . ':' . $tag->tag;
+
+                    $cache->delete($ck);
+                    if ($blowLast) {
+                        $cache->delete($ck . ';last');
+                    }
                 }
             }
             $tag->free();
@@ -367,8 +433,10 @@ class Notice extends Memcached_DataObject
 
             while ($user->fetch()) {
                 $cache->delete(common_cache_key('notice_inbox:by_user:'.$user->id));
+                $cache->delete(common_cache_key('notice_inbox:by_user_own:'.$user->id));
                 if ($blowLast) {
                     $cache->delete(common_cache_key('notice_inbox:by_user:'.$user->id.';last'));
+                    $cache->delete(common_cache_key('notice_inbox:by_user_own:'.$user->id.';last'));
                 }
             }
             $user->free();
@@ -430,8 +498,10 @@ class Notice extends Memcached_DataObject
             if ($fave->find()) {
                 while ($fave->fetch()) {
                     $cache->delete(common_cache_key('fave:ids_by_user:'.$fave->user_id));
+                    $cache->delete(common_cache_key('fave:by_user_own:'.$fave->user_id));
                     if ($blowLast) {
                         $cache->delete(common_cache_key('fave:ids_by_user:'.$fave->user_id.';last'));
+                        $cache->delete(common_cache_key('fave:by_user_own:'.$fave->user_id.';last'));
                     }
                 }
             }
@@ -634,7 +704,10 @@ class Notice extends Memcached_DataObject
         if (!empty($cache)) {
             $notices = array();
             foreach ($ids as $id) {
-                $notices[] = Notice::staticGet('id', $id);
+                $n = Notice::staticGet('id', $id);
+                if (!empty($n)) {
+                    $notices[] = $n;
+                }
             }
             return new ArrayWrapper($notices);
         } else {
@@ -703,29 +776,116 @@ class Notice extends Memcached_DataObject
         return $ids;
     }
 
+    function conversationStream($id, $offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
+    {
+        $ids = Notice::stream(array('Notice', '_conversationStreamDirect'),
+                              array($id),
+                              'notice:conversation_ids:'.$id,
+                              $offset, $limit, $since_id, $max_id, $since);
+
+        return Notice::getStreamByIds($ids);
+    }
+
+    function _conversationStreamDirect($id, $offset=0, $limit=20, $since_id=0, $max_id=0, $since=null)
+    {
+        $notice = new Notice();
+
+        $notice->selectAdd(); // clears it
+        $notice->selectAdd('id');
+
+        $notice->whereAdd('conversation = '.$id);
+
+        $notice->orderBy('id DESC');
+
+        if (!is_null($offset)) {
+            $notice->limit($offset, $limit);
+        }
+
+        if ($since_id != 0) {
+            $notice->whereAdd('id > ' . $since_id);
+        }
+
+        if ($max_id != 0) {
+            $notice->whereAdd('id <= ' . $max_id);
+        }
+
+        if (!is_null($since)) {
+            $notice->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\'');
+        }
+
+        $ids = array();
+
+        if ($notice->find()) {
+            while ($notice->fetch()) {
+                $ids[] = $notice->id;
+            }
+        }
+
+        $notice->free();
+        $notice = NULL;
+
+        return $ids;
+    }
+
     function addToInboxes()
     {
         $enabled = common_config('inboxes', 'enabled');
 
         if ($enabled === true || $enabled === 'transitional') {
-            $inbox = new Notice_inbox();
-            $UT = common_config('db','type')=='pgsql'?'"user"':'user';
-            $qry = 'INSERT INTO notice_inbox (user_id, notice_id, created) ' .
-              "SELECT $UT.id, " . $this->id . ", '" . $this->created . "' " .
-              "FROM $UT JOIN subscription ON $UT.id = subscription.subscriber " .
-              'WHERE subscription.subscribed = ' . $this->profile_id . ' ' .
-              'AND NOT EXISTS (SELECT user_id, notice_id ' .
-              'FROM notice_inbox ' .
-              "WHERE user_id = $UT.id " .
-              'AND notice_id = ' . $this->id . ' )';
-            if ($enabled === 'transitional') {
-                $qry .= " AND $UT.inboxed = 1";
+
+            $users = $this->getSubscribedUsers();
+
+            // FIXME: kind of ignoring 'transitional'...
+            // we'll probably stop supporting inboxless mode
+            // in 0.9.x
+
+            foreach ($users as $id) {
+                $this->addToUserInbox($id, NOTICE_INBOX_SOURCE_SUB);
             }
-            $inbox->query($qry);
         }
+
         return;
     }
 
+    function getSubscribedUsers()
+    {
+        $user = new User();
+
+        $qry =
+          'SELECT id ' .
+          'FROM user JOIN subscription '.
+          'ON user.id = subscription.subscriber ' .
+          'WHERE subscription.subscribed = %d ';
+
+        $user->query(sprintf($qry, $this->profile_id));
+
+        $ids = array();
+
+        while ($user->fetch()) {
+            $ids[] = $user->id;
+        }
+
+        $user->free();
+
+        return $ids;
+    }
+
+    function addToUserInbox($user_id, $source)
+    {
+        $inbox = Notice_inbox::pkeyGet(array('user_id' => $user_id,
+                                             'notice_id' => $this->id));
+        if (empty($inbox)) {
+            $inbox = new Notice_inbox();
+            $inbox->user_id   = $user_id;
+            $inbox->notice_id = $this->id;
+            $inbox->source    = $source;
+            $inbox->created   = $this->created;
+            return $inbox->insert();
+        }
+
+        return true;
+    }
+
     function saveGroups()
     {
         $enabled = common_config('inboxes', 'enabled');
@@ -747,16 +907,16 @@ class Notice extends Memcached_DataObject
 
         foreach (array_unique($match[1]) as $nickname) {
             /* XXX: remote groups. */
-            $group = User_group::staticGet('nickname', $nickname);
+            $group = User_group::getForNickname($nickname);
 
-            if (!$group) {
+            if (empty($group)) {
                 continue;
             }
 
             // we automatically add a tag for every group name, too
 
             $tag = Notice_tag::pkeyGet(array('tag' => common_canonical_tag($nickname),
-                                           'notice_id' => $this->id));
+                                             'notice_id' => $this->id));
 
             if (is_null($tag)) {
                 $this->saveTag($nickname);
@@ -764,13 +924,7 @@ class Notice extends Memcached_DataObject
 
             if ($profile->isMember($group)) {
 
-                $gi = new Group_inbox();
-
-                $gi->group_id  = $group->id;
-                $gi->notice_id = $this->id;
-                $gi->created   = common_sql_now();
-
-                $result = $gi->insert();
+                $result = $this->addToGroupInbox($group);
 
                 if (!$result) {
                     common_log_db_error($gi, 'INSERT', __FILE__);
@@ -778,27 +932,37 @@ class Notice extends Memcached_DataObject
 
                 // FIXME: do this in an offline daemon
 
-                $this->addToGroupInboxes($group);
+                $this->addToGroupMemberInboxes($group);
             }
         }
     }
 
-    function addToGroupInboxes($group)
+    function addToGroupInbox($group)
     {
-        $inbox = new Notice_inbox();
-        $UT = common_config('db','type')=='pgsql'?'"user"':'user';
-        $qry = 'INSERT INTO notice_inbox (user_id, notice_id, created, source) ' .
-          "SELECT $UT.id, " . $this->id . ", '" . $this->created . "', 2 " .
-          "FROM $UT JOIN group_member ON $UT.id = group_member.profile_id " .
-          'WHERE group_member.group_id = ' . $group->id . ' ' .
-          'AND NOT EXISTS (SELECT user_id, notice_id ' .
-          'FROM notice_inbox ' .
-          "WHERE user_id = $UT.id " .
-          'AND notice_id = ' . $this->id . ' )';
-        if ($enabled === 'transitional') {
-            $qry .= " AND $UT.inboxed = 1";
-        }
-        $result = $inbox->query($qry);
+        $gi = Group_inbox::pkeyGet(array('group_id' => $group->id,
+                                         'notice_id' => $this->id));
+
+        if (empty($gi)) {
+
+            $gi = new Group_inbox();
+
+            $gi->group_id  = $group->id;
+            $gi->notice_id = $this->id;
+            $gi->created   = $this->created;
+
+            return $gi->insert();
+        }
+
+        return true;
+    }
+
+    function addToGroupMemberInboxes($group)
+    {
+        $users = $group->getUserMembers();
+
+        foreach ($users as $id) {
+            $this->addToUserInbox($id, NOTICE_INBOX_SOURCE_GROUP);
+        }
     }
 
     function saveReplies()
index 673e187c7296694bbd228ef02f0e9ae3d6671298..940381f84cd1e258ba0a8563ea79bae62c6b4b8a 100644 (file)
@@ -25,6 +25,11 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
 
 define('INBOX_CACHE_WINDOW', 101);
 
+define('NOTICE_INBOX_SOURCE_SUB', 1);
+define('NOTICE_INBOX_SOURCE_GROUP', 2);
+define('NOTICE_INBOX_SOURCE_REPLY', 3);
+define('NOTICE_INBOX_SOURCE_GATEWAY', -1);
+
 class Notice_inbox extends Memcached_DataObject
 {
     ###START_AUTOCODE
@@ -43,20 +48,25 @@ class Notice_inbox extends Memcached_DataObject
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
 
-    function stream($user_id, $offset, $limit, $since_id, $max_id, $since)
+    function stream($user_id, $offset, $limit, $since_id, $max_id, $since, $own=false)
     {
         return Notice::stream(array('Notice_inbox', '_streamDirect'),
-                              array($user_id),
-                              'notice_inbox:by_user:'.$user_id,
+                              array($user_id, $own),
+                              ($own) ? 'notice_inbox:by_user:'.$user_id :
+                              'notice_inbox:by_user_own:'.$user_id,
                               $offset, $limit, $since_id, $max_id, $since);
     }
 
-    function _streamDirect($user_id, $offset, $limit, $since_id, $max_id, $since)
+    function _streamDirect($user_id, $own, $offset, $limit, $since_id, $max_id, $since)
     {
         $inbox = new Notice_inbox();
 
         $inbox->user_id = $user_id;
 
+        if (!$own) {
+            $inbox->whereAdd('source != ' . NOTICE_INBOX_SOURCE_GATEWAY);
+        }
+
         if ($since_id != 0) {
             $inbox->whereAdd('notice_id > ' . $since_id);
         }
index 758a6659473ec491bd625758ac697090e74aa9b8..4e52ef2697acdb1bd3bbebac196d1fabea8e8e02 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 4a459b9740e4055b5a62c9349ba689b9dc9598fd..a0ed6b3ca349e0da83dc161a409893f821cdb8af 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -153,18 +153,16 @@ class Profile extends Memcached_DataObject
         return null;
     }
 
-    function getTaggedNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null, $tag=null)
+    function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0, $since=null)
     {
-        // XXX: I'm not sure this is going to be any faster. It probably isn't.
         $ids = Notice::stream(array($this, '_streamTaggedDirect'),
-                              array(),
-                              'profile:notice_ids:' . $this->id,
-                              $offset, $limit, $since_id, $before_id, $since, $tag);
-        common_debug(print_r($ids, true));
+                              array($tag),
+                              'profile:notice_ids_tagged:' . $this->id . ':' . $tag,
+                              $offset, $limit, $since_id, $max_id, $since);
         return Notice::getStreamByIds($ids);
     }
 
-    function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
+    function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0, $since=null)
     {
         // XXX: I'm not sure this is going to be any faster. It probably isn't.
         $ids = Notice::stream(array($this, '_streamDirect'),
@@ -175,18 +173,23 @@ class Profile extends Memcached_DataObject
         return Notice::getStreamByIds($ids);
     }
 
-    function _streamTaggedDirect($offset, $limit, $since_id, $before_id, $since=null, $tag=null)
+    function _streamTaggedDirect($tag, $offset, $limit, $since_id, $max_id, $since)
     {
-        common_debug('_streamTaggedDirect()');
+        // XXX It would be nice to do this without a join
+
         $notice = new Notice();
-        $notice->profile_id = $this->id;
-        $query = "select id from notice join notice_tag on id=notice_id where tag='" . $notice->escape($tag) . "' and profile_id=" . $notice->escape($notice->profile_id);
+
+        $query =
+          "select id from notice join notice_tag on id=notice_id where tag='".
+          $notice->escape($tag) .
+          "' and profile_id=" . $notice->escape($this->id);
+
         if ($since_id != 0) {
             $query .= " and id > $since_id";
         }
 
-        if ($before_id != 0) {
-            $query .= " and id < $before_id";
+        if ($max_id != 0) {
+            $query .= " and id < $max_id";
         }
 
         if (!is_null($since)) {
@@ -198,21 +201,19 @@ class Profile extends Memcached_DataObject
         if (!is_null($offset)) {
             $query .= " limit $offset, $limit";
         }
+
         $notice->query($query);
+
         $ids = array();
 
         while ($notice->fetch()) {
-            common_debug(print_r($notice, true));
             $ids[] = $notice->id;
         }
 
         return $ids;
     }
 
-
-
-
-    function _streamDirect($offset, $limit, $since_id, $before_id, $since = null)
+    function _streamDirect($offset, $limit, $since_id, $max_id, $since = null)
     {
         $notice = new Notice();
 
@@ -288,4 +289,52 @@ class Profile extends Memcached_DataObject
             return Avatar::defaultImage($size);
         }
     }
+
+    function getSubscriptions($offset=0, $limit=null)
+    {
+        $qry =
+          'SELECT profile.* ' .
+          'FROM profile JOIN subscription ' .
+          'ON profile.id = subscription.subscribed ' .
+          'WHERE subscription.subscriber = %d ' .
+          'AND subscription.subscribed != subscription.subscriber ' .
+          'ORDER BY subscription.created DESC ';
+
+        if (common_config('db','type') == 'pgsql') {
+            $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
+        } else {
+            $qry .= ' LIMIT ' . $offset . ', ' . $limit;
+        }
+
+        $profile = new Profile();
+
+        $profile->query(sprintf($qry, $this->id));
+
+        return $profile;
+    }
+
+    function getSubscribers($offset=0, $limit=null)
+    {
+        $qry =
+          'SELECT profile.* ' .
+          'FROM profile JOIN subscription ' .
+          'ON profile.id = subscription.subscriber ' .
+          'WHERE subscription.subscribed = %d ' .
+          'AND subscription.subscribed != subscription.subscriber ' .
+          'ORDER BY subscription.created DESC ';
+
+        if ($offset) {
+            if (common_config('db','type') == 'pgsql') {
+                $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
+            } else {
+                $qry .= ' LIMIT ' . $offset . ', ' . $limit;
+            }
+        }
+
+        $profile = new Profile();
+
+        $cnt = $profile->query(sprintf($qry, $this->id));
+
+        return $profile;
+    }
 }
index 551e690e24a57e55c8850fb526be1848e31f4dc8..feadea42da8e474ca65b4f805d0fc77b6cab424a 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 5aa6d913e5949465ff3b1db84684b403f8324d33..975852dd9a5fe73db00870ee95af904259b50605 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index f7747f71d762b1e4aa6950a11611885353f17cbc..f8d6756b69090c4c51e7127ba2497e4b0e1a6444 100644 (file)
@@ -1,8 +1,26 @@
 <?php
 /**
  * Table Definition for status_network
+ *
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2009, Control Yourself, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+if (!defined('LACONICA')) { exit(1); }
+
 class Status_network extends DB_DataObject
 {
     ###START_AUTOCODE
@@ -12,11 +30,13 @@ class Status_network extends DB_DataObject
     public $nickname;                        // varchar(64)  primary_key not_null
     public $hostname;                        // varchar(255)  unique_key
     public $pathname;                        // varchar(255)  unique_key
-    public $sitename;                        // varchar(255)
     public $dbhost;                          // varchar(255)
     public $dbuser;                          // varchar(255)
     public $dbpass;                          // varchar(255)
     public $dbname;                          // varchar(255)
+    public $sitename;                        // varchar(255)
+    public $theme;                           // varchar(255)
+    public $logo;                            // varchar(255)
     public $created;                         // datetime()   not_null
     public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
@@ -26,7 +46,10 @@ class Status_network extends DB_DataObject
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
 
-    static function setupDB($dbhost, $dbuser, $dbpass, $dbname)
+    static $cache = null;
+    static $base = null;
+
+    static function setupDB($dbhost, $dbuser, $dbpass, $dbname, $servers)
     {
         global $config;
 
@@ -34,28 +57,134 @@ class Status_network extends DB_DataObject
         $config['db']['ini_'.$dbname] = INSTALLDIR.'/classes/statusnet.ini';
         $config['db']['table_status_network'] = $dbname;
 
-        return true;
+        self::$cache = new Memcache();
+
+        if (is_array($servers)) {
+            foreach($servers as $server) {
+                self::$cache->addServer($server);
+            }
+        } else {
+            self::$cache->addServer($servers);
+        }
+
+        self::$base = $dbname;
+    }
+
+    static function cacheKey($k, $v) {
+        return 'laconica:' . self::$base . ':status_network:'.$k.':'.$v;
+    }
+
+    static function memGet($k, $v)
+    {
+        $ck = self::cacheKey($k, $v);
+
+        $sn = self::$cache->get($ck);
+
+        if (empty($sn)) {
+            $sn = self::staticGet($k, $v);
+            if (!empty($sn)) {
+                self::$cache->set($ck, $sn);
+            }
+        }
+
+        return $sn;
     }
 
-    static function setupSite($servername, $pathname)
+    function decache()
+    {
+        $keys = array('nickname', 'hostname', 'pathname');
+        foreach ($keys as $k) {
+            $ck = self::cacheKey($k, $this->$k);
+            self::$cache->delete($ck);
+        }
+    }
+
+    function update($orig=null)
+    {
+        if (is_object($orig)) {
+            $orig->decache(); # might be different keys
+        }
+        return parent::update($orig);
+    }
+
+    function delete()
+    {
+        $this->decache(); # while we still have the values!
+        return parent::delete();
+    }
+
+    static function setupSite($servername, $pathname, $wildcard)
     {
         global $config;
 
-        $parts = explode('.', $servername);
+        $sn = null;
 
-        $sn = Status_network::staticGet('nickname', $parts[0]);
+        // XXX I18N, probably not crucial for hostnames
+        // XXX This probably needs a tune up
+
+        if (0 == strncasecmp(strrev($wildcard), strrev($servername), strlen($wildcard))) {
+            // special case for exact match
+            if (0 == strcasecmp($servername, $wildcard)) {
+                $sn = self::memGet('nickname', '');
+            } else {
+                $parts = explode('.', $servername);
+                $sn = self::memGet('nickname', strtolower($parts[0]));
+            }
+        } else {
+            $sn = self::memGet('hostname', strtolower($servername));
+        }
 
         if (!empty($sn)) {
+            if (!empty($sn->hostname) && 0 != strcasecmp($sn->hostname, $servername)) {
+                $sn->redirectToHostname();
+            }
             $dbhost = (empty($sn->dbhost)) ? 'localhost' : $sn->dbhost;
             $dbuser = (empty($sn->dbuser)) ? $sn->nickname : $sn->dbuser;
             $dbpass = $sn->dbpass;
             $dbname = (empty($sn->dbname)) ? $sn->nickname : $sn->dbname;
 
             $config['db']['database'] = "mysqli://$dbuser:$dbpass@$dbhost/$dbname";
+
             $config['site']['name'] = $sn->sitename;
-            return true;
+
+            if (!empty($sn->theme)) {
+                $config['site']['theme'] = $sn->theme;
+            }
+            if (!empty($sn->logo)) {
+                $config['site']['logo'] = $sn->logo;
+            }
+
+            return $sn;
         } else {
+            return null;
+        }
+    }
+
+    // Code partially mooked from http://www.richler.de/en/php-redirect/
+    // (C) 2006 by Heiko Richler  http://www.richler.de/
+    // LGPL
+
+    function redirectToHostname()
+    {
+        $destination = 'http://'.$this->hostname;
+        $destination .= $_SERVER['REQUEST_URI'];
+
+        $old = 'http'.
+          (($_SERVER['HTTPS'] == 'on') ? 'S' : '').
+          '://'.
+          $_SERVER['HTTP_HOST'].
+          $_SERVER['REQUEST_URI'].
+          $_SERVER['QUERY_STRING'];
+        if ($old == $destination) { // this would be a loop!
+            // error_log(...) ?
             return false;
         }
+
+        header('HTTP/1.1 301 Moved Permanently');
+        header("Location: $destination");
+
+        print "<a href='$destination'>$destination</a>\n";
+
+        exit;
     }
 }
index 3fe0d167f1c094da91866c57535764fd52307cbb..d4580fcbae053b58eb4c04bdf5d56b950c12fc93 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 08a166d5ae4a4478be5943cd69eee776834e54ec..62a3f8a66e214835548ceec645e021587e54a1bd 100644 (file)
@@ -62,14 +62,13 @@ class User extends Memcached_DataObject
     public $autosubscribe;                   // tinyint(1)
     public $urlshorteningservice;            // varchar(50)   default_ur1.ca
     public $inboxed;                         // tinyint(1)
+    public $design_id;                       // int(4)
+    public $viewdesigns;                     // tinyint(1)   default_1
     public $created;                         // datetime()   not_null
     public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
     /* Static get */
-    function staticGet($k,$v=NULL)
-    {
-        return Memcached_DataObject::staticGet('User',$k,$v);
-    }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('User',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
@@ -425,9 +424,9 @@ class User extends Memcached_DataObject
         }
     }
 
-    function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE)
+    function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE, $own=false)
     {
-        $ids = Fave::stream($this->id, $offset, $limit);
+        $ids = Fave::stream($this->id, $offset, $limit, $own);
         return Notice::getStreamByIds($ids);
     }
 
@@ -438,6 +437,33 @@ class User extends Memcached_DataObject
         // Complicated code, depending on whether we support inboxes yet
         // XXX: make this go away when inboxes become mandatory
 
+        if ($enabled === false ||
+            ($enabled == 'transitional' && $this->inboxed == 0)) {
+            $qry =
+              'SELECT notice.* ' .
+              'FROM notice JOIN subscription ON notice.profile_id = subscription.subscribed ' .
+              'WHERE subscription.subscriber = %d ' .
+              'AND notice.is_local != ' . NOTICE_GATEWAY;
+            return Notice::getStream(sprintf($qry, $this->id),
+                                     'user:notices_with_friends:' . $this->id,
+                                     $offset, $limit, $since_id, $before_id,
+                                     $order, $since);
+        } else if ($enabled === true ||
+                   ($enabled == 'transitional' && $this->inboxed == 1)) {
+
+            $ids = Notice_inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, false);
+
+            return Notice::getStreamByIds($ids);
+        }
+    }
+
+    function noticeInbox($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
+    {
+        $enabled = common_config('inboxes', 'enabled');
+
+        // Complicated code, depending on whether we support inboxes yet
+        // XXX: make this go away when inboxes become mandatory
+
         if ($enabled === false ||
             ($enabled == 'transitional' && $this->inboxed == 0)) {
             $qry =
@@ -451,7 +477,7 @@ class User extends Memcached_DataObject
         } else if ($enabled === true ||
                    ($enabled == 'transitional' && $this->inboxed == 1)) {
 
-            $ids = Notice_inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since);
+            $ids = Notice_inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, true);
 
             return Notice::getStreamByIds($ids);
         }
@@ -574,50 +600,16 @@ class User extends Memcached_DataObject
 
     function getSubscriptions($offset=0, $limit=null)
     {
-        $qry =
-          'SELECT profile.* ' .
-          'FROM profile JOIN subscription ' .
-          'ON profile.id = subscription.subscribed ' .
-          'WHERE subscription.subscriber = %d ' .
-          'AND subscription.subscribed != subscription.subscriber ' .
-          'ORDER BY subscription.created DESC ';
-
-        if (common_config('db','type') == 'pgsql') {
-            $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
-        } else {
-            $qry .= ' LIMIT ' . $offset . ', ' . $limit;
-        }
-
-        $profile = new Profile();
-
-        $profile->query(sprintf($qry, $this->id));
-
-        return $profile;
+        $profile = $this->getProfile();
+        assert(!empty($profile));
+        return $profile->getSubscriptions($offset, $limit);
     }
 
     function getSubscribers($offset=0, $limit=null)
     {
-        $qry =
-          'SELECT profile.* ' .
-          'FROM profile JOIN subscription ' .
-          'ON profile.id = subscription.subscriber ' .
-          'WHERE subscription.subscribed = %d ' .
-          'AND subscription.subscribed != subscription.subscriber ' .
-          'ORDER BY subscription.created DESC ';
-
-        if ($offset) {
-            if (common_config('db','type') == 'pgsql') {
-                $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
-            } else {
-                $qry .= ' LIMIT ' . $offset . ', ' . $limit;
-            }
-        }
-
-        $profile = new Profile();
-
-        $cnt = $profile->query(sprintf($qry, $this->id));
-
-        return $profile;
+        $profile = $this->getProfile();
+        assert(!empty($profile));
+        return $profile->getSubscribers($offset, $limit);
     }
 
     function getTaggedSubscribers($tag, $offset=0, $limit=null)
@@ -684,4 +676,9 @@ class User extends Memcached_DataObject
 
         return ($cnt > 0);
     }
+
+    function getDesign()
+    {
+        return Design::staticGet('id', $this->design_id);
+    }
 }
index a135015baca3a1f2d67601ebb9ae6f03d4c5dd0f..9b4b01ead7424f60ba7bae7db2df4152ee2ab335 100644 (file)
@@ -19,6 +19,7 @@ class User_group extends Memcached_DataObject
     public $homepage_logo;                   // varchar(255)
     public $stream_logo;                     // varchar(255)
     public $mini_logo;                       // varchar(255)
+    public $design_id;                       // int(4)
     public $created;                         // datetime()   not_null
     public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
@@ -125,6 +126,29 @@ class User_group extends Memcached_DataObject
         return $members;
     }
 
+    function getBlocked($offset=0, $limit=null)
+    {
+        $qry =
+          'SELECT profile.* ' .
+          'FROM profile JOIN group_block '.
+          'ON profile.id = group_block.blocked ' .
+          'WHERE group_block.group_id = %d ' .
+          'ORDER BY group_block.modified DESC ';
+
+        if ($limit != null) {
+            if (common_config('db','type') == 'pgsql') {
+                $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
+            } else {
+                $qry .= ' LIMIT ' . $offset . ', ' . $limit;
+            }
+        }
+
+        $blocked = new Profile();
+
+        $blocked->query(sprintf($qry, $this->id));
+        return $blocked;
+    }
+
     function setOriginal($filename)
     {
         $imagefile = new ImageFile($this->id, Avatar::path($filename));
@@ -137,4 +161,113 @@ class User_group extends Memcached_DataObject
         common_debug(common_log_objstring($this));
         return $this->update($orig);
     }
+
+    function getBestName()
+    {
+        return ($this->fullname) ? $this->fullname : $this->nickname;
+    }
+
+    function getAliases()
+    {
+        $aliases = array();
+
+        // XXX: cache this
+
+        $alias = new Group_alias();
+
+        $alias->group_id = $this->id;
+
+        if ($alias->find()) {
+            while ($alias->fetch()) {
+                $aliases[] = $alias->alias;
+            }
+        }
+
+        $alias->free();
+
+        return $aliases;
+    }
+
+    function setAliases($newaliases) {
+
+        $newaliases = array_unique($newaliases);
+
+        $oldaliases = $this->getAliases();
+
+        # Delete stuff that's old that not in new
+
+        $to_delete = array_diff($oldaliases, $newaliases);
+
+        # Insert stuff that's in new and not in old
+
+        $to_insert = array_diff($newaliases, $oldaliases);
+
+        $alias = new Group_alias();
+
+        $alias->group_id = $this->id;
+
+        foreach ($to_delete as $delalias) {
+            $alias->alias = $delalias;
+            $result = $alias->delete();
+            if (!$result) {
+                common_log_db_error($alias, 'DELETE', __FILE__);
+                return false;
+            }
+        }
+
+        foreach ($to_insert as $insalias) {
+            $alias->alias = $insalias;
+            $result = $alias->insert();
+            if (!$result) {
+                common_log_db_error($alias, 'INSERT', __FILE__);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    static function getForNickname($nickname)
+    {
+        $nickname = common_canonical_nickname($nickname);
+        $group = User_group::staticGet('nickname', $nickname);
+        if (!empty($group)) {
+            return $group;
+        }
+        $alias = Group_alias::staticGet('alias', $nickname);
+        if (!empty($alias)) {
+            return User_group::staticGet('id', $alias->group_id);
+        }
+        return null;
+    }
+
+    function getDesign()
+    {
+        return Design::staticGet('id', $this->design_id);
+    }
+
+    function getUserMembers()
+    {
+        // XXX: cache this
+
+        $user = new User();
+
+        $qry =
+          'SELECT id ' .
+          'FROM user JOIN group_member '.
+          'ON user.id = group_member.profile_id ' .
+          'WHERE group_member.group_id = %d ';
+
+        $user->query(sprintf($qry, $this->id));
+
+        $ids = array();
+
+        while ($user->fetch()) {
+            $ids[] = $user->id;
+        }
+
+        $user->free();
+
+        return $ids;
+    }
 }
old mode 100644 (file)
new mode 100755 (executable)
index 92bbb35..7e9b2b7
@@ -1,3 +1,4 @@
+
 [avatar]
 profile_id = 129
 original = 17
@@ -37,6 +38,19 @@ modified = 384
 [consumer__keys]
 consumer_key = K
 
+[design]
+id = 129
+backgroundcolor = 1
+contentcolor = 1
+sidebarcolor = 1
+textcolor = 1
+linkcolor = 1
+backgroundimage = 2
+disposition = 17
+
+[design__keys]
+id = N
+
 [fave]
 notice_id = 129
 user_id = 129
@@ -54,13 +68,14 @@ size = 1
 title = 2
 date = 1
 protected = 1
+filename = 2
+modified = 384
 
 [file__keys]
 id = N
 
 [file_oembed]
-id = 129
-file_id = 1
+file_id = 129
 version = 2
 type = 2
 provider = 2
@@ -72,37 +87,40 @@ title = 2
 author_name = 2
 author_url = 2
 url = 2
+modified = 384
 
 [file_oembed__keys]
-id = N
+file_id = K
 
 [file_redirection]
-id = 129
-url = 2
+url = 130
 file_id = 1
 redirections = 1
 httpcode = 1
+modified = 384
 
 [file_redirection__keys]
-id = N
+url = K
 
 [file_thumbnail]
-id = 129
-file_id = 1
+file_id = 129
 url = 2
 width = 1
 height = 1
+modified = 384
 
 [file_thumbnail__keys]
-id = N
+file_id = K
+url = U
 
 [file_to_post]
-id = 129
-file_id = 1
-post_id = 1
+file_id = 129
+post_id = 129
+modified = 384
 
 [file_to_post__keys]
-id = N
+file_id = K
+post_id = K
 
 [foreign_link]
 user_id = 129
@@ -157,6 +175,24 @@ id = K
 service = K
 uri = U
 
+[group_alias]
+alias = 130
+group_id = 129
+modified = 384
+
+[group_alias__keys]
+alias = K
+
+[group_block]
+group_id = 129
+blocked = 129
+blocker = 129
+modified = 384
+
+[group_block__keys]
+group_id = K
+blocked = K
+
 [group_inbox]
 group_id = 129
 notice_id = 129
@@ -411,6 +447,8 @@ uri = 2
 autosubscribe = 17
 urlshorteningservice = 2
 inboxed = 17
+design_id = 1
+viewdesigns = 17
 created = 142
 modified = 384
 
@@ -434,6 +472,7 @@ original_logo = 2
 homepage_logo = 2
 stream_logo = 2
 mini_logo = 2
+design_id = 1
 created = 142
 modified = 384
 
old mode 100755 (executable)
new mode 100644 (file)
index a70cd41..8123265
@@ -1,13 +1,14 @@
-
 [status_network]
 nickname = 130
 hostname = 2
 pathname = 2
-sitename = 2
 dbhost = 2
 dbuser = 2
 dbpass = 2
 dbname = 2
+sitename = 2
+theme = 2
+logo = 2
 created = 142
 modified = 384
 
index 636f4cf8e22255322c0452e154727f1ff3cb0f41..d42bac9a6920be6fd97332b5553f364f8b96fd76 100644 (file)
@@ -153,6 +153,12 @@ $config['sphinx']['port'] = 3312;
 // Twitter integration source attribute. Note: default is Laconica
 // $config['integration']['source'] = 'Laconica';
 
+// Enable bidirectional Twitter bridge
+//
+// NOTE: if you enable this you must also set $config['avatar']['path']
+//
+// $config['twitterbridge']['enabled'] = true;
+
 // Edit throttling. Off by default. If turned on, you can only post 20 notices
 // every 10 minutes. Admins may want to play with the settings to minimize inconvenience for
 // real users without getting uncontrollable floods from spammers or runaway bots.
@@ -222,4 +228,6 @@ $config['sphinx']['port'] = 3312;
 // $config['attachments']['file_quota'] = 5000000;
 // $config['attachments']['user_quota'] = 50000000;
 // $config['attachments']['monthly_quota'] = 15000000;
+// $config['attachments']['uploads'] = true;
 
+// $config['oohembed']['endpoint'] = 'http://oohembed.com/oohembed/';
diff --git a/db/074to080.sql b/db/074to080.sql
new file mode 100644 (file)
index 0000000..ff08191
--- /dev/null
@@ -0,0 +1,109 @@
+alter table user
+     add column design_id integer comment 'id of a design' references design(id),
+     add column viewdesigns tinyint default 1 comment 'whether to view user-provided designs';
+
+alter table notice add column
+     conversation integer comment 'id of root notice in this conversation' references notice (id),
+     add index notice_conversation_idx (conversation);
+
+alter table foreign_user
+     modify column id bigint not null comment 'unique numeric key on foreign service';
+
+alter table foreign_link
+     modify column foreign_id bigint unsigned comment 'link to user on foreign service, if exists';
+
+alter table user_group
+      add column design_id integer comment 'id of a design' references design(id);
+
+create table file (
+    id integer primary key auto_increment,
+    url varchar(255) comment 'destination URL after following redirections',
+    mimetype varchar(50) comment 'mime type of resource',
+    size integer comment 'size of resource when available',
+    title varchar(255) comment 'title of resource when available',
+    date integer(11) comment 'date of resource according to http query',
+    protected integer(1) comment 'true when URL is private (needs login)',
+    filename varchar(255) comment 'if a local file, name of the file',
+    modified timestamp comment 'date this record was modified',
+
+    unique(url)
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
+
+create table file_oembed (
+    file_id integer primary key comment 'oEmbed for that URL/file' references file (id),
+    version varchar(20) comment 'oEmbed spec. version',
+    type varchar(20) comment 'oEmbed type: photo, video, link, rich',
+    provider varchar(50) comment 'name of this oEmbed provider',
+    provider_url varchar(255) comment 'URL of this oEmbed provider',
+    width integer comment 'width of oEmbed resource when available',
+    height integer comment 'height of oEmbed resource when available',
+    html text comment 'html representation of this oEmbed resource when applicable',
+    title varchar(255) comment 'title of oEmbed resource when available',
+    author_name varchar(50) comment 'author name for this oEmbed resource',
+    author_url varchar(255) comment 'author URL for this oEmbed resource',
+    url varchar(255) comment 'URL for this oEmbed resource when applicable (photo, link)',
+    modified timestamp comment 'date this record was modified'
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
+
+create table file_redirection (
+
+    url varchar(255) primary key comment 'short URL (or any other kind of redirect) for file (id)',
+    file_id integer comment 'short URL for what URL/file' references file (id),
+    redirections integer comment 'redirect count',
+    httpcode integer comment 'HTTP status code (20x, 30x, etc.)',
+    modified timestamp comment 'date this record was modified'
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table file_thumbnail (
+
+    file_id integer primary key comment 'thumbnail for what URL/file' references file (id),
+    url varchar(255) comment 'URL of thumbnail',
+    width integer comment 'width of thumbnail',
+    height integer comment 'height of thumbnail',
+    modified timestamp comment 'date this record was modified',
+
+    unique(url)
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table file_to_post (
+
+    file_id integer comment 'id of URL/file' references file (id),
+    post_id integer comment 'id of the notice it belongs to' references notice (id),
+    modified timestamp comment 'date this record was modified',
+
+    constraint primary key (file_id, post_id)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table design (
+    id integer primary key auto_increment comment 'design ID',
+    backgroundcolor integer comment 'main background color',
+    contentcolor integer comment 'content area background color',
+    sidebarcolor integer comment 'sidebar background color',
+    textcolor integer comment 'text color',
+    linkcolor integer comment 'link color',
+    backgroundimage varchar(255) comment 'background image, if any',
+    disposition tinyint default 1 comment 'bit 1 = hide background image, bit 2 = display background image, bit 4 = tile background image'
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table group_block (
+   group_id integer not null comment 'group profile is blocked from' references user_group (id),
+   blocked integer not null comment 'profile that is blocked' references profile (id),
+   blocker integer not null comment 'user making the block' references user (id),
+   modified timestamp comment 'date of blocking',
+
+   constraint primary key (group_id, blocked)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table group_alias (
+
+   alias varchar(64) primary key comment 'additional nickname for the group',
+   group_id integer not null comment 'group profile is blocked from' references user_group (id),
+   modified timestamp comment 'date alias was created',
+
+   index group_alias_group_id_idx (group_id)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
diff --git a/db/innodb.sql b/db/innodb.sql
new file mode 100644 (file)
index 0000000..f3ab6cd
--- /dev/null
@@ -0,0 +1,2 @@
+alter table profile drop index nickname, engine=InnoDB;
+alter table notice drop index content, engine=InnoDB;
index a11e31692548449c4cabfd0629b589961ea577d4..3f8918de62d3b8bb9d5ebbadb79834162f8f6638 100644 (file)
@@ -41,6 +41,7 @@ create table sms_carrier (
 /* local users */
 
 create table user (
+
     id integer primary key comment 'foreign key to profile table' references profile (id),
     nickname varchar(64) unique key comment 'nickname or username, duped in profile',
     password varchar(255) comment 'salted password, can be null for OpenID users',
@@ -69,6 +70,9 @@ create table user (
     autosubscribe tinyint default 0 comment 'automatically subscribe to users who subscribe to us',
     urlshorteningservice varchar(50) default 'ur1.ca' comment 'service to use for auto-shortening URLs',
     inboxed tinyint default 0 comment 'has an inbox been created for this user?',
+    design_id integer comment 'id of a design' references design(id),
+    viewdesigns tinyint default 1 comment 'whether to view user-provided designs',
+
     created datetime not null comment 'date this record was created',
     modified timestamp comment 'date this record was modified',
 
@@ -273,7 +277,7 @@ create table foreign_service (
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
 
 create table foreign_user (
-     id int not null comment 'unique numeric key on foreign service',
+     id bigint not null comment 'unique numeric key on foreign service',
      service int not null comment 'foreign key to service' references foreign_service(id),
      uri varchar(255) not null unique key comment 'identifying URI',
      nickname varchar(255) comment 'nickname on foreign service',
@@ -383,6 +387,7 @@ create table user_group (
     homepage_logo varchar(255) comment 'homepage (profile) size logo',
     stream_logo varchar(255) comment 'stream-sized logo',
     mini_logo varchar(255) comment 'mini logo',
+    design_id integer comment 'id of a design' references design(id),
 
     created datetime not null comment 'date this record was created',
     modified timestamp comment 'date this record was modified',
@@ -426,59 +431,96 @@ create table group_inbox (
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
 
 create table file (
+
     id integer primary key auto_increment,
-    url varchar(255), mimetype varchar(50),
-    size integer,
-    title varchar(255),
-    date integer(11),
-    protected integer(1),
+    url varchar(255) comment 'destination URL after following redirections',
+    mimetype varchar(50) comment 'mime type of resource',
+    size integer comment 'size of resource when available',
+    title varchar(255) comment 'title of resource when available',
+    date integer(11) comment 'date of resource according to http query',
+    protected integer(1) comment 'true when URL is private (needs login)',
+    filename varchar(255) comment 'if a local file, name of the file',
+
+    modified timestamp comment 'date this record was modified',
 
     unique(url)
-) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
 
 create table file_oembed (
-    id integer primary key auto_increment,
-    file_id integer,
-    version varchar(20),
-    type varchar(20),
-    provider varchar(50),
-    provider_url varchar(255),
-    width integer,
-    height integer,
-    html text,
-    title varchar(255),
-    author_name varchar(50),
-    author_url varchar(255),
-    url varchar(255),
-
-    unique(file_id)
-) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
+    file_id integer primary key comment 'oEmbed for that URL/file' references file (id),
+    version varchar(20) comment 'oEmbed spec. version',
+    type varchar(20) comment 'oEmbed type: photo, video, link, rich',
+    provider varchar(50) comment 'name of this oEmbed provider',
+    provider_url varchar(255) comment 'URL of this oEmbed provider',
+    width integer comment 'width of oEmbed resource when available',
+    height integer comment 'height of oEmbed resource when available',
+    html text comment 'html representation of this oEmbed resource when applicable',
+    title varchar(255) comment 'title of oEmbed resource when available',
+    author_name varchar(50) comment 'author name for this oEmbed resource',
+    author_url varchar(255) comment 'author URL for this oEmbed resource',
+    url varchar(255) comment 'URL for this oEmbed resource when applicable (photo, link)',
+    modified timestamp comment 'date this record was modified'
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
 
 create table file_redirection (
-    id integer primary key auto_increment,
-    url varchar(255),
-    file_id integer,
-    redirections integer,
-    httpcode integer,
 
-    unique(url)
+    url varchar(255) primary key comment 'short URL (or any other kind of redirect) for file (id)',
+    file_id integer comment 'short URL for what URL/file' references file (id),
+    redirections integer comment 'redirect count',
+    httpcode integer comment 'HTTP status code (20x, 30x, etc.)',
+    modified timestamp comment 'date this record was modified'
+
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
 
 create table file_thumbnail (
-    id integer primary key auto_increment,
-    file_id integer,
-    url varchar(255),
-    width integer,
-    height integer,
 
-    unique(file_id),
+    file_id integer primary key comment 'thumbnail for what URL/file' references file (id),
+    url varchar(255) comment 'URL of thumbnail',
+    width integer comment 'width of thumbnail',
+    height integer comment 'height of thumbnail',
+    modified timestamp comment 'date this record was modified',
+
     unique(url)
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
 
 create table file_to_post (
-    id integer primary key auto_increment,
-    file_id integer,
-    post_id integer,
 
-    unique(file_id, post_id)
+    file_id integer comment 'id of URL/file' references file (id),
+    post_id integer comment 'id of the notice it belongs to' references notice (id),
+    modified timestamp comment 'date this record was modified',
+
+    constraint primary key (file_id, post_id)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table design (
+    id integer primary key auto_increment comment 'design ID',
+    backgroundcolor integer comment 'main background color',
+    contentcolor integer comment 'content area background color',
+    sidebarcolor integer comment 'sidebar background color',
+    textcolor integer comment 'text color',
+    linkcolor integer comment 'link color',
+    backgroundimage varchar(255) comment 'background image, if any',
+    disposition tinyint default 1 comment 'bit 1 = hide background image, bit 2 = display background image, bit 4 = tile background image'
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table group_block (
+   group_id integer not null comment 'group profile is blocked from' references user_group (id),
+   blocked integer not null comment 'profile that is blocked' references profile (id),
+   blocker integer not null comment 'user making the block' references user (id),
+   modified timestamp comment 'date of blocking',
+
+   constraint primary key (group_id, blocked)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table group_alias (
+
+   alias varchar(64) primary key comment 'additional nickname for the group',
+   group_id integer not null comment 'group profile is blocked from' references user_group (id),
+   modified timestamp comment 'date alias was created',
+
+   index group_alias_group_id_idx (group_id)
+
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
index 660ba475bb586edff429a7c271e8f434c96c2e96..a9f64e5a5d0c0f9eac3a30a1255db0b03eb451d1 100644 (file)
@@ -5,12 +5,16 @@ create table status_network (
     nickname varchar(64) primary key comment 'nickname',
     hostname varchar(255) unique key comment 'alternate hostname if any',
     pathname varchar(255) unique key comment 'alternate pathname if any',
-    sitename varchar(255) comment 'display name',
+
     dbhost varchar(255) comment 'database host',
     dbuser varchar(255) comment 'database username',
     dbpass varchar(255) comment 'database password',
     dbname varchar(255) comment 'database name',
 
+    sitename varchar(255) comment 'display name',
+    theme varchar(255) comment 'theme name',
+    logo varchar(255) comment 'site logo',
+
     created datetime not null comment 'date this record was created',
     modified timestamp comment 'date this record was modified'
 
diff --git a/extlib/Console/Getopt.php b/extlib/Console/Getopt.php
new file mode 100644 (file)
index 0000000..bb9d69c
--- /dev/null
@@ -0,0 +1,290 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Andrei Zmievski <andrei@php.net>                             |
+// +----------------------------------------------------------------------+
+//
+// $Id: Getopt.php,v 1.4 2007/06/12 14:58:56 cellog Exp $
+
+require_once 'PEAR.php';
+
+/**
+ * Command-line options parsing class.
+ *
+ * @author Andrei Zmievski <andrei@php.net>
+ *
+ */
+class Console_Getopt {
+    /**
+     * Parses the command-line options.
+     *
+     * The first parameter to this function should be the list of command-line
+     * arguments without the leading reference to the running program.
+     *
+     * The second parameter is a string of allowed short options. Each of the
+     * option letters can be followed by a colon ':' to specify that the option
+     * requires an argument, or a double colon '::' to specify that the option
+     * takes an optional argument.
+     *
+     * The third argument is an optional array of allowed long options. The
+     * leading '--' should not be included in the option name. Options that
+     * require an argument should be followed by '=', and options that take an
+     * option argument should be followed by '=='.
+     *
+     * The return value is an array of two elements: the list of parsed
+     * options and the list of non-option command-line arguments. Each entry in
+     * the list of parsed options is a pair of elements - the first one
+     * specifies the option, and the second one specifies the option argument,
+     * if there was one.
+     *
+     * Long and short options can be mixed.
+     *
+     * Most of the semantics of this function are based on GNU getopt_long().
+     *
+     * @param array  $args           an array of command-line arguments
+     * @param string $short_options  specifies the list of allowed short options
+     * @param array  $long_options   specifies the list of allowed long options
+     *
+     * @return array two-element array containing the list of parsed options and
+     * the non-option arguments
+     *
+     * @access public
+     *
+     */
+    function getopt2($args, $short_options, $long_options = null)
+    {
+        return Console_Getopt::doGetopt(2, $args, $short_options, $long_options);
+    }
+
+    /**
+     * This function expects $args to start with the script name (POSIX-style).
+     * Preserved for backwards compatibility.
+     * @see getopt2()
+     */    
+    function getopt($args, $short_options, $long_options = null)
+    {
+        return Console_Getopt::doGetopt(1, $args, $short_options, $long_options);
+    }
+
+    /**
+     * The actual implementation of the argument parsing code.
+     */
+    function doGetopt($version, $args, $short_options, $long_options = null)
+    {
+        // in case you pass directly readPHPArgv() as the first arg
+        if (PEAR::isError($args)) {
+            return $args;
+        }
+        if (empty($args)) {
+            return array(array(), array());
+        }
+        $opts     = array();
+        $non_opts = array();
+
+        settype($args, 'array');
+
+        if ($long_options) {
+            sort($long_options);
+        }
+
+        /*
+         * Preserve backwards compatibility with callers that relied on
+         * erroneous POSIX fix.
+         */
+        if ($version < 2) {
+            if (isset($args[0]{0}) && $args[0]{0} != '-') {
+                array_shift($args);
+            }
+        }
+
+        reset($args);
+        while (list($i, $arg) = each($args)) {
+
+            /* The special element '--' means explicit end of
+               options. Treat the rest of the arguments as non-options
+               and end the loop. */
+            if ($arg == '--') {
+                $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
+                break;
+            }
+
+            if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) {
+                $non_opts = array_merge($non_opts, array_slice($args, $i));
+                break;
+            } elseif (strlen($arg) > 1 && $arg{1} == '-') {
+                $error = Console_Getopt::_parseLongOption(substr($arg, 2), $long_options, $opts, $args);
+                if (PEAR::isError($error))
+                    return $error;
+            } elseif ($arg == '-') {
+                // - is stdin
+                $non_opts = array_merge($non_opts, array_slice($args, $i));
+                break;
+            } else {
+                $error = Console_Getopt::_parseShortOption(substr($arg, 1), $short_options, $opts, $args);
+                if (PEAR::isError($error))
+                    return $error;
+            }
+        }
+
+        return array($opts, $non_opts);
+    }
+
+    /**
+     * @access private
+     *
+     */
+    function _parseShortOption($arg, $short_options, &$opts, &$args)
+    {
+        for ($i = 0; $i < strlen($arg); $i++) {
+            $opt = $arg{$i};
+            $opt_arg = null;
+
+            /* Try to find the short option in the specifier string. */
+            if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':')
+            {
+                return PEAR::raiseError("Console_Getopt: unrecognized option -- $opt");
+            }
+
+            if (strlen($spec) > 1 && $spec{1} == ':') {
+                if (strlen($spec) > 2 && $spec{2} == ':') {
+                    if ($i + 1 < strlen($arg)) {
+                        /* Option takes an optional argument. Use the remainder of
+                           the arg string if there is anything left. */
+                        $opts[] = array($opt, substr($arg, $i + 1));
+                        break;
+                    }
+                } else {
+                    /* Option requires an argument. Use the remainder of the arg
+                       string if there is anything left. */
+                    if ($i + 1 < strlen($arg)) {
+                        $opts[] = array($opt,  substr($arg, $i + 1));
+                        break;
+                    } else if (list(, $opt_arg) = each($args)) {
+                        /* Else use the next argument. */;
+                        if (Console_Getopt::_isShortOpt($opt_arg) || Console_Getopt::_isLongOpt($opt_arg)) {
+                            return PEAR::raiseError("Console_Getopt: option requires an argument -- $opt");
+                        }
+                    } else {
+                        return PEAR::raiseError("Console_Getopt: option requires an argument -- $opt");
+                    }
+                }
+            }
+
+            $opts[] = array($opt, $opt_arg);
+        }
+    }
+
+    /**
+     * @access private
+     *
+     */
+    function _isShortOpt($arg)
+    {
+        return strlen($arg) == 2 && $arg[0] == '-' && preg_match('/[a-zA-Z]/', $arg[1]);
+    }
+
+    /**
+     * @access private
+     *
+     */
+    function _isLongOpt($arg)
+    {
+        return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' &&
+            preg_match('/[a-zA-Z]+$/', substr($arg, 2));
+    }
+
+    /**
+     * @access private
+     *
+     */
+    function _parseLongOption($arg, $long_options, &$opts, &$args)
+    {
+        @list($opt, $opt_arg) = explode('=', $arg, 2);
+        $opt_len = strlen($opt);
+
+        for ($i = 0; $i < count($long_options); $i++) {
+            $long_opt  = $long_options[$i];
+            $opt_start = substr($long_opt, 0, $opt_len);
+            $long_opt_name = str_replace('=', '', $long_opt);
+
+            /* Option doesn't match. Go on to the next one. */
+            if ($long_opt_name != $opt) {
+                continue;
+            }
+
+            $opt_rest  = substr($long_opt, $opt_len);
+
+            /* Check that the options uniquely matches one of the allowed
+               options. */
+            if ($i + 1 < count($long_options)) {
+                $next_option_rest = substr($long_options[$i + 1], $opt_len);
+            } else {
+                $next_option_rest = '';
+            }
+            if ($opt_rest != '' && $opt{0} != '=' &&
+                $i + 1 < count($long_options) &&
+                $opt == substr($long_options[$i+1], 0, $opt_len) &&
+                $next_option_rest != '' &&
+                $next_option_rest{0} != '=') {
+                return PEAR::raiseError("Console_Getopt: option --$opt is ambiguous");
+            }
+
+            if (substr($long_opt, -1) == '=') {
+                if (substr($long_opt, -2) != '==') {
+                    /* Long option requires an argument.
+                       Take the next argument if one wasn't specified. */;
+                    if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) {
+                        return PEAR::raiseError("Console_Getopt: option --$opt requires an argument");
+                    }
+                    if (Console_Getopt::_isShortOpt($opt_arg) || Console_Getopt::_isLongOpt($opt_arg)) {
+                        return PEAR::raiseError("Console_Getopt: option requires an argument --$opt");
+                    }
+                }
+            } else if ($opt_arg) {
+                return PEAR::raiseError("Console_Getopt: option --$opt doesn't allow an argument");
+            }
+
+            $opts[] = array('--' . $opt, $opt_arg);
+            return;
+        }
+
+        return PEAR::raiseError("Console_Getopt: unrecognized option --$opt");
+    }
+
+    /**
+    * Safely read the $argv PHP array across different PHP configurations.
+    * Will take care on register_globals and register_argc_argv ini directives
+    *
+    * @access public
+    * @return mixed the $argv PHP array or PEAR error if not registered
+    */
+    function readPHPArgv()
+    {
+        global $argv;
+        if (!is_array($argv)) {
+            if (!@is_array($_SERVER['argv'])) {
+                if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
+                    return PEAR::raiseError("Console_Getopt: Could not read cmd args (register_argc_argv=Off?)");
+                }
+                return $GLOBALS['HTTP_SERVER_VARS']['argv'];
+            }
+            return $_SERVER['argv'];
+        }
+        return $argv;
+    }
+
+}
+
+?>
diff --git a/extlib/System/Command.php b/extlib/System/Command.php
new file mode 100644 (file)
index 0000000..f5c3ec6
--- /dev/null
@@ -0,0 +1,587 @@
+<?php
+// {{{ license
+
+// +----------------------------------------------------------------------+
+// | PHP Version 4.0                                                      |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Anders Johannsen <anders@johannsen.com>                      |
+// | Author: Dan Allen <dan@mojavelinux.com>
+// +----------------------------------------------------------------------+
+
+// $Id: Command.php,v 1.9 2007/04/20 21:08:48 cconstantine Exp $
+
+// }}}
+// {{{ includes
+
+require_once 'PEAR.php';
+require_once 'System.php';
+
+// }}}
+// {{{ constants
+
+define('SYSTEM_COMMAND_OK',                 1);
+define('SYSTEM_COMMAND_ERROR',             -1);
+define('SYSTEM_COMMAND_NO_SHELL',          -2);
+define('SYSTEM_COMMAND_INVALID_SHELL',     -3);
+define('SYSTEM_COMMAND_TMPDIR_ERROR',      -4);
+define('SYSTEM_COMMAND_INVALID_OPERATOR',  -5);
+define('SYSTEM_COMMAND_INVALID_COMMAND',   -6);
+define('SYSTEM_COMMAND_OPERATOR_PLACEMENT',-7);
+define('SYSTEM_COMMAND_COMMAND_PLACEMENT', -8);
+define('SYSTEM_COMMAND_NOHUP_MISSING',     -9);
+define('SYSTEM_COMMAND_NO_OUTPUT',        -10);
+define('SYSTEM_COMMAND_STDERR',           -11);
+define('SYSTEM_COMMAND_NONZERO_EXIT',     -12);
+
+// }}}
+
+// {{{ class System_Command
+
+/**
+ * The System_Command:: class implements an abstraction for various ways 
+ * of executing commands (directly using the backtick operator,
+ * as a background task after the script has terminated using
+ * register_shutdown_function() or as a detached process using nohup).
+ *
+ * @author  Anders Johannsen <anders@johannsen.com>
+ * @author  Dan Allen <dan@mojavelinux.com>
+ * @version $Revision: 1.9 $
+ */
+
+// }}}
+class System_Command {
+    // {{{ properties
+
+    /**
+     * Array of settings used when creating the shell command
+     *
+     * @var array
+     * @access private
+     */
+    var $options = array();
+
+    /**
+     * Array of available shells to use to execute the command
+     *
+     * @var array
+     * @access private
+     */
+    var $shells = array();
+
+    /**
+     * Array of available control operators used between commands
+     *
+     * @var array
+     * @access private
+     */
+    var $controlOperators = array();
+
+    /**
+     * The system command to be executed
+     *
+     * @var string
+     * @access private
+     */
+    var $systemCommand = null;
+
+    /**
+     * Previously added part to the command string
+     *
+     * @var string
+     * @access private
+     */
+    var $previousElement = null;
+
+    /**
+     * Directory for writing stderr output
+     *
+     * @var string
+     * @access private
+     */
+    var $tmpDir = null;
+
+    /**
+     * To allow the pear error object to accumulate when building
+     * the command, we use the command status to keep track when
+     * a pear error is raised
+     *
+     * @var int
+     * @access private
+     */
+    var $commandStatus = 0;
+    
+    /**
+     * Hold initialization PEAR_Error
+     *
+     * @var object
+     * @access private
+     **/
+    var $_initError = null;
+        
+    // }}}
+    // {{{ constructor
+
+    /**
+     * Class constructor
+     * 
+     * Defines all necessary constants and sets defaults
+     * 
+     * @access public
+     */
+    function System_Command($in_shell = null)
+    {
+        // Defining constants
+        $this->options = array(
+            'SEQUENCE'   => true,
+            'SHUTDOWN'   => false,
+            'SHELL'      => $this->which($in_shell),
+            'OUTPUT'     => true,
+            'NOHUP'      => false,
+            'BACKGROUND' => false,
+            'STDERR'     => false
+        );
+
+        // prepare the available control operators
+        $this->controlOperators = array(
+            'PIPE'  => '|',
+            'AND'   => '&&',
+            'OR'    => '||',
+            'GROUP' => ';',
+            'LFIFO' => '<',
+            'RFIFO' => '>',
+        );
+                
+        // List of allowed/available shells
+        $this->shells = array(
+            'sh',
+            'bash',
+            'zsh',
+            'tcsh',
+            'csh',
+            'ash',
+            'sash',
+            'esh',
+            'ksh'
+        );
+                                   
+        // Find the first available shell
+        if (empty($this->options['SHELL'])) {
+            foreach ($this->shells as $shell) {
+                if ($this->options['SHELL'] = $this->which($shell)) {
+                    break;
+                }
+            }
+
+            // see if we still have no shell
+            if (empty($this->options['SHELL'])) {
+               $this->_initError =& PEAR::raiseError(null, SYSTEM_COMMAND_NO_SHELL, null, E_USER_WARNING, null, 'System_Command_Error', true);
+                return;
+            }
+        }
+
+        // Caputre a temporary directory for capturing stderr from commands
+        $this->tmpDir = System::tmpdir();
+        if (!System::mkDir("-p {$this->tmpDir}")) {
+            $this->_initError =& PEAR::raiseError(null, SYSTEM_COMMAND_TMPDIR_ERROR, null, E_USER_WARNING, null, 'System_Command_Error', true);
+            return;
+        }
+    }
+        
+    // }}}
+    // {{{ setOption()
+
+    /**
+     * Sets the value for an option. Each option should be set to true
+     * or false; except the 'SHELL' option which should be a string
+     * naming a shell. The options are:
+     * 
+     * 'SEQUENCE'   Allow a sequence command or not (right now this is always on);
+     *
+     * 'SHUTDOWN'   Execute commands via a shutdown function;
+     *
+     * 'SHELL'      Path to shell;
+     *
+     * 'OUTPUT'     Output stdout from process;
+     *
+     * 'NOHUP'      Use nohup to detach process;
+     *
+     * 'BACKGROUND' Run as a background process with &;
+     *
+     * 'STDERR'     Output on stderr will raise an error, even if
+     *              the command's exit value is zero. The output from
+     *              stderr can be retrieved using the getDebugInfo()
+     *              method of the Pear_ERROR object returned by
+     *              execute().;
+     *
+     * @param string $in_option is a case-sensitive string,
+     *                          corresponding to the option
+     *                          that should be changed
+     * @param mixed $in_setting is the new value for the option
+     * @access public
+     * @return bool true if succes, else false
+     */
+    function setOption($in_option, $in_setting)
+    {
+       if ($this->_initError) {
+            return $this->_initError;
+        }
+
+        $option = strtoupper($in_option);
+
+        if (!isset($this->options[$option])) {
+            PEAR::raiseError(null, SYSTEM_COMMAND_ERROR, null, E_USER_NOTICE, null, 'System_Command_Error', true);
+            return false;
+        }
+                
+        switch ($option) {
+            case 'OUTPUT':
+            case 'SHUTDOWN':
+            case 'SEQUENCE':
+            case 'BACKGROUND':
+            case 'STDERR':
+                $this->options[$option] = !empty($in_setting);
+                return true;
+            break;
+                
+            case 'SHELL':
+                if (($shell = $this->which($in_setting)) !== false) {
+                    $this->options[$option] = $shell;
+                    return true;
+                } 
+                else {
+                    PEAR::raiseError(null, SYSTEM_COMMAND_NO_SHELL, null, E_USER_NOTICE, $in_setting, 'System_Command_Error', true);
+                    return false;
+                }
+            break;
+                        
+            case 'NOHUP':
+                if (empty($in_setting)) {
+                    $this->options[$option] = false;
+                } 
+                else if ($location = $this->which('nohup')) {
+                    $this->options[$option] = $location;
+                } 
+                else {
+                    PEAR::raiseError(null, SYSTEM_COMMAND_NOHUP_MISSING, null, E_USER_NOTICE, null, 'System_Command_Error', true);
+                    return false;
+                }
+            break;
+        }
+    }
+    
+    // }}}
+    // {{{ pushCommand()
+
+    /**
+     * Used to push a command onto the running command to be executed
+     *
+     * @param  string $in_command binary to be run
+     * @param  string $in_argument either an option or argument value, to be handled appropriately
+     * @param  string $in_argument
+     * @param  ...
+     *
+     * @access public
+     * @return boolean true on success {or System_Command_Error Exception}
+     */
+    function pushCommand($in_command)
+    {
+       if ($this->_initError) {
+            return $this->_initError;
+        }
+        
+        if (!is_null($this->previousElement) && !in_array($this->previousElement, $this->controlOperators)) {
+            $this->commandStatus = -1;
+            $error = PEAR::raiseError(null, SYSTEM_COMMAND_COMMAND_PLACEMENT, null, E_USER_WARNING, null, 'System_Command_Error', true);
+        }
+
+        // check for error here
+        $command = escapeshellcmd($this->which($in_command));
+        if ($command === false) {
+            $error = PEAR::raiseError(null, SYSTEM_COMMAND_INVALID_COMMAND, null, E_USER_WARNING, null, 'System_Command_Error', true);
+        }
+
+        $argv = func_get_args();
+        array_shift($argv);
+        foreach($argv as $arg) {
+            if (strpos($arg, '-') === 0) {
+                $command .= ' ' . $arg; 
+            }
+            elseif ($arg != '') {
+                $command .= ' ' . escapeshellarg($arg);
+            }
+        }
+
+        $this->previousElement = $command;
+        $this->systemCommand .= $command;
+
+        return isset($error) ? $error : true;
+    }
+
+    // }}}
+    // {{{ pushOperator()
+
+    /**
+     * Used to push an operator onto the running command to be executed
+     *
+     * @param  string $in_operator Either string reprentation of operator or system character
+     *
+     * @access public
+     * @return boolean true on success {or System_Command_Error Exception}
+     */
+    function pushOperator($in_operator)
+    {
+       if ($this->_initError) {
+            return $this->_initError;
+        }
+
+        $operator = isset($this->controlOperators[$in_operator]) ? $this->controlOperators[$in_operator] : $in_operator;
+
+        if (is_null($this->previousElement) || in_array($this->previousElement, $this->controlOperators)) {
+            $this->commandStatus = -1;
+            $error = PEAR::raiseError(null, SYSTEM_COMMAND_OPERATOR_PLACEMENT, null, E_USER_WARNING, null, 'System_Command_Error', true);
+        }
+        elseif (!in_array($operator, $this->controlOperators)) {
+            $this->commandStatus = -1;
+            $error = PEAR::raiseError(null, SYSTEM_COMMAND_INVALID_OPERATOR, null, E_USER_WARNING, $operator, 'System_Command_Error', true);
+        }
+
+        $this->previousElement = $operator;
+        $this->systemCommand .= ' ' . $operator . ' ';
+        return isset($error) ? $error : true;
+    }
+
+    // }}}
+    // {{{ execute()
+
+    /**
+     * Executes the code according to given options
+     *
+     * @return bool true if success {or System_Command_Exception}
+     *
+     * @access public
+     */
+    function execute() 
+    {
+       if ($this->_initError) {
+            return $this->_initError;
+        }
+
+        // if the command is empty or if the last element was a control operator, we can't continue
+        if (is_null($this->previousElement) || $this->commandStatus == -1 || in_array($this->previousElement, $this->controlOperators)) {
+            return PEAR::raiseError(null, SYSTEM_COMMAND_INVALID_COMMAND, null, E_USER_WARNING, $this->systemCommand, 'System_Command_Error', true);
+        }
+
+        // Warning about impossible mix of options
+        if (!empty($this->options['OUTPUT'])) {
+            if (!empty($this->options['SHUTDOWN']) || !empty($this->options['NOHUP'])) {
+                return PEAR::raiseError(null, SYSTEM_COMMAND_NO_OUTPUT, null, E_USER_WARNING, null, 'System_Command_Error', true);
+            }
+        }
+                
+        // if this is not going to stdout, then redirect to /dev/null
+        if (empty($this->options['OUTPUT'])) {
+            $this->systemCommand .= ' >/dev/null';
+        }
+                
+        $suffix = '';
+        // run a command immune to hangups, with output to a non-tty
+        if (!empty($this->options['NOHUP'])) {
+            $this->systemCommand = $this->options['NOHUP'] . $this->systemCommand;
+        }
+        // run a background process (only if not nohup)
+        elseif (!empty($this->options['BACKGROUND'])) {
+            $suffix = ' &';
+        }
+                
+        // Register to be run on shutdown
+        if (!empty($this->options['SHUTDOWN'])) {
+            $line = "system(\"{$this->systemCommand}$suffix\");";
+            $function = create_function('', $line);
+            register_shutdown_function($function);
+            return true;
+        } 
+        else {
+            // send stderr to a file so that we can reap the error message
+            $tmpFile = tempnam($this->tmpDir, 'System_Command-');
+            $this->systemCommand .= ' 2>' . $tmpFile . $suffix;
+            $shellPipe = $this->which('echo') . ' ' . escapeshellarg($this->systemCommand) . ' | ' . $this->options['SHELL'];
+            exec($shellPipe, $result, $returnVal);
+
+            if ($returnVal !== 0) {
+                // command returned nonzero; that's always an error
+                $return = PEAR::raiseError(null, SYSTEM_COMMAND_NONZERO_EXIT, null, E_USER_WARNING, null, 'System_Command_Error', true);
+            }
+            else if (!$this->options['STDERR']) {
+                // caller does not care about stderr; return success
+                $return = implode("\n", $result);
+            }
+            else {
+                // our caller cares about stderr; check stderr output
+                clearstatcache();
+                if (filesize($tmpFile) > 0) {
+                    // the command actually wrote to stderr
+                    $stderr_output = file_get_contents($tmpFile);
+                    $return = PEAR::raiseError(null, SYSTEM_COMMAND_STDERR, null, E_USER_WARNING, $stderr_output, 'System_Command_Error', true);
+                } else {
+                    // total success; return stdout gathered by exec()
+                    $return = implode("\n", $result);
+                }
+            }
+
+            unlink($tmpFile);
+            return $return;
+        }
+    }
+
+    // }}}
+    // {{{ which()
+
+    /**
+     * Functionality similiar to unix 'which'. Searches the path
+     * for the specified program. 
+     *
+     * @param $cmd name of the executable to search for 
+     *
+     * @access private
+     * @return string returns the full path if found, false if not
+     */
+    function which($in_cmd)
+    {
+        // only pass non-empty strings to System::which()
+        if (!is_string($in_cmd) || '' === $in_cmd) {
+            return(false);
+        }
+
+        // explicitly pass false as fallback value
+        return System::which($in_cmd, false);
+    }   
+
+    // }}}
+    // {{{ reset()
+
+    /**
+     * Prepare for a new command to be built
+     *
+     * @access public
+     * @return void
+     */
+    function reset()
+    {
+        $this->previousElement = null;
+        $this->systemCommand = null;
+        $this->commandStatus = 0;
+    }
+
+    // }}}
+    // {{{ errorMessage()
+
+    /**
+     * Return a textual error message for a System_Command error code
+     *
+     * @param integer error code
+     *
+     * @return string error message, or false if the error code was
+     * not recognized
+     */
+    function errorMessage($in_value)
+    {
+        static $errorMessages;
+        if (!isset($errorMessages)) {
+            $errorMessages = array(
+                SYSTEM_COMMAND_OK                     => 'no error',
+                SYSTEM_COMMAND_ERROR                  => 'unknown error',
+                SYSTEM_COMMAND_NO_SHELL               => 'no shell found',
+                SYSTEM_COMMAND_INVALID_SHELL          => 'invalid shell',
+                SYSTEM_COMMAND_TMPDIR_ERROR           => 'could not create temporary directory',
+                SYSTEM_COMMAND_INVALID_OPERATOR       => 'control operator invalid',
+                SYSTEM_COMMAND_INVALID_COMMAND        => 'invalid system command',
+                SYSTEM_COMMAND_OPERATOR_PLACEMENT     => 'invalid placement of control operator',
+                SYSTEM_COMMAND_COMMAND_PLACEMENT      => 'invalid placement of command',
+                SYSTEM_COMMAND_NOHUP_MISSING          => 'nohup not found on system',
+                SYSTEM_COMMAND_NO_OUTPUT              => 'output not allowed',
+                SYSTEM_COMMAND_STDERR                 => 'command wrote to stderr',
+                SYSTEM_COMMAND_NONZERO_EXIT           => 'non-zero exit value from command',
+            );
+        }
+
+        if (System_Command::isError($in_value)) {
+            $in_value = $in_value->getCode();
+        }
+
+        return isset($errorMessages[$in_value]) ? $errorMessages[$in_value] : $errorMessages[SYSTEM_COMMAND_ERROR];
+    }
+
+    // }}}
+    // {{{ isError()
+
+    /**
+     * Tell whether a result code from a System_Command method is an error
+     *
+     * @param int result code
+     *
+     * @return bool whether $in_value is an error
+     *
+     * @access public
+     */
+    function isError($in_value)
+    {
+        return (is_object($in_value) &&
+                (strtolower(get_class($in_value)) == 'system_command_error' ||
+                 is_subclass_of($in_value, 'system_command_error')));
+    }
+    
+    // }}}
+}
+
+// {{{ class System_Command_Error
+
+/**
+ * System_Command_Error constructor.
+ *
+ * @param mixed      System_Command error code, or string with error message.
+ * @param integer    what "error mode" to operate in
+ * @param integer    what error level to use for $mode & PEAR_ERROR_TRIGGER
+ * @param mixed      additional debug info, such as the last query
+ *
+ * @access public
+ *
+ * @see PEAR_Error
+ */
+
+// }}}
+class System_Command_Error extends PEAR_Error
+{
+    // {{{ properties
+
+    /**
+     * Message in front of the error message
+     * @var string $error_message_prefix
+     */
+    var $error_message_prefix = 'System_Command Error: ';
+
+    // }}}
+    // {{{ constructor
+
+    function System_Command_Error($code = SYSTEM_COMMAND_ERROR, $mode = PEAR_ERROR_RETURN,
+              $level = E_USER_NOTICE, $debuginfo = null)
+    {
+        if (is_int($code)) {
+            $this->PEAR_Error(System_Command::errorMessage($code), $code, $mode, $level, $debuginfo);
+        } else {
+            $this->PEAR_Error("Invalid error code: $code", SYSTEM_COMMAND_ERROR, $mode, $level, $debuginfo);
+        }
+    }
+    
+    // }}}
+}
+?>
index 4eff99dff5b67ec8607c8315816fce069f9a32ee..cb6a0fe6032677522b9cb42f1d360932f17f1fd5 100644 (file)
--- a/index.php
+++ b/index.php
@@ -1,7 +1,7 @@
 <?php
 /**
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -48,13 +48,25 @@ function handleError($error)
         $logmsg .= " : ". $error->getDebugInfo();
     }
     common_log(LOG_ERR, $logmsg);
-    $msg = sprintf(_('The database for %s isn\'t responding correctly, '.
-                     'so the site won\'t work properly. '.
-                     'The site admins probably know about the problem, '.
-                     'but you can contact them at %s to make sure. '.
-                     'Otherwise, wait a few minutes and try again.'),
-                   common_config('site', 'name'),
-                   common_config('site', 'email'));
+    if(common_config('site', 'logdebug')) {
+        $bt = $error->getBacktrace();
+        foreach ($bt as $line) {
+            common_log(LOG_ERR, $line);
+        }
+    }
+    if ($error instanceof DB_DataObject_Error ||
+        $error instanceof DB_Error) {
+        $msg = sprintf(_('The database for %s isn\'t responding correctly, '.
+                         'so the site won\'t work properly. '.
+                         'The site admins probably know about the problem, '.
+                         'but you can contact them at %s to make sure. '.
+                         'Otherwise, wait a few minutes and try again.'),
+                       common_config('site', 'name'),
+                       common_config('site', 'email'));
+    } else {
+        $msg = _('An important error occured, probably related to email setup. '.
+                 'Check logfiles for more info..');
+    }
 
     $dac = new DBErrorAction($msg, 500);
     $dac->showPage();
@@ -70,7 +82,7 @@ function main()
     global $user, $action, $config;
 
     Snapshot::check();
-    
+
     if (!_have_config()) {
         $msg = sprintf(_("No configuration file found. Try running ".
                          "the installation program first."));
index 133f2b30f68843220461fb7fcefd52c01cf738f9..b94a9293607e7928659c4da53e09323283d61f5b 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /**
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2009, Controlez-Vous, Inc.
+ * 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
@@ -36,7 +36,7 @@ function main()
 function checkPrereqs()
 {
        $pass = true;
-       
+
     if (file_exists(INSTALLDIR.'/config.php')) {
          ?><p class="error">Config file &quot;config.php&quot; already exists.</p>
          <?php
@@ -88,7 +88,6 @@ function checkExtension($name)
 
 function showForm()
 {
-    $config_path = htmlentities(trim(dirname($_SERVER['REQUEST_URI']), '/'));
     echo<<<E_O_T
         </ul>
     </dd>
@@ -116,11 +115,6 @@ function showForm()
                 <input type="radio" name="fancy" id="fancy-disable" value="" /> disable<br />
                 <p class="form_guide" id='fancy-form_guide'>Enable fancy (pretty) URLs. Auto-detection failed, it depends on Javascript.</p>
             </li>
-            <li>
-                <label for="host">Site path</label>
-                <input type="text" id="path" name="path" value="$config_path" />
-                <p class="form_guide">Site path, following the "/" after the domain name in the URL. Empty is fine. Field should be filled automatically.</p>
-            </li>
             <li>
                 <label for="host">Hostname</label>
                 <input type="text" id="host" name="host" />
@@ -167,7 +161,6 @@ function handlePost()
     $username = $_POST['username'];
     $password = $_POST['password'];
     $sitename = $_POST['sitename'];
-    $path     = $_POST['path'];
     $fancy    = !empty($_POST['fancy']);
 ?>
     <dl class="system_notice">
@@ -176,7 +169,7 @@ function handlePost()
             <ul>
 <?php
        $fail = false;
-       
+
     if (empty($host)) {
         updateStatus("No hostname specified.", true);
                $fail = true;
@@ -243,7 +236,7 @@ function handlePost()
     }
     updateStatus("Writing config file...");
     $sqlUrl = "mysqli://$username:$password@$host/$database";
-    $res = writeConf($sitename, $sqlUrl, $fancy, $path);
+    $res = writeConf($sitename, $sqlUrl, $fancy);
     if (!$res) {
         updateStatus("Can't write config file.", true);
         showForm();
@@ -257,14 +250,13 @@ function handlePost()
 <?php
 }
 
-function writeConf($sitename, $sqlUrl, $fancy, $path)
+function writeConf($sitename, $sqlUrl, $fancy)
 {
     $res = file_put_contents(INSTALLDIR.'/config.php',
                              "<?php\n".
                              "if (!defined('LACONICA')) { exit(1); }\n\n".
                              "\$config['site']['name'] = \"$sitename\";\n\n".
                              ($fancy ? "\$config['site']['fancy'] = true;\n\n":'').
-                             "\$config['site']['path'] = \"$path\";\n\n".
                              "\$config['db']['database'] = \"$sqlUrl\";\n\n".
                              "?>");
     return $res;
diff --git a/js/farbtastic/farbtastic.go.js b/js/farbtastic/farbtastic.go.js
deleted file mode 100644 (file)
index 0149eca..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/** Init for Farbtastic library and page setup
- *
- * @package   Laconica
- * @author Sarven Capadisli <csarven@controlyourself.ca>
- * @copyright 2009 Control Yourself, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://laconi.ca/
- */
-$(document).ready(function() {
-    function UpdateColors(S) {
-        C = $(S).val();
-        switch (parseInt(S.id.slice(-1))) {
-            case 0: default:
-                $('body').css({'background-color':C});
-                break;
-            case 1:
-                $('#content').css({'background-color':C});
-                break;
-            case 2:
-                $('#aside_primary').css({'background-color':C});
-                break;
-            case 3:
-                $('body').css({'color':C});
-                break;
-            case 4:
-                $('a').css({'color':C});
-                break;
-        }
-    }
-
-    function UpdateFarbtastic(e) {
-        f.linked = e;
-        f.setColor(e.value);
-    }
-
-    function UpdateSwatch(e) {
-        $(e).css({"background-color": e.value,
-                  "color": f.hsl[2] > 0.5 ? "#000": "#fff"});
-    }
-
-    function SynchColors(e) {
-        var S = f.linked;
-        var C = f.color;
-
-        if (S && S.value && S.value != C) {
-            S.value = C;
-            UpdateSwatch(S);
-            UpdateColors(S);
-        }
-    }
-
-    function Init() {
-        $('#settings_design_color').append('<div id="color-picker"></div>');
-        $('#color-picker').hide();
-
-        f = $.farbtastic('#color-picker', SynchColors);
-        swatches = $('#settings_design_color .swatch');
-
-        swatches
-            .each(SynchColors)
-            .blur(function() {
-                $(this).val($(this).val().toUpperCase());
-             })
-            .focus(function() {
-                $('#color-picker').show();
-                UpdateFarbtastic(this);
-            })
-            .change(function() {
-                UpdateFarbtastic(this);
-                UpdateSwatch(this);
-                UpdateColors(this);
-            }).change();
-    }
-
-    var f, swatches;
-    Init();
-    $('#form_settings_design').bind('reset', function(){
-        setTimeout(function(){
-            swatches.each(function(){UpdateColors(this);});
-            $('#color-picker').remove();
-            swatches.unbind();
-            Init();
-        },10);
-    });
-});
index 24a377803c42008f36b7e455a369b4dcd15bea51..d8b5ad9cdf077a2460c6cfab11cef2eb53f0c229 100644 (file)
@@ -1,5 +1,21 @@
-// $Id: farbtastic.js,v 1.2 2007/01/08 22:53:01 unconed Exp $
-// Farbtastic 1.2
+/**
+ * Farbtastic Color Picker 1.2
+ * Â© 2008 Steven Wittens
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
 
 jQuery.fn.farbtastic = function (callback) {
   $.farbtastic(this, callback);
diff --git a/js/userdesign.go.js b/js/userdesign.go.js
new file mode 100644 (file)
index 0000000..833b19a
--- /dev/null
@@ -0,0 +1,98 @@
+/** Init for Farbtastic library and page setup
+ *
+ * @package   Laconica
+ * @author Sarven Capadisli <csarven@controlyourself.ca>
+ * @copyright 2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+$(document).ready(function() {
+    function UpdateColors(S) {
+        C = $(S).val();
+        switch (parseInt(S.id.slice(-1))) {
+            case 1: default:
+                $('html, body').css({'background-color':C});
+                break;
+            case 2:
+                $('#content, #site_nav_local_views .current a').css({'background-color':C});
+                break;
+            case 3:
+                $('#aside_primary').css({'background-color':C});
+                break;
+            case 4:
+                $('html body').css({'color':C});
+                break;
+            case 5:
+                $('a').css({'color':C});
+                break;
+        }
+    }
+
+    function UpdateFarbtastic(e) {
+        f.linked = e;
+        f.setColor(e.value);
+    }
+
+    function UpdateSwatch(e) {
+        $(e).css({"background-color": e.value,
+                  "color": f.hsl[2] > 0.5 ? "#000": "#fff"});
+    }
+
+    function SynchColors(e) {
+        var S = f.linked;
+        var C = f.color;
+
+        if (S && S.value && S.value != C) {
+            S.value = C;
+            UpdateSwatch(S);
+            UpdateColors(S);
+        }
+    }
+
+    function InitFarbtastic() {
+        $('#settings_design_color').append('<div id="color-picker"></div>');
+        $('#color-picker').hide();
+
+        f = $.farbtastic('#color-picker', SynchColors);
+        swatches = $('#settings_design_color .swatch');
+
+        swatches
+            .each(SynchColors)
+            .blur(function() {
+                tv = $(this).val();
+                $(this).val(tv.toUpperCase());
+                (tv.length == 4) ? ((tv[0] == '#') ? $(this).val('#'+tv[1]+tv[1]+tv[2]+tv[2]+tv[3]+tv[3]) : '') : '';
+             })
+            .focus(function() {
+                $('#color-picker').show();
+                UpdateFarbtastic(this);
+            })
+            .change(function() {
+                UpdateFarbtastic(this);
+                UpdateSwatch(this);
+                UpdateColors(this);
+            }).change();
+    }
+
+    var f, swatches;
+    InitFarbtastic();
+    $('#form_settings_design').bind('reset', function(){
+        setTimeout(function(){
+            swatches.each(function(){UpdateColors(this);});
+            $('#color-picker').remove();
+            swatches.unbind();
+            InitFarbtastic();
+        },10);
+    });
+
+    $('#design_background-image_off').focus(function() {
+        $('body').css({'background-image':'none'});
+    });
+    $('#design_background-image_on').focus(function() {
+        $('body').css({'background-image':'url('+$('#design_background-image_onoff img')[0].src+')'});
+    });
+
+    $('#design_background-image_repeat').click(function() {
+        ($(this)[0].checked) ? $('body').css({'background-repeat':'repeat'}) : $('body').css({'background-repeat':'no-repeat'});
+    });
+});
index fd2500d44710a5552f2c5d00771589eb14b9dc08..e7c54b74accb245d8397105efe30cd3af19ecb67 100644 (file)
@@ -217,10 +217,12 @@ $(document).ready(function(){
                                                             $('#'+li.id).css({display:'none'});
                                                             $('#'+li.id).fadeIn(2500);
                                                             NoticeReply();
+                                                            NoticeAttachments();
                                                          }
                                                                                                        }
                                                                                                        $("#notice_data-text").val("");
                                                                                                $("#notice_data-attach").val("");
+                                                    $('#notice_data-attach_selected').remove();
                                                     counter();
                                                                                                }
                                                                                                $("#form_notice").removeClass("processing");
@@ -230,23 +232,13 @@ $(document).ready(function(){
                                           };
        $("#form_notice").ajaxForm(PostNotice);
        $("#form_notice").each(addAjaxHidden);
-    NoticeHover();
     NoticeReply();
     NoticeAttachments();
+    NoticeDataAttach();
 });
 
-
-function NoticeHover() {
-    function mouseHandler(e) {
-        $(e.target).closest('li.hentry')[(e.type === 'mouseover') ? 'addClass' : 'removeClass']('hover');
-    };
-    $('#content .notices').mouseover(mouseHandler);
-    $('#content .notices').mouseout(mouseHandler);
-}
-
-
 function NoticeReply() {
-    if ($('#notice_data-text').length > 0) {
+    if ($('#notice_data-text').length > 0 && $('#content .notice_reply').length > 0) {
         $('#content .notice').each(function() {
             var notice = $(this)[0];
             $($('.notice_reply', notice)[0]).click(function() {
@@ -290,13 +282,13 @@ function NoticeAttachments() {
         timeout : 0
     };
 
-    $('a.attachment').click(function() {
+    $('#content .notice a.attachment').click(function() {
         $().jOverlay({url: $('address .url')[0].href+'/attachment/' + ($(this).attr('id').substring('attachment'.length + 1)) + '/ajax'});
         return false;
     });
     
     var t;
-    $("body:not(#shownotice) a.thumbnail").hover(
+    $("body:not(#shownotice) #content .notice a.thumbnail").hover(
         function() {
             var anchor = $(this);
             $("a.thumbnail").children('img').hide();
@@ -320,3 +312,16 @@ function NoticeAttachments() {
         }
     );
 }
+
+function NoticeDataAttach() {
+    NDA = $('#notice_data-attach');
+    NDA.change(function() {
+        S = '<div id="notice_data-attach_selected" class="success"><code>'+$(this).val()+'</code> <button>&#215;</button></div>';
+        NDAS = $('#notice_data-attach_selected');
+        (NDAS.length > 0) ? NDAS.replaceWith(S) : $('#form_notice').append(S);
+        $('#notice_data-attach_selected button').click(function(){
+            $('#notice_data-attach_selected').remove();
+            NDA.val('');
+        });
+    });
+}
index 924aa93a8953ab7b0239660989cc1db849d43e97..29f4eb3a66ff3eefbe989d9f07c51e815ac2487a 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 86800d2a36b1e3630d61cb399dda7523be7f2d4e..4ab50abcef1134a50282bbdfb64a45a4696fa972 100644 (file)
@@ -115,7 +115,7 @@ class AccountSettingsNav extends Widget
                 'openidsettings' =>
                 array(_('OpenID'),
                       _('Add or remove OpenIDs')),
-                'designsettings' =>
+                'userdesignsettings' =>
                 array(_('Design'),
                       _('Design your profile')),
                 'othersettings' =>
index ef0eeffa5ec774a372e94f70256452473987763f..a8a12b3bb35668ec1bc8e41872e8d5a7ab962b09 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 45e4fa319687761ee9dc31d3b617b7cfc18a2e5e..f6a1b59d03d8521752aa58f9ade72c1ac82d428e 100644 (file)
@@ -46,7 +46,6 @@ if (!defined('LACONICA')) {
  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link     http://laconi.ca/
  * @see      Notice
- * @see      StreamAction
  * @see      NoticeListItem
  * @see      ProfileNoticeList
  */
@@ -83,7 +82,8 @@ class AttachmentList extends Widget
         $atts = new File;
         $att = $atts->getAttachments($this->notice->id);
         if (empty($att)) return 0;
-        $this->out->elementStart('dl', array('id' =>'attachments'));
+        $this->out->elementStart('dl', array('id' =>'attachments',
+                                             'class' => 'entry-content'));
         $this->out->element('dt', null, _('Attachments'));
         $this->out->elementStart('dd');
         $this->out->elementStart('ol', array('class' => 'attachments'));
@@ -211,7 +211,7 @@ class AttachmentListItem extends Widget
     function showRepresentation() {
         $thumbnail = File_thumbnail::staticGet('file_id', $this->attachment->id);
         if (!empty($thumbnail)) {
-            $this->out->element('img', array('alt' => 'nothing to say', 'src' => $thumbnail->url, 'width' => $thumbnail->width, 'height' => $thumbnail->height));
+            $this->out->element('img', array('alt' => '', 'src' => $thumbnail->url, 'width' => $thumbnail->width, 'height' => $thumbnail->height));
         }
     }
 
@@ -244,6 +244,53 @@ class AttachmentListItem extends Widget
 
 class Attachment extends AttachmentListItem
 {
+    function showLink() {
+        $this->out->elementStart('div', array('id' => 'attachment_view',
+                                              'class' => 'hentry'));
+        $this->out->elementStart('div', 'entry-title');
+        $this->out->elementStart('a', $this->linkAttr());
+        $this->out->element('span', null, $this->linkTitle());
+        $this->out->elementEnd('a');
+        $this->out->elementEnd('div');
+
+        $this->out->elementStart('div', 'entry-content');
+        $this->showRepresentation();
+        $this->out->elementEnd('div');
+
+        if (!empty($this->oembed->author_name) || !empty($this->oembed->provider)) {
+            $this->out->elementStart('div', array('id' => 'oembed_info', 
+                                                  'class' => 'entry-content'));
+            if (!empty($this->oembed->author_name)) {
+                $this->out->elementStart('dl', 'vcard author');
+                $this->out->element('dt', null, _('Author'));
+                $this->out->elementStart('dd', 'fn');
+                if (empty($this->oembed->author_url)) {
+                    $this->out->text($this->oembed->author_name);
+                } else {
+                    $this->out->element('a', array('href' => $this->oembed->author_url,
+                                                   'class' => 'url'), $this->oembed->author_name);
+                }
+                $this->out->elementEnd('dd');
+                $this->out->elementEnd('dl');
+            }
+            if (!empty($this->oembed->provider)) {
+                $this->out->elementStart('dl', 'vcard');
+                $this->out->element('dt', null, _('Provider'));
+                $this->out->elementStart('dd', 'fn');
+                if (empty($this->oembed->provider_url)) {
+                    $this->out->text($this->oembed->provider);
+                } else {
+                    $this->out->element('a', array('href' => $this->oembed->provider_url,
+                                                   'class' => 'url'), $this->oembed->provider);
+                }
+                $this->out->elementEnd('dd');
+                $this->out->elementEnd('dl');
+            }
+            $this->out->elementEnd('div');
+        }
+        $this->out->elementEnd('div');
+    }
+
     function show() {
         $this->showNoticeAttachment();
     }
index f1e2055466ae2f7f83fe66b39b8ca9b94f2caaac..38c1d4d67f56f37025f8c5bc4115dda5cd3614b9 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 0c48414d55f066c774c2e41a4aebc6b3ec98b0d4..7ddc35eb660459d407701581323d9dc8ff776f85 100644 (file)
@@ -13,7 +13,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 507990a0b3ac6a6ba6d5315a9c3c605f0050b957..56466138293caa18cab3023d5bfca0dd2a24ffff 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 49c733c0339c0f2c5f29f85af847f803642dcbd8..6538d44420e9135f78d432277ac5a65c7b6c3928 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 6bf4ad21f5fea78baa57e86919b1e43008645aec..bb1a4255da7fa9a5d7869f93eb7ee8e97d502812 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -55,14 +55,37 @@ require_once(INSTALLDIR.'/lib/language.php');
 require_once(INSTALLDIR.'/lib/event.php');
 require_once(INSTALLDIR.'/lib/plugin.php');
 
-// try to figure out where we are
+function _sn_to_path($sn)
+{
+    $past_root = substr($sn, 1);
+    $last_slash = strrpos($past_root, '/');
+    if ($last_slash > 0) {
+        $p = substr($past_root, 0, $last_slash);
+    } else {
+        $p = '';
+    }
+    return $p;
+}
+
+// try to figure out where we are. $server and $path
+// can be set by including module, else we guess based
+// on HTTP info.
+
+if (isset($server)) {
+    $_server = $server;
+} else {
+    $_server = array_key_exists('SERVER_NAME', $_SERVER) ?
+      strtolower($_SERVER['SERVER_NAME']) :
+    null;
+}
 
-$_server = array_key_exists('SERVER_NAME', $_SERVER) ?
-  strtolower($_SERVER['SERVER_NAME']) :
-  null;
-$_path = array_key_exists('SCRIPT_NAME', $_SERVER) ?
-  substr($_SERVER['SCRIPT_NAME'], 1, strrpos($_SERVER['SCRIPT_NAME'], '/') - 1) :
-  null;
+if (isset($path)) {
+    $_path = $path;
+} else {
+    $_path = array_key_exists('SCRIPT_NAME', $_SERVER) ?
+      _sn_to_path($_SERVER['SCRIPT_NAME']) :
+    null;
+}
 
 // default configuration, overwritten in config.php
 
@@ -71,6 +94,14 @@ $config =
         array('name' => 'Just another Laconica microblog',
               'server' => $_server,
               'theme' => 'default',
+              'design' =>
+              array('backgroundcolor' => '#CEE1E9',
+                    'contentcolor' => '#FFFFFF',
+                    'sidebarcolor' => '#C8D1D5',
+                    'textcolor' => '#000000',
+                    'linkcolor' => '#002E6E',
+                    'backgroundimage' => null,
+                    'disposition' => 1),
               'path' => $_path,
               'logfile' => null,
               'logo' => null,
@@ -94,7 +125,13 @@ $config =
         array('appname' => 'laconica', # for syslog
               'priority' => 'debug'), # XXX: currently ignored
         'queue' =>
-        array('enabled' => false),
+        array('enabled' => false,
+              'subsystem' => 'db', # default to database, or 'stomp'
+              'stomp_server' => null,
+              'queue_basename' => 'laconica',
+              'stomp_username' => null,
+              'stomp_password' => null,
+              ),
         'license' =>
         array('url' => 'http://creativecommons.org/licenses/by/3.0/',
               'title' => 'Creative Commons Attribution 3.0',
@@ -108,13 +145,21 @@ $config =
         'profile' =>
         array('banned' => array()),
         'avatar' =>
-        array('server' => null),
+        array('server' => null,
+              'dir' => INSTALLDIR . '/avatar/',
+              'path' => $_path . '/avatar/'),
+        'background' =>
+        array('server' => null,
+              'dir' => INSTALLDIR . '/background/',
+              'path' => $_path . '/background/'),
         'public' =>
         array('localonly' => true,
               'blacklist' => array(),
               'autosource' => array()),
         'theme' =>
-        array('server' => null),
+        array('server' => null,
+              'dir' => null,
+              'path'=> null),
         'throttle' =>
         array('enabled' => false, // whether to throttle edits; false by default
               'count' => 20, // number of allowed messages in timespan
@@ -150,6 +195,7 @@ $config =
         'memcached' =>
         array('enabled' => false,
               'server' => 'localhost',
+              'base' => null,
               'port' => 11211),
                'ping' =>
         array('notify' => array()),
@@ -163,41 +209,51 @@ $config =
               'frequency' => 10000,
               'reporturl' => 'http://laconi.ca/stats/report'),
         'attachments' =>
-        array('supported' => array('image/png',
-            'image/jpeg',
-            'image/gif',
-            'image/svg+xml',
-            'audio/mpeg',
-            'audio/x-speex',
-            'application/ogg',
-            'application/pdf',
-            'application/vnd.oasis.opendocument.text',
-            'application/vnd.oasis.opendocument.text-template',
-            'application/vnd.oasis.opendocument.graphics',
-            'application/vnd.oasis.opendocument.graphics-template',
-            'application/vnd.oasis.opendocument.presentation',
-            'application/vnd.oasis.opendocument.presentation-template',
-            'application/vnd.oasis.opendocument.spreadsheet',
-            'application/vnd.oasis.opendocument.spreadsheet-template',
-            'application/vnd.oasis.opendocument.chart',
-            'application/vnd.oasis.opendocument.chart-template',
-            'application/vnd.oasis.opendocument.image',
-            'application/vnd.oasis.opendocument.image-template',
-            'application/vnd.oasis.opendocument.formula',
-            'application/vnd.oasis.opendocument.formula-template',
-            'application/vnd.oasis.opendocument.text-master',
-            'application/vnd.oasis.opendocument.text-web',
-            'application/x-zip',
-            'application/zip',
-            'text/plain',
-            'video/mpeg',
-            'video/mp4',
-            'video/quicktime',
-            'video/mpeg'),
+        array('server' => null,
+              'dir' => INSTALLDIR . '/file/',
+              'path' => $_path . '/file/',
+              'supported' => array('image/png',
+                                   'image/jpeg',
+                                   'image/gif',
+                                   'image/svg+xml',
+                                   'audio/mpeg',
+                                   'audio/x-speex',
+                                   'application/ogg',
+                                   'application/pdf',
+                                   'application/vnd.oasis.opendocument.text',
+                                   'application/vnd.oasis.opendocument.text-template',
+                                   'application/vnd.oasis.opendocument.graphics',
+                                   'application/vnd.oasis.opendocument.graphics-template',
+                                   'application/vnd.oasis.opendocument.presentation',
+                                   'application/vnd.oasis.opendocument.presentation-template',
+                                   'application/vnd.oasis.opendocument.spreadsheet',
+                                   'application/vnd.oasis.opendocument.spreadsheet-template',
+                                   'application/vnd.oasis.opendocument.chart',
+                                   'application/vnd.oasis.opendocument.chart-template',
+                                   'application/vnd.oasis.opendocument.image',
+                                   'application/vnd.oasis.opendocument.image-template',
+                                   'application/vnd.oasis.opendocument.formula',
+                                   'application/vnd.oasis.opendocument.formula-template',
+                                   'application/vnd.oasis.opendocument.text-master',
+                                   'application/vnd.oasis.opendocument.text-web',
+                                   'application/x-zip',
+                                   'application/zip',
+                                   'text/plain',
+                                   'video/mpeg',
+                                   'video/mp4',
+                                   'video/quicktime',
+                                   'video/mpeg'),
         'file_quota' => 5000000,
         'user_quota' => 50000000,
         'monthly_quota' => 15000000,
+        'uploads' => true,
+        'filecommand' => '/usr/bin/file',
         ),
+        'group' =>
+        array('maxaliases' => 3),
+        'oohembed' => array('endpoint' => 'http://oohembed.com/oohembed/'),
+        'search' =>
+        array('type' => 'fulltext'),
         );
 
 $config['db'] = &PEAR::getStaticProperty('DB_DataObject','options');
@@ -223,14 +279,18 @@ if (function_exists('date_default_timezone_set')) {
 // server-wide, then vhost-wide, then for a path,
 // finally for a dir (usually only need one of the last two).
 
-$_config_files = array('/etc/laconica/laconica.php',
-                  '/etc/laconica/'.$_server.'.php');
+if (isset($conffile)) {
+    $_config_files = array($conffile);
+} else {
+    $_config_files = array('/etc/laconica/laconica.php',
+                           '/etc/laconica/'.$_server.'.php');
 
-if (strlen($_path) > 0) {
-    $_config_files[] = '/etc/laconica/'.$_server.'_'.$_path.'.php';
-}
+    if (strlen($_path) > 0) {
+        $_config_files[] = '/etc/laconica/'.$_server.'_'.$_path.'.php';
+    }
 
-$_config_files[] = INSTALLDIR.'/config.php';
+    $_config_files[] = INSTALLDIR.'/config.php';
+}
 
 $_have_a_config = false;
 
diff --git a/lib/currentuserdesignaction.php b/lib/currentuserdesignaction.php
new file mode 100644 (file)
index 0000000..7c2520c
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Base class for actions that use the current user's design
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Action
+ * @package   Laconica
+ * @author    Evan Prodromou <evan@controlyourself.ca>
+ * @copyright 2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Base class for actions that use the current user's design
+ *
+ * Some pages (settings in particular) use the current user's chosen
+ * design. This superclass returns that design.
+ *
+ * @category Action
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://laconi.ca/
+ *
+ */
+
+class CurrentUserDesignAction extends Action
+{
+
+    /**
+      * Show the user's design stylesheet
+      *
+      * @return nothing
+      */
+     function showStylesheets()
+     {
+         parent::showStylesheets();
+
+         $design = $this->getDesign();
+
+         if (!empty($design)) {
+             $design->showCSS($this);
+         }
+     }
+
+    /**
+     * A design for this action
+     *
+     * if the user attribute has been set, returns that user's
+     * design.
+     *
+     * @return Design a design object to use
+     */
+
+    function getDesign()
+    {
+        $cur = common_current_user();
+
+        if (empty($cur)) {
+            return null;
+        }
+
+        return $cur->getDesign();
+    }
+
+
+}
index 9c1ae50a02c56e5be81534f905d352fd287e6944..a0df00bdcc852cc434f025ed6f062aa4496a653d 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /**
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 0dc92490cdc70d8265e001eb81530b60856fb173..a04e5f74f7a23e69873102dd09cce63e667ea7e9 100644 (file)
@@ -12,7 +12,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/lib/designsettings.php b/lib/designsettings.php
new file mode 100644 (file)
index 0000000..9650679
--- /dev/null
@@ -0,0 +1,406 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Change user password
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Settings
+ * @package   Laconica
+ * @author    Sarven Capadisli <csarven@controlyourself.ca>
+ * @author    Zach Copley <zach@controlyourself.ca>
+ * @copyright 2008-2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/lib/accountsettingsaction.php';
+require_once INSTALLDIR . '/lib/webcolor.php';
+
+class DesignSettingsAction extends AccountSettingsAction
+{
+
+    var $submitaction = null;
+
+    /**
+     * Title of the page
+     *
+     * @return string Title of the page
+     */
+
+    function title()
+    {
+        return _('Profile design');
+    }
+
+    /**
+     * Instructions for use
+     *
+     * @return instructions for use
+     */
+
+    function getInstructions()
+    {
+        return _('Customize the way your profile looks ' .
+        'with a background image and a colour palette of your choice.');
+    }
+
+    function showDesignForm($design)
+    {
+
+        $this->elementStart('form', array('method' => 'post',
+                                          'enctype' => 'multipart/form-data',
+                                          'id' => 'form_settings_design',
+                                          'class' => 'form_settings',
+                                          'action' => $this->submitaction));
+        $this->elementStart('fieldset');
+        $this->hidden('token', common_session_token());
+
+        $this->elementStart('fieldset', array('id' =>
+            'settings_design_background-image'));
+        $this->element('legend', null, _('Change background image'));
+        $this->elementStart('ul', 'form_data');
+        $this->elementStart('li');
+        $this->element('label', array('for' => 'design_background-image_file'),
+                                _('Upload file'));
+        $this->element('input', array('name' => 'design_background-image_file',
+                                      'type' => 'file',
+                                      'id' => 'design_background-image_file'));
+        $this->element('p', 'form_guide', _('You can upload your personal ' .
+            'background image. The maximum file size is 2Mb.'));
+        $this->element('input', array('name' => 'MAX_FILE_SIZE',
+                                      'type' => 'hidden',
+                                      'id' => 'MAX_FILE_SIZE',
+                                      'value' => ImageFile::maxFileSizeInt()));
+        $this->elementEnd('li');
+
+        if (!empty($design->backgroundimage)) {
+
+            $this->elementStart('li', array('id' => 'design_background-image_onoff'));
+
+            $this->element('img', array('src' =>
+                Design::url($design->backgroundimage)));
+
+            $attrs = array('name' => 'design_background-image_onoff',
+                           'type' => 'radio',
+                           'id' => 'design_background-image_on',
+                           'class' => 'radio',
+                           'value' => 'on');
+
+            if ($design->disposition & BACKGROUND_ON) {
+                $attrs['checked'] = 'checked';
+            }
+
+            $this->element('input', $attrs);
+
+            $this->element('label', array('for' => 'design_background-image_on',
+                                          'class' => 'radio'),
+                                          _('On'));
+
+            $attrs = array('name' => 'design_background-image_onoff',
+                           'type' => 'radio',
+                           'id' => 'design_background-image_off',
+                           'class' => 'radio',
+                           'value' => 'off');
+
+            if ($design->disposition & BACKGROUND_OFF) {
+                $attrs['checked'] = 'checked';
+            }
+
+            $this->element('input', $attrs);
+
+            $this->element('label', array('for' => 'design_background-image_off',
+                                          'class' => 'radio'),
+                                          _('Off'));
+            $this->element('p', 'form_guide', _('Turn background image on or off.'));
+            $this->elementEnd('li');
+
+            $this->elementStart('li');
+            $this->checkbox('design_background-image_repeat',
+                            _('Tile background image'),
+                            ($design->disposition & BACKGROUND_TILE) ? true : false );
+            $this->elementEnd('li');
+        }
+
+        $this->elementEnd('ul');
+        $this->elementEnd('fieldset');
+
+        $this->elementStart('fieldset', array('id' => 'settings_design_color'));
+        $this->element('legend', null, _('Change colours'));
+        $this->elementStart('ul', 'form_data');
+
+        try {
+
+            $bgcolor = new WebColor($design->backgroundcolor);
+
+            $this->elementStart('li');
+            $this->element('label', array('for' => 'swatch-1'), _('Background'));
+            $this->element('input', array('name' => 'design_background',
+                                          'type' => 'text',
+                                          'id' => 'swatch-1',
+                                          'class' => 'swatch',
+                                          'maxlength' => '7',
+                                          'size' => '7',
+                                          'value' => '#' . $bgcolor->hexValue()));
+            $this->elementEnd('li');
+
+            $ccolor = new WebColor($design->contentcolor);
+
+            $this->elementStart('li');
+            $this->element('label', array('for' => 'swatch-2'), _('Content'));
+            $this->element('input', array('name' => 'design_content',
+                                          'type' => 'text',
+                                          'id' => 'swatch-2',
+                                          'class' => 'swatch',
+                                          'maxlength' => '7',
+                                          'size' => '7',
+                                          'value' => '#' . $ccolor->hexValue()));
+            $this->elementEnd('li');
+
+            $sbcolor = new WebColor($design->sidebarcolor);
+
+            $this->elementStart('li');
+            $this->element('label', array('for' => 'swatch-3'), _('Sidebar'));
+            $this->element('input', array('name' => 'design_sidebar',
+                                        'type' => 'text',
+                                        'id' => 'swatch-3',
+                                        'class' => 'swatch',
+                                        'maxlength' => '7',
+                                        'size' => '7',
+                                        'value' => '#' . $sbcolor->hexValue()));
+            $this->elementEnd('li');
+
+            $tcolor = new WebColor($design->textcolor);
+
+            $this->elementStart('li');
+            $this->element('label', array('for' => 'swatch-4'), _('Text'));
+            $this->element('input', array('name' => 'design_text',
+                                        'type' => 'text',
+                                        'id' => 'swatch-4',
+                                        'class' => 'swatch',
+                                        'maxlength' => '7',
+                                        'size' => '7',
+                                        'value' => '#' . $tcolor->hexValue()));
+            $this->elementEnd('li');
+
+            $lcolor = new WebColor($design->linkcolor);
+
+            $this->elementStart('li');
+            $this->element('label', array('for' => 'swatch-5'), _('Links'));
+            $this->element('input', array('name' => 'design_links',
+                                         'type' => 'text',
+                                         'id' => 'swatch-5',
+                                         'class' => 'swatch',
+                                         'maxlength' => '7',
+                                         'size' => '7',
+                                         'value' => '#' . $lcolor->hexValue()));
+
+           $this->elementEnd('li');
+
+       } catch (WebColorException $e) {
+           common_log(LOG_ERR, 'Bad color values in design ID: ' .
+               $design->id);
+       }
+
+       $this->elementEnd('ul');
+       $this->elementEnd('fieldset');
+
+       $this->element('input', array('id' => 'settings_design_reset',
+                                     'type' => 'reset',
+                                     'value' => 'Reset',
+                                     'class' => 'submit form_action-primary',
+                                     'title' => _('Reset back to default')));
+
+        $this->submit('save', _('Save'), 'submit form_action-secondary',
+            'save', _('Save design'));
+
+        $this->elementEnd('fieldset');
+        $this->elementEnd('form');
+    }
+
+    /**
+     * Handle a post
+     *
+     * Validate input and save changes. Reload the form with a success
+     * or error message.
+     *
+     * @return void
+     */
+
+    function handlePost()
+    {
+        // XXX: Robin's workaround for a bug in PHP where $_POST
+        // and $_FILE are empty in the case that the uploaded
+        // file is bigger than PHP is configured to handle.
+
+        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            if (empty($_POST) && $_SERVER['CONTENT_LENGTH']) {
+
+                $msg = _('The server was unable to handle that much POST ' .
+                    'data (%s bytes) due to its current configuration.');
+
+                $this->showForm(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
+            }
+        }
+
+        // CSRF protection
+        $token = $this->trimmed('token');
+        if (!$token || $token != common_session_token()) {
+            $this->showForm(_('There was a problem with your session token. '.
+                              'Try again, please.'));
+            return;
+        }
+
+        if ($this->arg('save')) {
+            $this->saveDesign();
+        } else if ($this->arg('reset')) {
+            $this->resetDesign();
+        } else {
+            $this->showForm(_('Unexpected form submission.'));
+        }
+    }
+
+    /**
+     * Add the Farbtastic stylesheet
+     *
+     * @return void
+     */
+
+    function showStylesheets()
+    {
+        parent::showStylesheets();
+        $farbtasticStyle =
+          common_path('theme/base/css/farbtastic.css?version='.LACONICA_VERSION);
+
+        $this->element('link', array('rel' => 'stylesheet',
+                                     'type' => 'text/css',
+                                     'href' => $farbtasticStyle,
+                                     'media' => 'screen, projection, tv'));
+    }
+
+    /**
+     * Add the Farbtastic scripts
+     *
+     * @return void
+     */
+
+    function showScripts()
+    {
+        parent::showScripts();
+
+        $farbtasticPack = common_path('js/farbtastic/farbtastic.js');
+        $userDesignGo   = common_path('js/userdesign.go.js');
+
+        $this->element('script', array('type' => 'text/javascript',
+                                       'src' => $farbtasticPack));
+        $this->element('script', array('type' => 'text/javascript',
+                                       'src' => $userDesignGo));
+    }
+
+    /**
+     * Get a default user design
+     *
+     * @return Design design
+     */
+
+    function defaultDesign()
+    {
+        $defaults = common_config('site', 'design');
+
+        $design = new Design();
+
+        try {
+
+            $color = new WebColor();
+
+            $color->parseColor($defaults['backgroundcolor']);
+            $design->backgroundcolor = $color->intValue();
+
+            $color->parseColor($defaults['contentcolor']);
+            $design->contentcolor = $color->intValue();
+
+            $color->parseColor($defaults['sidebarcolor']);
+            $design->sidebarcolor = $color->intValue();
+
+            $color->parseColor($defaults['textcolor']);
+            $design->textcolor = $color->intValue();
+
+            $color->parseColor($defaults['linkcolor']);
+            $design->linkcolor = $color->intValue();
+
+            $design->backgroundimage = $defaults['backgroundimage'];
+
+            $design->disposition = $defaults['disposition'];
+
+        } catch (WebColorException $e) {
+            common_log(LOG_ERR, _('Bad default color settings: ' .
+                $e->getMessage()));
+        }
+
+        return $design;
+    }
+
+    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
+        // associated with the Design rather than the User was worth
+        // it. -- Zach
+
+        if ($_FILES['design_background-image_file']['error'] ==
+            UPLOAD_ERR_OK) {
+
+            $filepath = null;
+
+            try {
+                    $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),
+                    common_timestamp());
+
+            $filepath = Design::path($filename);
+
+            move_uploaded_file($imagefile->filepath, $filepath);
+
+            $original = clone($design);
+            $design->backgroundimage = $filename;
+
+            // default to on, no tile
+
+            $design->setDisposition(true, false, false);
+
+            $result = $design->update($original);
+
+            if ($result === false) {
+                common_log_db_error($design, 'UPDATE', __FILE__);
+                $this->showForm(_('Couldn\'t update your design.'));
+                return;
+            }
+        }
+    }
+
+}
index 282682133a2c64abd366ef1102fd2869c74b4648..bbf9987cff4632281c888ec0fd08c9815ad50a2a 100644 (file)
@@ -13,7 +13,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 242d2e06f280e1bd0bbe6587d2ece54b7ab1dd04..4d0df797bedf820867cdda6dc8b0ec321cd91fb8 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 8fa11a7562752f217edb65bb206f797b5732c40a..b389fc00f805634b92f3dd1eddc52e20dcaa5ff4 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /**
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -27,10 +27,9 @@ require_once INSTALLDIR.'/lib/profilelist.php';
 
 define('AVATARS_PER_PAGE', 80);
 
-class GalleryAction extends Action
+class GalleryAction extends OwnerDesignAction
 {
     var $profile = null;
-    var $user = null;
     var $page = null;
     var $tag = null;
 
diff --git a/lib/groupdesignaction.php b/lib/groupdesignaction.php
new file mode 100644 (file)
index 0000000..bc95921
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Base class for actions that use the current user's design
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Action
+ * @package   Laconica
+ * @author    Zach Copley <zach@controlyourself.ca>
+ * @copyright 2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Base class for actions that use a group's design
+ *
+ * Pages related to groups can be themed with a design. 
+ * This superclass returns that design.
+ *
+ * @category Action
+ * @package  Laconica
+ * @author   Zach Copley <zach@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 GroupDesignAction extends Action {
+
+    /** The group in question */
+    var $group = null;
+    
+    /**
+      * Show the groups's design stylesheet
+      *
+      * @return nothing
+      */
+     function showStylesheets()
+     {
+         parent::showStylesheets();
+
+         $design = $this->getDesign();
+
+         if (!empty($design)) {
+             $design->showCSS($this);
+         }
+     }
+
+    /**
+     * A design for this action
+     *
+     * if the group attribute has been set, returns that group's
+     * design.
+     *
+     * @return Design a design object to use
+     */
+
+    function getDesign()
+    {
+
+        if (empty($this->group)) {
+            return null;
+        }
+
+        return $this->group->getDesign();
+    }
+
+}
index ca674f3c8e24a925c93b194c0866ce5748fc0663..7e8d6eea3ad93cb9d8f567e4dde8abfabe8f52b3 100644 (file)
@@ -111,7 +111,6 @@ class GroupEditForm extends Form
         }
     }
 
-
     /**
      * Name of the form
      *
@@ -157,6 +156,16 @@ class GroupEditForm extends Form
                      ($this->out->arg('location')) ? $this->out->arg('location') : $this->group->location,
                      _('Location for the group, if any, like "City, State (or Region), Country"'));
         $this->out->elementEnd('li');
+        if (common_config('group', 'maxaliases') > 0) {
+            $aliases = (empty($this->group)) ? array() : $this->group->getAliases();
+            $this->out->elementStart('li');
+            $this->out->input('aliases', _('Aliases'),
+                              ($this->out->arg('aliases')) ? $this->out->arg('aliases') :
+                              (!empty($aliases)) ? implode(' ', $aliases) : '',
+                              sprintf(_('Extra nicknames for the group, comma- or space- separated, max %d'),
+                                      common_config('group', 'maxaliases')));;
+            $this->out->elementEnd('li');
+        }
         $this->out->elementEnd('ul');
     }
 
index 1b854749982198e68f89de742d2c1fd306e3ce1c..1ded5160bdcd655e2c1b2656d7eee3d8af6c3aab 100644 (file)
@@ -166,7 +166,7 @@ class GroupList extends Widget
             if ($user->isMember($this->group)) {
                 $lf = new LeaveForm($this->out, $this->group);
                 $lf->show();
-            } else {
+            } else if (!Group_block::isBlocked($this->group, $user->getProfile())) {
                 $jf = new JoinForm($this->out, $this->group);
                 $jf->show();
             }
index 90bdc10149b30cda2cb0aab8dca759d615e90c78..9e530c447ce511bca2eabb1b3d5371a10c92dc03 100644 (file)
@@ -95,6 +95,12 @@ class GroupNav extends Widget
         $cur = common_current_user();
 
         if ($cur && $cur->isAdmin($this->group)) {
+            $this->out->menuItem(common_local_url('blockedfromgroup', array('nickname' =>
+                                                                            $nickname)),
+                                 _('Blocked'),
+                                 sprintf(_('%s blocked users'), $nickname),
+                                 $action_name == 'blockedfromgroup',
+                                 'nav_group_blocked');
             $this->out->menuItem(common_local_url('editgroup', array('nickname' =>
                                                                      $nickname)),
                                  _('Admin'),
@@ -107,6 +113,12 @@ class GroupNav extends Widget
                                  sprintf(_('Add or edit %s logo'), $nickname),
                                  $action_name == 'grouplogo',
                                  'nav_group_logo');
+            $this->out->menuItem(common_local_url('groupdesignsettings', array('nickname' =>
+                                                                  $nickname)),
+                                 _('Design'),
+                                 sprintf(_('Add or edit %s design'), $nickname),
+                                 $action_name == 'groupdesignsettings',
+                                 'nav_group_design');
         }
         $this->out->elementEnd('ul');
     }
index 5d68af28bf7bdc62e8a9ba3b3892a3b03c6d8084..9b7a10f6b9ea029e45bda11f64fa7604411f04b3 100644 (file)
@@ -32,7 +32,7 @@ if (!defined('LACONICA')) {
 }
 
 /**
- * Personal tag cloud section
+ * Group tag cloud section
  *
  * @category Widget
  * @package  Laconica
@@ -64,12 +64,27 @@ class GroupTagCloudSection extends TagCloudSection
             $weightexpr='sum(exp(-(now() - notice_tag.created) / %s))';
         }
 
+        $names = $this->group->getAliases();
+
+        $names = array_merge(array($this->group->nickname), $names);
+
+        // XXX This is dumb.
+
+        $quoted = array();
+
+        foreach ($names as $name) {
+            $quoted[] = "\"$name\"";
+        }
+
+        $namestring = implode(',', $quoted);
+
         $qry = 'SELECT notice_tag.tag, '.
           $weightexpr . ' as weight ' .
           'FROM notice_tag JOIN notice ' .
           'ON notice_tag.notice_id = notice.id ' .
           'JOIN group_inbox on group_inbox.notice_id = notice.id ' .
           'WHERE group_inbox.group_id = %d ' .
+          'AND notice_tag.tag not in (%s) '.
           'GROUP BY notice_tag.tag ' .
           'ORDER BY weight DESC ';
 
@@ -85,9 +100,9 @@ class GroupTagCloudSection extends TagCloudSection
         $tag = Memcached_DataObject::cachedQuery('Notice_tag',
                                                  sprintf($qry,
                                                          common_config('tag', 'dropoff'),
-                                                         $this->group->id),
+                                                         $this->group->id,
+                                                         $namestring),
                                                  3600);
         return $tag;
     }
-
 }
index 0c93b257ed394d5433e6a2c8f147e18aad480587..52e4c4b2272d66e8a2b9e0de1612e0a73e7d308e 100644 (file)
@@ -72,7 +72,8 @@ class ImageFile
             break;
          case UPLOAD_ERR_INI_SIZE:
          case UPLOAD_ERR_FORM_SIZE:
-            throw new Exception(sprintf(_('That file is too big. The maximum file size is %d.'), $this->maxFileSize()));
+            throw new Exception(sprintf(_('That file is too big. The maximum file size is %d.'),
+                ImageFile::maxFileSize()));
             return;
          case UPLOAD_ERR_PARTIAL:
             @unlink($_FILES[$param]['tmp_name']);
index 01bbf5721a4450426f2b53a0d731979fa76c1b02..f1f6e98c195343d9d36a4becba131b701b30531a 100644 (file)
@@ -31,8 +31,6 @@ if (!defined('LACONICA')) {
     exit(1);
 }
 
-require_once INSTALLDIR.'/lib/personal.php';
-
 define('MESSAGES_PER_PAGE', 20);
 
 /**
@@ -47,11 +45,11 @@ define('MESSAGES_PER_PAGE', 20);
  * @see      OutboxAction
  */
 
-class MailboxAction extends PersonalAction
+class MailboxAction extends CurrentUserDesignAction
 {
     var $page = null;
 
-    function prepare($args) 
+    function prepare($args)
     {
         parent::prepare($args);
 
@@ -265,12 +263,12 @@ class MailboxAction extends PersonalAction
      * Returns either the name (and link) of the API client that posted the notice,
      * or one of other other channels.
      *
-     * @param string $source the source of the message 
+     * @param string $source the source of the message
      *
      * @return void
      */
 
-    function showSource($source) 
+    function showSource($source)
     {
         $source_name = _($source);
         switch ($source) {
@@ -297,4 +295,17 @@ class MailboxAction extends PersonalAction
         return;
     }
 
+    /**
+     * Mailbox actions are read only
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean
+     */
+
+    function isReadOnly($args)
+    {
+         return true;
+    }
+
 }
index 3212f382ad56e1a33d53263d3c8783df52e34456..4e2a2edd61ff78c085d46e5c7419228e7a83a57a 100644 (file)
@@ -90,7 +90,9 @@ class NoticeForm extends Form
             $this->user = common_current_user();
         }
 
-        $this->enctype = 'multipart/form-data';
+        if (common_config('attachments', 'uploads')) {
+            $this->enctype = 'multipart/form-data';
+        }
     }
 
     /**
@@ -148,12 +150,14 @@ class NoticeForm extends Form
         $this->out->element('dd', array('id' => 'notice_text-count'),
                             '140');
         $this->out->elementEnd('dl');
-        $this->out->hidden('MAX_FILE_SIZE', common_config('attachments', 'file_quota'));
-        $this->out->element('label', array('for' => 'notice_data-attach'), _('Attach'));
-        $this->out->element('input', array('id' => 'notice_data-attach',
-                                           'type' => 'file',
-                                           'name' => 'attach',
-                                           'title' => _('Attach a file')));
+        if (common_config('attachments', 'uploads')) {
+            $this->out->element('label', array('for' => 'notice_data-attach'),_('Attach'));
+            $this->out->element('input', array('id' => 'notice_data-attach',
+                                               'type' => 'file',
+                                               'name' => 'attach',
+                                               'title' => _('Attach a file')));
+            $this->out->hidden('MAX_FILE_SIZE', common_config('attachments', 'file_quota'));
+        }
         if ($this->action) {
             $this->out->hidden('notice_return-to', $this->action, 'returnto');
         }
index fadc238a4db17685668b16e68631d3959045a61a..44726a17b7b7a3da4116724bc0ed1d4c69b8d35b 100644 (file)
@@ -50,7 +50,6 @@ require_once INSTALLDIR.'/lib/attachmentlist.php';
  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link     http://laconi.ca/
  * @see      Notice
- * @see      StreamAction
  * @see      NoticeListItem
  * @see      ProfileNoticeList
  */
@@ -180,7 +179,6 @@ class NoticeListItem extends Widget
     {
         $this->showStart();
         $this->showNotice();
-        $this->showNoticeAttachments();
         $this->showNoticeInfo();
         $this->showNoticeOptions();
         $this->showEnd();
@@ -194,36 +192,10 @@ class NoticeListItem extends Widget
         $this->out->elementEnd('div');
     }
 
-    function showNoticeAttachments() {
-        if ($this->isUsedInList()) {
-            return;
-        }
-        $al = new AttachmentList($this->notice, $this->out);
-        $al->show();
-    }
-
-    function isUsedInList() {
-        return 'shownotice' !== $this->out->args['action'];
-    }
-
-/*
-    function attachmentCount($discriminant = true) {
-        $file_oembed = new File_oembed;
-        $query = "select count(*) as c from file_oembed join file_to_post on file_oembed.file_id = file_to_post.file_id where post_id=" . $this->notice->id;
-        $file_oembed->query($query);
-        $file_oembed->fetch();
-        return intval($file_oembed->c);
-    }
-*/
-
-    function showWithAttachment() {
-    }
-
     function showNoticeInfo()
     {
         $this->out->elementStart('div', 'entry-content');
         $this->showNoticeLink();
-//        $this->showWithAttachment();
         $this->showNoticeSource();
         $this->showContext();
         $this->out->elementEnd('div');
@@ -364,10 +336,6 @@ class NoticeListItem extends Widget
             // versions (>> 0.4.x)
             $this->out->raw(common_render_content($this->notice->content, $this->notice));
         }
-        $uploaded = $this->notice->getUploadedAttachment();
-        if ($uploaded) {
-            $this->out->element('a', array('href' => $uploaded[0], 'class' => 'attachment', 'id' => 'attachment-' . $uploaded[1]), $uploaded[0]);
-        }
         $this->out->elementEnd('p');
     }
 
@@ -464,7 +432,7 @@ class NoticeListItem extends Widget
             $this->out->elementStart('dl', 'response');
             $this->out->element('dt', null, _('To'));
             $this->out->elementStart('dd');
-            $this->out->element('a', array('href' => $convurl),
+            $this->out->element('a', array('href' => $convurl.'#notice-'.$this->notice->id),
                                 _('in context'));
             $this->out->elementEnd('dd');
             $this->out->elementEnd('dl');
index 183164e170a86ae2353d4c3d6266d5a6c1024ebc..f224c6c2213ed3324ad49d3f87567abc4ef988fd 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 40cb847dfa8434e96f6ca0a388aa7069e867b8f0..4f6a9609541ded863edcc62edef1ffcff10f4283 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 3af7a39cfa2a807718fafbdd864e16af90bd7cd7..0b7633284e50016bd798b4f663357de4f3c14228 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/lib/ownerdesignaction.php b/lib/ownerdesignaction.php
new file mode 100644 (file)
index 0000000..424474f
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Base class for actions that use the page owner's design
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Action
+ * @package   Laconica
+ * @author    Evan Prodromou <evan@controlyourself.ca>
+ * @copyright 2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Base class for actions that use the page owner's design
+ *
+ * Some pages have a clear "owner" -- like the profile page, subscriptions
+ * pages, etc. This superclass uses that owner's chosen design for the page
+ * design.
+ *
+ * @category Action
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://laconi.ca/
+ *
+ */
+
+class OwnerDesignAction extends Action {
+
+    /** The user for this page. */
+
+    var $user = null;
+
+    /**
+      * Show the owner's design stylesheet
+      *
+      * @return nothing
+      */
+     function showStylesheets()
+     {
+         parent::showStylesheets();
+
+         $design = $this->getDesign();
+
+         if (!empty($design)) {
+             $design->showCSS($this);
+        }
+     }
+
+    /**
+     * A design for this action
+     *
+     * if the user attribute has been set, returns that user's
+     * design.
+     *
+     * @return Design a design object to use
+     */
+
+    function getDesign()
+    {
+        if (empty($this->user)) {
+            return null;
+        }
+
+        return $this->user->getDesign();
+    }
+}
index d3f8408525ae3eb3dd43f9c5a50b37d63bca1841..9f6696b5f60237dce362ec97fd73f4357f6abaf0 100644 (file)
@@ -12,7 +12,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -56,20 +56,25 @@ class PeopleSearchResults extends ProfileList
 
     function __construct($profile, $terms, $action)
     {
-        parent::__construct($profile, $terms, $action);
+        parent::__construct($profile, $action);
+
         $this->terms = array_map('preg_quote',
                                  array_map('htmlspecialchars', $terms));
+
         $this->pattern = '/('.implode('|',$terms).')/i';
     }
 
-    function highlight($text)
+    function newProfileItem($profile)
     {
-        return preg_replace($this->pattern, '<strong>\\1</strong>', htmlspecialchars($text));
+        return new PeopleSearchResultItem($profile, $this->action);
     }
+}
 
-    function isReadOnly($args)
+class PeopleSearchResultItem extends ProfileListItem
+{
+    function highlight($text)
     {
-        return true;
+        return preg_replace($this->pattern, '<strong>\\1</strong>', htmlspecialchars($text));
     }
 }
 
diff --git a/lib/personal.php b/lib/personal.php
deleted file mode 100644 (file)
index f927323..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-/**
- * Laconica, the distributed open-source microblogging tool
- *
- * User profile page
- *
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Personal
- * @package   Laconica
- * @author    Evan Prodromou <evan@controlyourself.ca>
- * @author    Sarven Capadisli <csarven@controlyourself.ca>
- * @copyright 2008-2009 Control Yourself, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://laconi.ca/
- */
-
-if (!defined('LACONICA')) {
-    exit(1);
-}
-
-/**
- * Base class for user profile page
- *
- * @category Personal
- * @package  Laconica
- * @author   Evan Prodromou <evan@controlyourself.ca>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://laconi.ca/
- */
-
-class PersonalAction extends Action
-{
-
-    var $user = null;
-
-    function isReadOnly($args)
-    {
-         return true;
-    }
-
-    function handle($args)
-    {
-        parent::handle($args);
-    }
-
-}
index a3437ff4ddbec567d630c3f25e4985aa8b3f73a8..2519922b2b28e8292e9a4a51e270b8e6af3e8416 100644 (file)
@@ -47,9 +47,8 @@ require_once INSTALLDIR.'/lib/groupminilist.php';
  * @link     http://laconi.ca/
  */
 
-class ProfileAction extends Action
+class ProfileAction extends OwnerDesignAction
 {
-    var $user    = null;
     var $page    = null;
     var $profile = null;
     var $tag     = null;
@@ -110,7 +109,7 @@ class ProfileAction extends Action
         $this->element('h2', null, _('Subscriptions'));
 
         if ($profile) {
-            $pml = new ProfileMiniList($profile, $this->user, $this);
+            $pml = new ProfileMiniList($profile, $this);
             $cnt = $pml->show();
             if ($cnt == 0) {
                 $this->element('p', null, _('(None)'));
@@ -139,7 +138,7 @@ class ProfileAction extends Action
         $this->element('h2', null, _('Subscribers'));
 
         if ($profile) {
-            $pml = new ProfileMiniList($profile, $this->user, $this);
+            $pml = new ProfileMiniList($profile, $this);
             $cnt = $pml->show();
             if ($cnt == 0) {
                 $this->element('p', null, _('(None)'));
index a4cc235552a4d24723cbb5abe9934bfa1c9e08a6..a604230f85dd5c0554823cf0747550082dbd1610 100644 (file)
@@ -49,25 +49,37 @@ class ProfileList extends Widget
 {
     /** Current profile, profile query. */
     var $profile = null;
-    /** Owner of this list */
-    var $owner = null;
     /** Action object using us. */
     var $action = null;
 
-    function __construct($profile, $owner=null, $action=null)
+    function __construct($profile, $action=null)
     {
         parent::__construct($action);
 
         $this->profile = $profile;
-        $this->owner = $owner;
         $this->action = $action;
     }
 
     function show()
     {
+        $this->startList();
+        $cnt = $this->showProfiles();
+        $this->endList();
+        return $cnt;
+    }
 
+    function startList()
+    {
         $this->out->elementStart('ul', 'profiles');
+    }
 
+    function endList()
+    {
+        $this->out->elementEnd('ul');
+    }
+
+    function showProfiles()
+    {
         $cnt = 0;
 
         while ($this->profile->fetch()) {
@@ -75,24 +87,66 @@ class ProfileList extends Widget
             if($cnt > PROFILES_PER_PAGE) {
                 break;
             }
-            $this->showProfile();
+            $pli = $this->newListItem($this->profile);
+            $pli->show();
         }
 
-        $this->out->elementEnd('ul');
-
         return $cnt;
     }
 
-    function showProfile()
+    function newListItem($profile)
+    {
+        return new ProfileListItem($this->profile, $this->action);
+    }
+}
+
+class ProfileListItem extends Widget
+{
+    /** Current profile. */
+    var $profile = null;
+    /** Action object using us. */
+    var $action = null;
+
+    function __construct($profile, $action)
+    {
+        parent::__construct($action);
+
+        $this->profile = $profile;
+        $this->action  = $action;
+    }
+
+    function show()
+    {
+        $this->startItem();
+        $this->showProfile();
+        $this->showActions();
+        $this->endItem();
+    }
+
+    function startItem()
     {
         $this->out->elementStart('li', array('class' => 'profile',
                                              'id' => 'profile-' . $this->profile->id));
+    }
 
-        $user = common_current_user();
-        $is_own = !is_null($user) && isset($this->owner) && ($user->id === $this->owner->id);
+    function showProfile()
+    {
+        $this->startProfile();
+        $this->showAvatar();
+        $this->showFullName();
+        $this->showLocation();
+        $this->showHomepage();
+        $this->showBio();
+        $this->endProfile();
+    }
 
+    function startProfile()
+    {
         $this->out->elementStart('div', 'entity_profile vcard');
+    }
 
+    function showAvatar()
+    {
         $avatar = $this->profile->getAvatar(AVATAR_STREAM_SIZE);
         $this->out->elementStart('a', array('href' => $this->profile->profileurl,
                                             'class' => 'url'));
@@ -108,7 +162,10 @@ class ProfileList extends Widget
         $this->out->raw($this->highlight($this->profile->nickname));
         $this->out->elementEnd('span');
         $this->out->elementEnd('a');
+    }
 
+    function showFullName()
+    {
         if (!empty($this->profile->fullname)) {
             $this->out->elementStart('dl', 'entity_fn');
             $this->out->element('dt', null, 'Full name');
@@ -119,6 +176,10 @@ class ProfileList extends Widget
             $this->out->elementEnd('dd');
             $this->out->elementEnd('dl');
         }
+    }
+
+    function showLocation()
+    {
         if (!empty($this->profile->location)) {
             $this->out->elementStart('dl', 'entity_location');
             $this->out->element('dt', null, _('Location'));
@@ -127,6 +188,10 @@ class ProfileList extends Widget
             $this->out->elementEnd('dd');
             $this->out->elementEnd('dl');
         }
+    }
+
+    function showHomepage()
+    {
         if (!empty($this->profile->homepage)) {
             $this->out->elementStart('dl', 'entity_url');
             $this->out->element('dt', null, _('URL'));
@@ -138,6 +203,10 @@ class ProfileList extends Widget
             $this->out->elementEnd('dd');
             $this->out->elementEnd('dl');
         }
+    }
+
+    function showBio()
+    {
         if (!empty($this->profile->bio)) {
             $this->out->elementStart('dl', 'entity_note');
             $this->out->element('dt', null, _('Note'));
@@ -146,57 +215,33 @@ class ProfileList extends Widget
             $this->out->elementEnd('dd');
             $this->out->elementEnd('dl');
         }
+    }
 
-        # If we're on a list with an owner (subscriptions or subscribers)...
-
-        if ($this->owner) {
-            # Get tags
-            $tags = Profile_tag::getTags($this->owner->id, $this->profile->id);
-
-            $this->out->elementStart('dl', 'entity_tags');
-            $this->out->elementStart('dt');
-            if ($is_own) {
-                $this->out->element('a', array('href' => common_local_url('tagother',
-                                                                          array('id' => $this->profile->id))),
-                                    _('Tags'));
-            } else {
-                $this->out->text(_('Tags'));
-            }
-            $this->out->elementEnd('dt');
-            $this->out->elementStart('dd');
-            if ($tags) {
-                $this->out->elementStart('ul', 'tags xoxo');
-                foreach ($tags as $tag) {
-                    $this->out->elementStart('li');
-                    $this->out->element('span', 'mark_hash', '#');
-                    $this->out->element('a', array('rel' => 'tag',
-                                                   'href' => common_local_url($this->action->trimmed('action'),
-                                                                              array('nickname' => $this->owner->nickname,
-                                                                                    'tag' => $tag))),
-                                        $tag);
-                    $this->out->elementEnd('li');
-                }
-                $this->out->elementEnd('ul');
-            } else {
-                $this->out->text(_('(none)'));
-            }
-            $this->out->elementEnd('dd');
-            $this->out->elementEnd('dl');
-        }
-
-        if ($is_own) {
-            $this->showOwnerControls($this->profile);
-        }
-
+    function endProfile()
+    {
         $this->out->elementEnd('div');
+    }
 
-        $this->out->elementStart('div', 'entity_actions');
+    function showActions()
+    {
+        $this->startActions();
+        $this->showSubscribeButton();
+        $this->endActions();
+    }
 
+    function startActions()
+    {
+        $this->out->elementStart('div', 'entity_actions');
         $this->out->elementStart('ul');
+    }
 
+    function showSubscribeButton()
+    {
         // Is this a logged-in user, looking at someone else's
         // profile?
 
+        $user = common_current_user();
+
         if (!empty($user) && $this->profile->id != $user->id) {
             $this->out->elementStart('li', 'entity_subscribe');
             if ($user->isSubscribed($this->profile)) {
@@ -207,33 +252,22 @@ class ProfileList extends Widget
                 $sf->show();
             }
             $this->out->elementEnd('li');
-            $this->out->elementStart('li', 'entity_block');
-            if ($user->id == $this->owner->id) {
-                $this->showBlockForm();
-            }
-            $this->out->elementEnd('li');
         }
+    }
 
+    function endActions()
+    {
         $this->out->elementEnd('ul');
-
         $this->out->elementEnd('div');
-
-        $this->out->elementEnd('li');
     }
 
-    /* Override this in subclasses. */
-
-    function showOwnerControls($profile)
+    function endItem()
     {
-        return;
+        $this->out->elementEnd('li');
     }
 
     function highlight($text)
     {
         return htmlspecialchars($text);
     }
-
-    function showBlockForm()
-    {
-    }
 }
index 57496d0e97d77ee236a74d03adead1ad5860bdd8..09bef6f7c6158a054c806b210add89c8b3b2daea 100644 (file)
@@ -47,26 +47,20 @@ define('PROFILES_PER_MINILIST', 27);
 
 class ProfileMiniList extends ProfileList
 {
-    function show()
+    function startList()
     {
         $this->out->elementStart('ul', 'entities users xoxo');
+    }
 
-        $cnt = 0;
-
-        while ($this->profile->fetch()) {
-            $cnt++;
-            if($cnt > PROFILES_PER_MINILIST) {
-                break;
-            }
-            $this->showProfile();
-        }
-
-        $this->out->elementEnd('ul');
-
-        return $cnt;
+    function newListItem($profile)
+    {
+        return new ProfileMiniListItem($profile, $this->action);
     }
+}
 
-    function showProfile()
+class ProfileMiniListItem extends ProfileListItem
+{
+    function show()
     {
         $this->out->elementStart('li', 'vcard');
         $this->out->elementStart('a', array('title' => $this->profile->getBestName(),
index f76f16e0732f8c2dadb5b5a08e3c9b2519e483ac..ae403c65e24f815f4f38b56b9ec081e760092074 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -112,12 +112,21 @@ class QueueHandler extends Daemon
     }
 
     function stomp_dispatch() {
-        require("Stomp.php");
-        $con = new Stomp(common_config('queue','stomp_server'));
-        if (!$con->connect()) {
+
+        // use an external message queue system via STOMP
+        require_once("Stomp.php");
+
+        $server = common_config('queue','stomp_server');
+        $username = common_config('queue', 'stomp_username');
+        $password = common_config('queue', 'stomp_password');
+
+        $con = new Stomp($server);
+
+        if (!$con->connect($username, $password)) {
             $this->log(LOG_ERR, 'Failed to connect to queue server');
             return false;
         }
+
         $queue_basename = common_config('queue','queue_basename');
         // subscribe to the relevant queue (format: basename-transport)
         $con->subscribe('/queue/'.$queue_basename.'-'.$this->transport());
index 456d1793e3245bf333343aae7ee4213245420fda..1f39c60dc50207449c6ddba1a196560d0a1a390c 100644 (file)
@@ -101,7 +101,8 @@ class Router
         $main = array('login', 'logout', 'register', 'subscribe',
                       'unsubscribe', 'confirmaddress', 'recoverpassword',
                       'invite', 'favor', 'disfavor', 'sup',
-                      'block', 'subedit');
+                      'block', 'unblock', 'subedit',
+                      'groupblock', 'groupunblock');
 
         foreach ($main as $a) {
             $m->connect('main/'.$a, array('action' => $a));
@@ -131,7 +132,7 @@ class Router
         // settings
 
         foreach (array('profile', 'avatar', 'password', 'openid', 'im',
-                       'email', 'sms', 'twitter', 'design', 'other') as $s) {
+                       'email', 'sms', 'twitter', 'userdesign', 'other') as $s) {
             $m->connect('settings/'.$s, array('action' => $s.'settings'));
         }
 
@@ -164,10 +165,10 @@ class Router
                     array('action' => 'newnotice'),
                     array('replyto' => '[A-Za-z0-9_-]+'));
 
-        $m->connect('notice/:notice/file', 
-            array('action' => 'file'), 
+        $m->connect('notice/:notice/file',
+            array('action' => 'file'),
             array('notice' => '[0-9]+'));
-        
+
         $m->connect('notice/:notice',
                     array('action' => 'shownotice'),
                     array('notice' => '[0-9]+'));
@@ -222,12 +223,20 @@ class Router
                         array('nickname' => '[a-zA-Z0-9]+'));
         }
 
-        foreach (array('members', 'logo', 'rss') as $n) {
+        foreach (array('members', 'logo', 'rss', 'designsettings') as $n) {
             $m->connect('group/:nickname/'.$n,
                         array('action' => 'group'.$n),
                         array('nickname' => '[a-zA-Z0-9]+'));
         }
 
+        $m->connect('group/:nickname/blocked',
+                    array('action' => 'blockedfromgroup'),
+                    array('nickname' => '[a-zA-Z0-9]+'));
+
+        $m->connect('group/:nickname/makeadmin',
+                    array('action' => 'makeadmin'),
+                    array('nickname' => '[a-zA-Z0-9]+'));
+
         $m->connect('group/:id/id',
                     array('action' => 'groupbyid'),
                     array('id' => '[0-9]+'));
@@ -342,7 +351,8 @@ class Router
 
         $m->connect('api/favorites/:method/:argument',
                     array('action' => 'api',
-                          'apiaction' => 'favorites'));
+                          'apiaction' => 'favorites',
+                          array('method' => '(create|destroy)')));
 
         $m->connect('api/favorites/:argument',
                     array('action' => 'api',
index eafdbf131d6d00d3801adb87729e49928a024598..6f6c9a8cb08e76461b1438556de367850c39e20d 100644 (file)
@@ -212,6 +212,10 @@ class Rss10Action extends Action
         $this->element('sioc:has_creator', array('rdf:resource' => $creator_uri.'#acct'));
         $this->element('laconica:postIcon', array('rdf:resource' => $profile->avatarUrl()));
         $this->element('cc:licence', array('rdf:resource' => common_config('license', 'url')));
+        if ($notice->reply_to) {
+            $replyurl = common_local_url('shownotice', array('notice' => $notice->reply_to));
+            $this->element('sioc:reply_to', array('rdf:resource' => $replyurl));
+        }
         $this->elementEnd('item');
         $this->creators[$creator_uri] = $profile;
     }
index 7b9dbb6182543e9e14969f7564c336cddc867350..772f41883b9dde17f5b5e59f39c276628fe189ad 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -118,12 +118,20 @@ class MySQLSearch extends SearchEngine
             }
             return true;
         } else if ('identica_notices' === $this->table) {
-             $this->target->whereAdd('MATCH(content) ' .
-                                     'AGAINST (\''.addslashes($q).'\' IN BOOLEAN MODE)');
+
+            // Don't show imported notices
+            $this->target->whereAdd('notice.is_local != ' . NOTICE_GATEWAY);
+
             if (strtolower($q) != $q) {
+                $this->target->whereAdd("( MATCH(content) AGAINST ('" . addslashes($q) .
+                    "' IN BOOLEAN MODE)) OR ( MATCH(content) " .
+                    "AGAINST ('"  . addslashes(strtolower($q)) .
+                    "' IN BOOLEAN MODE))");
+            } else {
                 $this->target->whereAdd('MATCH(content) ' .
-                                        'AGAINST (\''.addslashes(strtolower($q)).'\' IN BOOLEAN MODE)', 'OR');
+                                         'AGAINST (\''.addslashes($q).'\' IN BOOLEAN MODE)');
             }
+
             return true;
         } else {
             throw new ServerException('Unknown table: ' . $this->table);
@@ -131,6 +139,28 @@ class MySQLSearch extends SearchEngine
     }
 }
 
+class MySQLLikeSearch extends SearchEngine
+{
+    function query($q)
+    {
+        if ('identica_people' === $this->table) {
+            $qry = sprintf('(nickname LIKE "%%%1$s%%" OR '.
+                           ' fullname LIKE "%%%1$s%%" OR '.
+                           ' location LIKE "%%%1$s%%" OR '.
+                           ' bio      LIKE "%%%1$s%%" OR '.
+                           ' homepage LIKE "%%%1$s%%")', addslashes($q));
+        } else if ('identica_notices' === $this->table) {
+            $qry = sprintf('content LIKE "%%%1$s%%"', addslashes($q));
+        } else {
+            throw new ServerException('Unknown table: ' . $this->table);
+        }
+
+        $this->target->whereAdd($qry);
+
+        return true;
+    }
+}
+
 class PGSearch extends SearchEngine
 {
     function query($q)
@@ -138,6 +168,9 @@ class PGSearch extends SearchEngine
         if ('identica_people' === $this->table) {
             return $this->target->whereAdd('textsearch @@ plainto_tsquery(\''.addslashes($q).'\')');
         } else if ('identica_notices' === $this->table) {
+
+            // XXX: We need to filter out gateway notices (notice.is_local = -2) --Zach
+
             return $this->target->whereAdd('to_tsvector(\'english\', content) @@ plainto_tsquery(\''.addslashes($q).'\')');
         } else {
             throw new ServerException('Unknown table: ' . $this->table);
index e74450e11f3e06dee57ce04754d81c7ac02a6bd6..34fe9373f43832cc793a59484a62f7f007b05620 100644 (file)
@@ -12,7 +12,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -148,10 +148,10 @@ You can also try your search on other engines:
 * [Tweet scan](http://www.tweetscan.com/indexi.php?s=%s)
 * [Google](http://www.google.com/search?q=site%%3A%%%%site.server%%%%+%s)
 * [Yahoo](http://search.yahoo.com/search?p=site%%3A%%%%site.server%%%%+%s)
-
+* [Collecta](http://collecta.com/#q=%s)
 
 E_O_T
-), $qe, $qe, $qe, $qe);
+), $qe, $qe, $qe, $qe, $qe);
         $this->elementStart('dl', array('id' => 'help_search', 'class' => 'help'));
         $this->element('dt', null, _('Search help'));
         $this->elementStart('dd', 'instructions');
index 595dcf1470a438fa4c94151cfaf057a730f9b381..db735216685848daf9b436b8104a47c06e811dfe 100644 (file)
@@ -13,7 +13,7 @@
  * @link     http://laconi.ca/
  *
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index db20c580436b46e0ae4bc361d65013d2234d43d2..17d3a2f64dcb1853190ee0064b6a224d01342be0 100644 (file)
@@ -43,7 +43,7 @@ if (!defined('LACONICA')) {
  * @see      Widget
  */
 
-class SettingsAction extends Action
+class SettingsAction extends CurrentUserDesignAction
 {
     /**
      * A message for the user.
diff --git a/lib/stream.php b/lib/stream.php
deleted file mode 100644 (file)
index 0cb9e0b..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-/*
- * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.     If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('LACONICA')) { exit(1); }
-
-require_once(INSTALLDIR.'/lib/personal.php');
-require_once(INSTALLDIR.'/lib/noticelist.php');
-
-class StreamAction extends PersonalAction
-{
-    function show_notice_list($notice)
-    {
-        $nl = new NoticeList($notice);
-        return $nl->show();
-    }
-}
index 0e7b9ded522c07e55811fc313ad084e046221e5c..3bd67b39c7142ffb64deb1926afa378b4315cfa0 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/lib/subscriptionlist.php b/lib/subscriptionlist.php
new file mode 100644 (file)
index 0000000..23da64c
--- /dev/null
@@ -0,0 +1,131 @@
+<?php
+
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Widget to show a list of profiles
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Public
+ * @package   Laconica
+ * @author    Evan Prodromou <evan@controlyourself.ca>
+ * @copyright 2008-2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/lib/profilelist.php';
+
+/**
+ * Widget to show a list of subscriptions
+ *
+ * @category Public
+ * @package  Laconica
+ * @author   Zach Copley <zach@controlyourself.ca>
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://laconi.ca/
+ */
+
+class SubscriptionList extends ProfileList
+{
+    /** Owner of this list */
+    var $owner = null;
+
+    function __construct($profile, $owner=null, $action=null)
+    {
+        parent::__construct($profile, $action);
+
+        $this->owner = $owner;
+    }
+
+    function newListItem($profile)
+    {
+        return new SubscriptionListItem($profile, $this->owner, $this->action);
+    }
+}
+
+class SubscriptionListItem extends ProfileListItem
+{
+    /** Owner of this list */
+    var $owner = null;
+
+    function __construct($profile, $owner, $action)
+    {
+        parent::__construct($profile, $action);
+
+        $this->owner = $owner;
+    }
+
+    function showProfile()
+    {
+        $this->startProfile();
+        $this->showAvatar();
+        $this->showFullName();
+        $this->showLocation();
+        $this->showHomepage();
+        $this->showBio();
+        // Relevant portion!
+        $this->showTags();
+        $this->endProfile();
+    }
+
+    function isOwn()
+    {
+        $user = common_current_user();
+        return (!empty($user) && ($this->owner->id == $user->id));
+    }
+
+    function showTags()
+    {
+        $tags = Profile_tag::getTags($this->owner->id, $this->profile->id);
+
+        $this->out->elementStart('dl', 'entity_tags');
+        $this->out->elementStart('dt');
+        if ($this->isOwn()) {
+            $this->out->element('a', array('href' => common_local_url('tagother',
+                                                                      array('id' => $this->profile->id))),
+                                _('Tags'));
+        } else {
+            $this->out->text(_('Tags'));
+        }
+        $this->out->elementEnd('dt');
+        $this->out->elementStart('dd');
+        if ($tags) {
+            $this->out->elementStart('ul', 'tags xoxo');
+            foreach ($tags as $tag) {
+                $this->out->elementStart('li');
+                $this->out->element('span', 'mark_hash', '#');
+                $this->out->element('a', array('rel' => 'tag',
+                                               'href' => common_local_url($this->action->trimmed('action'),
+                                                                          array('nickname' => $this->owner->nickname,
+                                                                                'tag' => $tag))),
+                                    $tag);
+                $this->out->elementEnd('li');
+            }
+            $this->out->elementEnd('ul');
+        } else {
+            $this->out->text(_('(none)'));
+        }
+        $this->out->elementEnd('dd');
+        $this->out->elementEnd('dl');
+    }
+}
index 0d882482279f6bfe934450c5d165e85bf1f58d23..2fe6ab69b7799cc19fd683a5e6c27adbfb2f96ad 100644 (file)
@@ -43,10 +43,14 @@ if (!defined('LACONICA')) {
 
 function theme_file($relative, $theme=null)
 {
-    if (!$theme) {
+    if (empty($theme)) {
         $theme = common_config('site', 'theme');
     }
-    return INSTALLDIR.'/theme/'.$theme.'/'.$relative;
+    $dir = common_config('theme', 'dir');
+    if (empty($dir)) {
+        $dir = INSTALLDIR.'/theme';
+    }
+    return $dir.'/'.$theme.'/'.$relative;
 }
 
 /**
@@ -60,13 +64,31 @@ function theme_file($relative, $theme=null)
 
 function theme_path($relative, $theme=null)
 {
-    if (!$theme) {
+    if (empty($theme)) {
         $theme = common_config('site', 'theme');
     }
+
+    $path = common_config('theme', 'path');
+
+    if (empty($path)) {
+        $path = common_config('site', 'path') . '/theme/';
+    }
+
+    if ($path[strlen($path)-1] != '/') {
+        $path .= '/';
+    }
+
+    if ($path[0] != '/') {
+        $path = '/'.$path;
+    }
+
     $server = common_config('theme', 'server');
-    if ($server) {
-        return 'http://'.$server.'/'.$theme.'/'.$relative;
-    } else {
-        return common_path('theme/'.$theme.'/'.$relative);
+
+    if (empty($server)) {
+        $server = common_config('site', 'server');
     }
+
+    // XXX: protocol
+
+    return 'http://'.$server.$path.$theme.'/'.$relative;
 }
index c1d0dc254d8c99dc7ff8bc6a730644ee44e53097..3ec082686aff7c3ba89095494d34ac1384ad4a7e 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 569bc6d7a21fb55194ea333b5ba4779967439bd2..f538a0298148e2c49659dab968e5cb7c5c89a381 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -545,7 +545,7 @@ class TwitterapiAction extends Action
             $this->init_twitter_atom();
             break;
         default:
-            $this->client_error(_('Not a supported data format.'));
+            $this->clientError(_('Not a supported data format.'));
             break;
         }
 
@@ -573,13 +573,13 @@ class TwitterapiAction extends Action
             $this->end_twitter_rss();
             break;
         default:
-            $this->client_error(_('Not a supported data format.'));
+            $this->clientError(_('Not a supported data format.'));
             break;
         }
         return;
     }
 
-    function client_error($msg, $code = 400, $content_type = 'json')
+    function clientError($msg, $code = 400, $content_type = 'json')
     {
 
         static $status = array(400 => 'Bad Request',
@@ -666,7 +666,7 @@ class TwitterapiAction extends Action
             $this->show_json_objects($profile_array);
             break;
         default:
-            $this->client_error(_('Not a supported data format.'));
+            $this->clientError(_('Not a supported data format.'));
             return;
         }
         return;
index b3a94a5a011ffe1d947d907a5ceb4e690f0548d1..f6d50b18074be35f8729ae3410ed2f6fbe30c44b 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -497,6 +497,22 @@ function common_linkify($url) {
 
     $attrs = array('href' => $longurl, 'rel' => 'external');
 
+    $is_attachment = false;
+    $attachment_id = null;
+    $has_thumb = false;
+
+    // Check to see whether there's a filename associated with this URL.
+    // If there is, it's an upload and qualifies as an attachment
+
+    $localfile = File::staticGet('url', $longurl);
+
+    if (!empty($localfile)) {
+        if (isset($localfile->filename)) {
+            $is_attachment = true;
+            $attachment_id = $localfile->id;
+        }
+    }
+
 // 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.
 //
@@ -504,24 +520,35 @@ function common_linkify($url) {
 // we're currently picking up oembeds only.
 // I think the best option is another file_view table in the db
 // and associated dbobject.
+
     $query = "select file_oembed.file_id as file_id from file join file_oembed on file.id = file_oembed.file_id where file.url='$longurl'";
     $file = new File;
     $file->query($query);
     $file->fetch();
 
     if (!empty($file->file_id)) {
+        $is_attachment = true;
+        $attachment_id = $file->file_id;
+
         $query = "select file_thumbnail.file_id as file_id from file join file_thumbnail on file.id = file_thumbnail.file_id where file.url='$longurl'";
         $file2 = new File;
         $file2->query($query);
         $file2->fetch();
 
-        if (empty($file2->file_id)) {
-            $attrs['class'] = 'attachment';
-        } else {
+        if (!empty($file2)) {
+            $has_thumb = true;
+        }
+    }
+
+    // Add clippy
+    if ($is_attachment) {
+        $attrs['class'] = 'attachment';
+        if ($has_thumb) {
             $attrs['class'] = 'attachment thumbnail';
         }
-        $attrs['id'] = "attachment-{$file->file_id}";
+        $attrs['id'] = "attachment-{$attachment_id}";
     }
+
     return XMLStringer::estring('a', $attrs, $display);
 }
 
@@ -591,7 +618,7 @@ function common_at_link($sender_id, $nickname)
 function common_group_link($sender_id, $nickname)
 {
     $sender = Profile::staticGet($sender_id);
-    $group = User_group::staticGet('nickname', common_canonical_nickname($nickname));
+    $group = User_group::getForNickname($nickname);
     if ($group && $sender->isMember($group)) {
         $attrs = array('href' => $group->permalink(),
                        'class' => 'url');
@@ -826,89 +853,91 @@ function common_broadcast_notice($notice, $remote=false)
 
 function common_enqueue_notice($notice)
 {
+    $transports = array('omb', 'sms', 'public', 'twitter', 'facebook', 'ping');
+
+    if (common_config('xmpp', 'enabled'))
+    {
+        $transports[] = 'jabber';
+    }
+
     if (common_config('queue','subsystem') == 'stomp') {
-       // use an external message queue system via STOMP
-       require_once("Stomp.php");
-       $con = new Stomp(common_config('queue','stomp_server'));
-       if (!$con->connect()) {
-               common_log(LOG_ERR, 'Failed to connect to queue server');
-               return false;
-       }
-       $queue_basename = common_config('queue','queue_basename');
-       foreach (array('jabber', 'omb', 'sms', 'public', 'twitter', 'facebook', 'ping') as $transport) {
-               if (!$con->send(
-                       '/queue/'.$queue_basename.'-'.$transport, // QUEUE
-                       $notice->id,            // BODY of the message
-                       array (                 // HEADERS of the msg
-                       'created' => $notice->created
-                       ))) {
-                       common_log(LOG_ERR, 'Error sending to '.$transport.' queue');
-                       return false;
-               }
-        common_log(LOG_DEBUG, 'complete remote queueing notice ID = ' . $notice->id . ' for ' . $transport);
-       }
-
-       //send tags as headers, so they can be used as JMS selectors
-        common_log(LOG_DEBUG, 'searching for tags ' . $notice->id);
-        $tags = array();
-       $tag = new Notice_tag();
-        $tag->notice_id = $notice->id;
-        if ($tag->find()) {
-            while ($tag->fetch()) {
-               common_log(LOG_DEBUG, 'tag found = ' . $tag->tag);
-               array_push($tags,$tag->tag);
-            }
-        }
-        $tag->free();
-
-       $con->send('/topic/laconica.'.$notice->profile_id,
-                       $notice->content,
-                       array(
-                               'profile_id' => $notice->profile_id,
-                               'created' => $notice->created,
-                               'tags' => implode($tags,' - ')
-                               )
-                       );
-        common_log(LOG_DEBUG, 'sent to personal topic ' . $notice->id);
-       $con->send('/topic/laconica.allusers',
-                       $notice->content,
-                       array(
-                               'profile_id' => $notice->profile_id,
-                               'created' => $notice->created,
-                               'tags' => implode($tags,' - ')
-                               )
-                       );
-        common_log(LOG_DEBUG, 'sent to catch-all topic ' . $notice->id);
-       $result = true;
+        common_enqueue_notice_stomp($notice, $transports);
     }
     else {
-       // in any other case, 'internal'
-       foreach (array('jabber', 'omb', 'sms', 'public', 'twitter', 'facebook', 'ping') as $transport) {
-               $qi = new Queue_item();
-               $qi->notice_id = $notice->id;
-               $qi->transport = $transport;
-               $qi->created = $notice->created;
-               $result = $qi->insert();
-               if (!$result) {
-                   $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
-                   common_log(LOG_ERR, 'DB error inserting queue item: ' . $last_error->message);
-                   return false;
-               }
-               common_log(LOG_DEBUG, 'complete queueing notice ID = ' . $notice->id . ' for ' . $transport);
-        }
+        common_enqueue_notice_db($notice, $transports);
     }
     return $result;
 }
 
-function common_post_inbox_transports()
+function common_enqueue_notice_stomp($notice, $transports)
 {
-    $transports = array('omb', 'sms');
+    // use an external message queue system via STOMP
+    require_once("Stomp.php");
+
+    $server = common_config('queue','stomp_server');
+    $username = common_config('queue', 'stomp_username');
+    $password = common_config('queue', 'stomp_password');
+
+    $con = new Stomp($server);
+
+    if (!$con->connect($username, $password)) {
+        common_log(LOG_ERR, 'Failed to connect to queue server');
+        return false;
+    }
+
+    $queue_basename = common_config('queue','queue_basename');
+
+    foreach ($transports as $transport) {
+        $result = $con->send('/queue/'.$queue_basename.'-'.$transport, // QUEUE
+                             $notice->id,              // BODY of the message
+                             array ('created' => $notice->created));
+        if (!$result) {
+            common_log(LOG_ERR, 'Error sending to '.$transport.' queue');
+            return false;
+        }
+        common_log(LOG_DEBUG, 'complete remote queueing notice ID = ' . $notice->id . ' for ' . $transport);
+    }
 
-    if (common_config('xmpp', 'enabled')) {
-        $transports = array_merge($transports, array('jabber', 'public'));
+    //send tags as headers, so they can be used as JMS selectors
+    common_log(LOG_DEBUG, 'searching for tags ' . $notice->id);
+    $tags = array();
+    $tag = new Notice_tag();
+    $tag->notice_id = $notice->id;
+    if ($tag->find()) {
+        while ($tag->fetch()) {
+            common_log(LOG_DEBUG, 'tag found = ' . $tag->tag);
+            array_push($tags,$tag->tag);
+        }
     }
+    $tag->free();
+
+    $con->send('/topic/laconica.'.$notice->profile_id,
+               $notice->content,
+               array(
+                     'profile_id' => $notice->profile_id,
+                     'created' => $notice->created,
+                     'tags' => implode($tags,' - ')
+                     )
+               );
+    common_log(LOG_DEBUG, 'sent to personal topic ' . $notice->id);
+    $con->send('/topic/laconica.allusers',
+               $notice->content,
+               array(
+                     'profile_id' => $notice->profile_id,
+                     'created' => $notice->created,
+                     'tags' => implode($tags,' - ')
+                     )
+               );
+    common_log(LOG_DEBUG, 'sent to catch-all topic ' . $notice->id);
+    $result = true;
+}
 
-    return $transports;
+function common_enqueue_notice_db($notice, $transports)
+{
+    // in any other case, 'internal'
+    foreach ($transports as $transport) {
+        common_enqueue_notice_transport($notice, $transport);
+    }
 }
 
 function common_enqueue_notice_transport($notice, $transport)
@@ -1322,7 +1351,13 @@ function common_session_token()
 
 function common_cache_key($extra)
 {
-    return 'laconica:' . common_keyize(common_config('site', 'name')) . ':' . $extra;
+    $base_key = common_config('memcached', 'base');
+
+    if (empty($base_key)) {
+        $base_key = common_keyize(common_config('site', 'name'));
+    }
+
+    return 'laconica:' . $base_key . ':' . $extra;
 }
 
 function common_keyize($str)
@@ -1371,3 +1406,68 @@ function common_database_tablename($tablename)
   //table prefixes could be added here later
   return $tablename;
 }
+
+function common_shorten_url($long_url)
+{
+    $user = common_current_user();
+    if (empty($user)) {
+        // common current user does not find a user when called from the XMPP daemon
+        // therefore we'll set one here fix, so that XMPP given URLs may be shortened
+        $svc = 'ur1.ca';
+    } else {
+        $svc = $user->urlshorteningservice;
+    }
+
+    $curlh = curl_init();
+    curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait
+    curl_setopt($curlh, CURLOPT_USERAGENT, 'Laconica');
+    curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true);
+
+    switch($svc) {
+     case 'ur1.ca':
+        require_once INSTALLDIR.'/lib/Shorturl_api.php';
+        $short_url_service = new LilUrl;
+        $short_url = $short_url_service->shorten($long_url);
+        break;
+
+     case '2tu.us':
+        $short_url_service = new TightUrl;
+        require_once INSTALLDIR.'/lib/Shorturl_api.php';
+        $short_url = $short_url_service->shorten($long_url);
+        break;
+
+     case 'ptiturl.com':
+        require_once INSTALLDIR.'/lib/Shorturl_api.php';
+        $short_url_service = new PtitUrl;
+        $short_url = $short_url_service->shorten($long_url);
+        break;
+
+     case 'bit.ly':
+        curl_setopt($curlh, CURLOPT_URL, 'http://bit.ly/api?method=shorten&long_url='.urlencode($long_url));
+        $short_url = current(json_decode(curl_exec($curlh))->results)->hashUrl;
+        break;
+
+     case 'is.gd':
+        curl_setopt($curlh, CURLOPT_URL, 'http://is.gd/api.php?longurl='.urlencode($long_url));
+        $short_url = curl_exec($curlh);
+        break;
+     case 'snipr.com':
+        curl_setopt($curlh, CURLOPT_URL, 'http://snipr.com/site/snip?r=simple&link='.urlencode($long_url));
+        $short_url = curl_exec($curlh);
+        break;
+     case 'metamark.net':
+        curl_setopt($curlh, CURLOPT_URL, 'http://metamark.net/api/rest/simple?long_url='.urlencode($long_url));
+        $short_url = curl_exec($curlh);
+        break;
+     case 'tinyurl.com':
+        curl_setopt($curlh, CURLOPT_URL, 'http://tinyurl.com/api-create.php?url='.urlencode($long_url));
+        $short_url = curl_exec($curlh);
+        break;
+     default:
+        $short_url = false;
+    }
+
+    curl_close($curlh);
+
+    return $short_url;
+}
\ No newline at end of file
diff --git a/lib/webcolor.php b/lib/webcolor.php
new file mode 100644 (file)
index 0000000..f3ca6e9
--- /dev/null
@@ -0,0 +1,192 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Base class for deleting things
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Personal
+ * @package   Laconica
+ * @author    Zach Copley <zach@controlyourself.ca>
+ * @copyright 2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+     exit(1);
+}
+
+class WebColor {
+
+    // XXX: Maybe make getters and setters for r,g,b values and tuples,
+    // e.g.: to support this kinda CSS representation: rgb(255,0,0)
+    // http://www.w3.org/TR/CSS21/syndata.html#color-units
+
+    var $red   = 0;
+    var $green = 0;
+    var $blue  = 0;
+
+    /**
+     * Constructor
+     *
+     * @return nothing
+     */
+
+    function __construct($color = null)
+    {
+        if (isset($color)) {
+            $this->parseColor($color);
+        }
+    }
+
+    /**
+     * Parses input to and tries to determine whether the color
+     * is being specified via an integer or hex tuple and sets
+     * the RGB instance variables accordingly.
+     *
+     * XXX: Maybe support (r,g,b) style, and array?
+     *
+     * @param mixed $color
+     *
+     * @return nothing
+     */
+
+    function parseColor($color) {
+
+        if (is_numeric($color)) {
+            $this->setIntColor($color);
+        } else {
+
+            // XXX named colors
+
+            // XXX: probably should do even more validation
+
+            if (preg_match('/(#([0-9A-Fa-f]{3,6})\b)/u', $color) > 0) {
+                $this->setHexColor($color);
+            } else {
+                $errmsg = _('%s is not a valid color!');
+                throw new WebColorException(sprintf($errmsg, $color));
+            }
+        }
+    }
+
+    /**
+     * @param string $name
+     *
+     * @return nothing
+     */
+
+    function setNamedColor($name)
+    {
+        // XXX Implement this
+    }
+
+
+    /**
+     * Sets the RGB color values from a a hex tuple
+     *
+     * @param string $hexcolor
+     *
+     * @return nothing
+     */
+
+    function setHexColor($hexcolor) {
+
+        if ($hexcolor[0] == '#') {
+            $hexcolor = substr($hexcolor, 1);
+        }
+
+        if (strlen($hexcolor) == 6) {
+            list($r, $g, $b) = array($hexcolor[0].$hexcolor[1],
+                                     $hexcolor[2].$hexcolor[3],
+                                     $hexcolor[4].$hexcolor[5]);
+        } elseif (strlen($hexcolor) == 3) {
+            list($r, $g, $b) = array($hexcolor[0].$hexcolor[0],
+                                     $hexcolor[1].$hexcolor[1],
+                                     $hexcolor[2].$hexcolor[2]);
+        } else {
+            $errmsg = _('%s is not a valid color! Use 3 or 6 hex chars.');
+            throw new WebColorException(sprintf($errmsg, $hexcolor));
+        }
+
+        $this->red   = hexdec($r);
+        $this->green = hexdec($g);
+        $this->blue  = hexdec($b);
+
+    }
+
+    /**
+     * Sets the RGB color values from a 24-bit integer
+     *
+     * @param int $intcolor
+     *
+     * @return nothing
+     */
+
+    function setIntColor($intcolor)
+    {
+        // We could do 32 bit and have an alpha channel because
+        // Sarven wants one real bad, but nah.
+
+        $this->red   = $intcolor >> 16;
+        $this->green = $intcolor >> 8 & 0xFF;
+        $this->blue  = $intcolor & 0xFF;
+
+    }
+
+    /**
+     * Returns a hex tuple of the RGB color useful for output in HTML
+     *
+     * @return string
+     */
+
+    function hexValue() {
+
+        $hexcolor  = (strlen(dechex($this->red)) < 2 ? '0' : '' ) .
+            dechex($this->red);
+        $hexcolor .= (strlen(dechex($this->green)) < 2 ? '0' : '') .
+            dechex($this->green);
+        $hexcolor .= (strlen(dechex($this->blue)) < 2 ? '0' : '') .
+            dechex($this->blue);
+
+        return strtoupper($hexcolor);
+
+    }
+
+    /**
+     * Returns a 24-bit packed integer representation of the RGB color
+     * for convenient storage in the DB
+     *
+     * XXX: probably could just use hexdec() instead
+     *
+     * @return int
+     */
+
+    function intValue()
+    {
+        $intcolor = 256 * 256 * $this->red + 256 * $this->green + $this->blue;
+        return $intcolor;
+    }
+
+}
+
+class WebColorException extends Exception
+{
+}
+
+?>
\ No newline at end of file
index 91015fd4503f4875d489807a8b7bdf5642c561ee..986e09c25e4c353cb0d849b5a4736edfd6638cac 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -22,7 +22,7 @@ if (!defined('LACONICA')) { exit(1); }
 require_once(INSTALLDIR.'/lib/queuehandler.php');
 
 /**
- * Common superclass for all XMPP-using queue handlers. They all need to 
+ * Common superclass for all XMPP-using queue handlers. They all need to
  * service their message queues on idle, and forward any incoming messages
  * to the XMPP listener connection. So, we abstract out common code to a
  * superclass.
@@ -30,12 +30,11 @@ require_once(INSTALLDIR.'/lib/queuehandler.php');
 
 class XmppQueueHandler extends QueueHandler
 {
-    
     function start()
     {
         # Low priority; we don't want to receive messages
         $this->log(LOG_INFO, "INITIALIZE");
-        $this->conn = jabber_connect($this->_id);
+        $this->conn = jabber_connect($this->_id.$this->transport());
         if ($this->conn) {
             $this->conn->addEventHandler('message', 'forward_message', $this);
             $this->conn->addEventHandler('reconnect', 'handle_reconnect', $this);
@@ -44,7 +43,7 @@ class XmppQueueHandler extends QueueHandler
         }
         return !is_null($this->conn);
     }
-    
+
     function handle_reconnect(&$pl)
     {
         $this->conn->processUntil('session_start');
@@ -63,7 +62,7 @@ class XmppQueueHandler extends QueueHandler
             die($e->getMessage());
         }
     }
-    
+
     function forward_message(&$pl)
     {
         if ($pl['type'] != 'chat') {
diff --git a/scripts/allsites.php b/scripts/allsites.php
new file mode 100755 (executable)
index 0000000..d6768c2
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/env php
+<?php
+/*
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2009, Control Yourself, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+# Abort if called from a web server
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+$helptext = <<<ENDOFHELP
+allsites.php - list all sites configured for multi-site use
+
+returns the nickname of each site configured for multi-site use
+
+ENDOFHELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+$sn = new Status_network();
+
+if ($sn->find()) {
+    while ($sn->fetch()) {
+        print "$sn->nickname\n";
+    }
+}
\ No newline at end of file
diff --git a/scripts/commandline.inc b/scripts/commandline.inc
new file mode 100644 (file)
index 0000000..4a7757f
--- /dev/null
@@ -0,0 +1,138 @@
+<?php
+/*
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// -*- mode: php -*-
+
+# Abort if called from a web server
+
+if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
+    print "This script must be run from the command line\n";
+    exit();
+}
+
+define('LACONICA', true);
+
+// Set various flags so we don't time out on long-running processes
+
+ini_set("max_execution_time", "0");
+ini_set("max_input_time", "0");
+set_time_limit(0);
+mb_internal_encoding('UTF-8');
+
+// Add extlib to our path so we can get Console_Getopt
+
+$_extra_path = array(INSTALLDIR.'/extlib/');
+
+set_include_path(implode(PATH_SEPARATOR, $_extra_path) . PATH_SEPARATOR . get_include_path());
+
+require_once 'Console/Getopt.php';
+
+// Note: $shortoptions and $longoptions should be pre-defined!
+
+$_default_shortoptions = 'qvhc:s:p:';
+
+$_default_longoptions = array('quiet', 'verbose', 'help', 'conf=', 'server=', 'path=');
+
+if (isset($shortoptions)) {
+    $shortoptions .= $_default_shortoptions;
+} else {
+    $shortoptions = $_default_shortoptions;
+}
+
+if (isset($longoptions)) {
+    $longoptions = array_merge($longoptions, $_default_longoptions);
+} else {
+    $longoptions = $_default_longoptions;
+}
+
+$parser = new Console_Getopt();
+
+list($options, $args) = $parser->getopt($argv, $shortoptions, $longoptions);
+
+function show_help()
+{
+    global $helptext;
+
+    $_default_help_text = <<<END_OF_DEFAULT
+General options:
+
+    -q --quiet           Quiet (little output)
+    -v --verbose         Verbose (lots of output)
+    -c --conf=<filename> Use <filename> as config file
+    -s --server=<name>   Use <name> as server name
+    -p --path=<path>     Use <path> as path name
+    -h --help            Show this message and quit.
+
+END_OF_DEFAULT;
+        if (isset($helptext)) {
+            print $helptext;
+        }
+        print $_default_help_text;
+        exit(0);
+}
+
+foreach ($options as $option) {
+
+    switch ($option[0]) {
+     case '--server':
+     case 's':
+        $server = $option[1];
+        break;
+
+     case '--path':
+     case 'p':
+        $path = $option[1];
+        break;
+
+     case '--conf':
+     case 'c':
+        $conffile = $option[1];
+        break;
+
+     case '--help':
+     case 'h':
+        show_help();
+    }
+}
+
+require_once INSTALLDIR . '/lib/common.php';
+
+set_error_handler('common_error_handler');
+
+function have_option($str)
+{
+   global $options;
+   foreach ($options as $option) {
+       if ($option[0] == $str) {
+          return true;
+       }
+   }
+   return false;
+}
+
+function get_option_value($str)
+{
+   global $options;
+   foreach ($options as $option) {
+       if ($option[0] == $str) {
+          return $option[1];
+       }
+   }
+   return null;
+}
\ No newline at end of file
index b18eaa2cd3fa3d783c911997efcfe920ccb36c1e..90e1ec63c0d374411af49e07c3d6fc3d8c430f13 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-# Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit(1);
-}
-
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
+$helptext = <<<ENDOFHELP
+USAGE: decache.php <table> <id> [<column>]
+Clears the cache for the object in table <table> with id <id>
+If <column> is specified, use that instead of 'id'
+ENDOFHELP;
 
-if ($argc < 3 || $argc > 4) {
-    print "USAGE: decache.php <table> <id> [<column>]\n";
-    print "Clears the cache for the object in table <table> with id <id>.\n\n";
-    print "If <column> is specified, use that instead of 'id'\n";
-    exit(1);
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+if (count($args) < 2 || count($args) > 3) {
+    show_help();
 }
 
-$table = $argv[1];
-$id = $argv[2];
-if ($argc > 3) {
-    $column = $argv[3];
+$table = $args[0];
+$id = $args[1];
+if (count($args) > 2) {
+    $column = $args[2];
 } else {
-    $colum = 'id';
+    $column = 'id';
 }
 
 $object = Memcached_DataObject::staticGet($table, $column, $id);
diff --git a/scripts/delete_status_network.sh b/scripts/delete_status_network.sh
new file mode 100755 (executable)
index 0000000..3218738
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+source /etc/laconica/setup.cfg
+
+export nickname=$1
+
+export database=$nickname$DBBASE
+
+# Create the db
+
+mysqladmin -h $DBHOST -u $ADMIN --password=$ADMINPASS -f drop $database
+
+mysql -h $DBHOST -u $ADMIN --password=$ADMINPASS $SITEDB << ENDOFCOMMANDS
+
+delete from status_network where nickname = '$nickname';
+
+ENDOFCOMMANDS
+
+for top in $AVATARBASE $FILEBASE $BACKGROUNDBASE; do
+    rm -Rf $top/$nickname
+done
index 40f60da5d80996fc19901c61546fc91be72e57ba..05e1d9366bed3a65cea394f1e0118bfc8ea2adf6 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-# Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
-require_once(INSTALLDIR . '/lib/mail.php');
-require_once(INSTALLDIR . '/lib/queuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_ENJIT_HELP
+Daemon script for watching new notices and posting to enjit.
+
+    -i --id           Identity (default none)
+
+END_OF_ENJIT_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+require_once INSTALLDIR . '/lib/mail.php';
+require_once INSTALLDIR . '/lib/queuehandler.php';
 
 set_error_handler('common_error_handler');
 
 class EnjitQueueHandler extends QueueHandler
 {
-    
     function transport()
     {
         return 'enjit';
@@ -60,7 +63,6 @@ class EnjitQueueHandler extends QueueHandler
                     return "skipped";
                 }
 
-
                 #
                 # Build an Atom message from the notice
                 #
@@ -93,8 +95,8 @@ class EnjitQueueHandler extends QueueHandler
         $ch   = curl_init();
 
         curl_setopt($ch, CURLOPT_URL, $url);
-                curl_setopt($ch, CURLOPT_HEADER, 1); 
+
+                curl_setopt($ch, CURLOPT_HEADER, 1);
         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
         curl_setopt($ch, CURLOPT_POST, 1) ;
         curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
@@ -103,7 +105,7 @@ class EnjitQueueHandler extends QueueHandler
                 #
         # curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
         # curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
-                # curl_setopt($ch, CURLOPT_VERBOSE, 1); 
+                # curl_setopt($ch, CURLOPT_VERBOSE, 1);
 
         $result = curl_exec($ch);
 
@@ -115,13 +117,18 @@ class EnjitQueueHandler extends QueueHandler
 
                 return $code;
     }
-    
 
 }
 
-mb_internal_encoding('UTF-8');
-
-$id = ($argc > 1) ? $argv[1] : null;
+if (have_option('-i')) {
+    $id = get_option_value('-i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
 $handler = new EnjitQueueHandler($id);
 
index c6859cb219121f10b4885b277f94702ed7005a9b..05a35577fea71b87f34a35f3746b6a0c68e9c8d7 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-# Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
-require_once(INSTALLDIR . '/lib/facebookutil.php');
-require_once(INSTALLDIR . '/lib/queuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_FACEBOOK_HELP
+Daemon script for pushing new notices to Facebook.
+
+    -i --id           Identity (default none)
+
+END_OF_FACEBOOK_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
 
-set_error_handler('common_error_handler');
+require_once INSTALLDIR . '/lib/facebookutil.php';
+require_once INSTALLDIR . '/lib/queuehandler.php';
 
 class FacebookQueueHandler extends QueueHandler
 {
-    
     function transport()
     {
         return 'facebook';
     }
-    
+
     function start()
     {
         $this->log(LOG_INFO, "INITIALIZE");
@@ -51,20 +52,22 @@ class FacebookQueueHandler extends QueueHandler
     {
         return facebookBroadcastNotice($notice);
     }
-    
+
     function finish()
     {
     }
 
 }
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-
-mb_internal_encoding('UTF-8');
-
-$id = ($argc > 1) ? $argv[1] : null;
+if (have_option('i')) {
+    $id = get_option_value('i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
 $handler = new FacebookQueueHandler($id);
 
diff --git a/scripts/fixup_conversations.php b/scripts/fixup_conversations.php
new file mode 100755 (executable)
index 0000000..2cfa422
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/env php
+<?php
+/*
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+common_log(LOG_INFO, 'Fixing up conversations.');
+
+$notice = new Notice();
+$notice->whereAdd('conversation is null');
+$notice->orderBy('id');
+
+$cnt = $notice->find();
+
+print "Found $cnt notices.\n";
+
+while ($notice->fetch()) {
+
+    print "$notice->id =>";
+
+    $orig = clone($notice);
+
+    if (empty($notice->reply_to)) {
+        $notice->conversation = $notice->id;
+    } else {
+        $reply = Notice::staticGet('id', $notice->reply_to);
+
+        if (empty($reply)) {
+            common_log(LOG_WARNING, "Replied-to notice $notice->reply_to not found.");
+            $notice->conversation = $notice->id;
+        } else if (empty($reply->conversation)) {
+            common_log(LOG_WARNING, "Replied-to notice $reply->id has no conversation ID.");
+            $notice->conversation = $notice->id;
+        } else {
+            $notice->conversation = $reply->conversation;
+        }
+    }
+
+    print "$notice->conversation";
+
+    $result = $notice->update($orig);
+
+    if (!$result) {
+        common_log_db_error($notice, 'UPDATE', __FILE__);
+        continue;
+    }
+
+    print ".\n";
+}
index 6f65c78a1fe8f8971c3ed5ad0d90b04cd5f2ed63..bd38e3105575a2720c3f45f080cf1197d5edcf72 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index a5c8a0a5acb5e0dcf9afacb932aacd4d8151c5a1..3e55edef17257fbf4233f858947fbef33643a25c 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index c27185546e7004c150f0290274621dcbf48311d1..3e7eb7acb872bc425ecad7020636d347d69d0e54 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 6010e21d1121e418be8b0f7e3395d4b1191d7677..9d8cfda08c29c572283dc153f8ce7e51884e1440 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
index 1693760914e32e96c6227e4715fd3c669728aead..8c9a9127fdd772f25658020151063848101758bf 100644 (file)
  */
 
 # Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit(1);
-}
-
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
 
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
-require_once('DB.php');
+$helptext = <<<ENDOFHELP
+fixup_utf8.php <maxdate> <maxid> <minid>
+
+Fixup records in a database that stored the data incorrectly (pre-0.7.4 for Laconica).
+
+ENDOFHELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+require_once 'DB.php';
 
 class UTF8FixerUpper
 {
@@ -356,9 +353,9 @@ class UTF8FixerUpper
     }
 }
 
-$max_date = ($argc > 1) ? $argv[1] : null;
-$max_id = ($argc > 2) ? $argv[2] : null;
-$min_id = ($argc > 3) ? $argv[3] : null;
+$max_date = (count($args) > 0) ? $args[0] : null;
+$max_id = (count($args) > 1) ? $args[1] : null;
+$min_id = (count($args) > 2) ? $args[2] : null;
 
 $fixer = new UTF8FixerUpper(array('max_date' => $max_date,
                                   'max_notice' => $max_id,
index 4f5704249319b018beee7be4dcdc613fe0997621..9927cc6d95b07a9f4a831b204dde44a6ce5b088b 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-# Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
+$helptext = <<<ENDOFHELP
+getpiddir.php - print out the currently configured PID directory
+
+ENDOFHELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
 
-echo common_config('daemon','piddir');
+echo common_config('daemon', 'piddir');
index 4e49f9bd4bc776e1ef7b5928dd3deab39972ac89..97c230784f73e5be9897bfc536583a6cdd97c900 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * daemon names.
  */
 
-# Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
+$helptext = <<<ENDOFHELP
+getvaliddaemons.php - print out the currently configured PID directory
+
+ENDOFHELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
 
 if(common_config('xmpp','enabled')) {
     echo "xmppdaemon.php jabberqueuehandler.php publicqueuehandler.php ";
     echo "xmppconfirmhandler.php ";
 }
-if(common_config('memcached','enabled')) {
-    echo "memcachedqueuehandler.php ";
-}
 if(common_config('twitterbridge','enabled')) {
     echo "twitterstatusfetcher.php ";
 }
index 7d14f0efe6258fd85e8fd5ac591148ebd0b6a7d3..4883fea2015be1e1432fdb4f8910c3100069f43c 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
 
 # Abort if called from a web server
 
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
+$helptext = <<<ENDOFHELP
+inbox_users.php <idfile>
 
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
+Update users to use inbox table. Listed in an ID file, default 'ids.txt'.
 
-require_once(INSTALLDIR . '/lib/common.php');
+ENDOFHELP;
 
-$id_file = ($argc > 1) ? $argv[1] : 'ids.txt';
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+$id_file = (count($args) > 1) ? $args[0] : 'ids.txt';
 
 common_log(LOG_INFO, 'Updating user inboxes.');
 
 $ids = file($id_file);
 
 foreach ($ids as $id) {
-       
+
        $user = User::staticGet('id', $id);
 
        if (!$user) {
                common_log(LOG_WARNING, 'No such user: ' . $id);
                continue;
        }
-       
+
        if ($user->inboxed) {
                common_log(LOG_WARNING, 'Already inboxed: ' . $id);
                continue;
        }
-       
+
     common_log(LOG_INFO, 'Updating inbox for user ' . $user->id);
-       
+
        $user->query('BEGIN');
-       
+
        $old_inbox = new Notice_inbox();
        $old_inbox->user_id = $user->id;
-       
+
        $result = $old_inbox->delete();
-       
+
        if (is_null($result) || $result === false) {
                common_log_db_error($old_inbox, 'DELETE', __FILE__);
                continue;
        }
 
        $old_inbox->free();
-       
+
        $inbox = new Notice_inbox();
-       
+
        $result = $inbox->query('INSERT INTO notice_inbox (user_id, notice_id, created) ' .
                                                        'SELECT ' . $user->id . ', notice.id, notice.created ' .
                                                        'FROM subscription JOIN notice ON subscription.subscribed = notice.profile_id ' .
@@ -80,30 +76,30 @@ foreach ($ids as $id) {
                                                        'AND notice.created >= subscription.created ' .
                                                        'AND NOT EXISTS (SELECT user_id, notice_id ' .
                                                        'FROM notice_inbox ' .
-                                                       'WHERE user_id = ' . $user->id . ' ' . 
+                                                       'WHERE user_id = ' . $user->id . ' ' .
                                                        'AND notice_id = notice.id) ' .
                                                        'ORDER BY notice.created DESC ' .
                                                        'LIMIT 0, 1000');
-       
+
        if (is_null($result) || $result === false) {
                common_log_db_error($inbox, 'INSERT', __FILE__);
                continue;
        }
-       
+
        $orig = clone($user);
        $user->inboxed = 1;
        $result = $user->update($orig);
-       
+
        if (!$result) {
                common_log_db_error($user, 'UPDATE', __FILE__);
                continue;
        }
-       
+
        $user->query('COMMIT');
-       
+
        $inbox->free();
        unset($inbox);
-       
+
        if ($cache) {
                $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id));
                $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id . ';last'));
index 8b6e974c0aa7af3a87cca507ccfbf10f4d6b1920..5b581629d33fc79a58844ab236f46a73944e3263 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-# Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
-require_once(INSTALLDIR . '/lib/jabber.php');
-require_once(INSTALLDIR . '/lib/xmppqueuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_JABBER_HELP
+Daemon script for pushing new notices to Jabber users.
+
+    -i --id           Identity (default none)
 
-set_error_handler('common_error_handler');
+END_OF_JABBER_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+require_once INSTALLDIR . '/lib/common.php';
+require_once INSTALLDIR . '/lib/jabber.php';
+require_once INSTALLDIR . '/lib/xmppqueuehandler.php';
 
 class JabberQueueHandler extends XmppQueueHandler
 {
-
     var $conn = null;
 
     function transport()
@@ -61,13 +63,16 @@ if (common_config('xmpp','enabled')==false) {
     exit();
 }
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
-
-$resource = ($argc > 1) ? $argv[1] : (common_config('xmpp','resource') . '-queuehandler');
+if (have_option('i')) {
+    $id = get_option_value('i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
-$handler = new JabberQueueHandler($resource);
+$handler = new JabberQueueHandler($id);
 
 $handler->runOnce();
index b9facec1a56f2cd38a673c5134ca585c9dfc27b3..cfb11a36fc11e113c9b1f983f744fd21d954d249 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-# Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
+$helptext = <<<END_OF_HELP
+Script for converting mail messages into notices. Takes message body
+as STDIN.
+
+END_OF_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
 require_once(INSTALLDIR . '/lib/mail.php');
 require_once('Mail/mimeDecode.php');
 
@@ -36,7 +36,6 @@ require_once('Mail/mimeDecode.php');
 
 class MailerDaemon
 {
-
     function __construct()
     {
     }
@@ -66,7 +65,13 @@ class MailerDaemon
             return true;
         }
         $msg = $this->cleanup_msg($msg);
-        $this->add_notice($user, $msg);
+        $err = $this->add_notice($user, $msg);
+        if (is_string($err)) {
+            $this->error($from, $err);
+            return false;
+        } else {
+            return true;
+        }
     }
 
     function error($from, $msg)
@@ -130,17 +135,15 @@ class MailerDaemon
 
     function add_notice($user, $msg)
     {
-        // should test
-        // $msg_shortened = common_shorten_links($msg);
-        // if (mb_strlen($msg_shortened) > 140) ERROR and STOP
         $notice = Notice::saveNew($user->id, $msg, 'mail');
         if (is_string($notice)) {
             $this->log(LOG_ERR, $notice);
-            return;
+            return $notice;
         }
         common_broadcast_notice($notice);
         $this->log(LOG_INFO,
                    'Added notice ' . $notice->id . ' from user ' . $user->nickname);
+        return true;
     }
 
     function parse_message($fname)
index cdcea51dc737d1e6eaa9ab884aaa2779e9c50ae6..1587192b6fb5b9c1532e6af18411fa76dbc44ba4 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-# Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
-require_once(INSTALLDIR . '/lib/omb.php');
-require_once(INSTALLDIR . '/lib/queuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_OMB_HELP
+Daemon script for pushing new notices to OpenMicroBlogging subscribers.
+
+    -i --id           Identity (default none)
+
+END_OF_OMB_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+require_once INSTALLDIR . '/lib/omb.php';
+require_once INSTALLDIR . '/lib/queuehandler.php';
 
 set_error_handler('common_error_handler');
 
 class OmbQueueHandler extends QueueHandler
 {
-    
+
     function transport()
     {
         return 'omb';
     }
-    
+
     function start()
     {
         $this->log(LOG_INFO, "INITIALIZE");
@@ -56,7 +60,7 @@ class OmbQueueHandler extends QueueHandler
             return omb_broadcast_remote_subscribers($notice);
         }
     }
-    
+
     function finish()
     {
     }
@@ -68,12 +72,15 @@ class OmbQueueHandler extends QueueHandler
     }
 }
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
-
-$id = ($argc > 1) ? $argv[1] : null;
+if (have_option('i')) {
+    $id = get_option_value('i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
 $handler = new OmbQueueHandler($id);
 
index ada6ecdba2070f20ad9a865553fdca2543df15ec..23678ea4b5278f16e6dd0f69c93e0b340931fe56 100644 (file)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-# Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-       print "This script must be run from the command line\n";
-       exit();
-}
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
-require_once(INSTALLDIR . '/lib/ping.php');
-require_once(INSTALLDIR . '/lib/queuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_PING_HELP
+Daemon script for pushing new notices to ping servers.
+
+    -i --id           Identity (default none)
 
-set_error_handler('common_error_handler');
+END_OF_PING_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+require_once INSTALLDIR . '/lib/ping.php';
+require_once INSTALLDIR . '/lib/queuehandler.php';
 
 class PingQueueHandler extends QueueHandler {
 
@@ -52,12 +54,15 @@ class PingQueueHandler extends QueueHandler {
        }
 }
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
-
-$id = ($argc > 1) ? $argv[1] : NULL;
+if (have_option('i')) {
+    $id = get_option_value('i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
 $handler = new PingQueueHandler($id);
 
index b0fa22d438e6a66c0c3931e33965a739e4afe087..701d50e0189f737ed36b41d4f90bcbcf2c23e5dc 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-# Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
-require_once(INSTALLDIR . '/lib/jabber.php');
-require_once(INSTALLDIR . '/lib/xmppqueuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_PUBLIC_HELP
+Daemon script for pushing new notices to public XMPP subscribers.
+
+    -i --id           Identity (default none)
+
+END_OF_PUBLIC_HELP;
 
-set_error_handler('common_error_handler');
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+require_once INSTALLDIR . '/lib/jabber.php';
+require_once INSTALLDIR . '/lib/xmppqueuehandler.php';
 
 class PublicQueueHandler extends XmppQueueHandler
 {
-    
+
     function transport()
     {
         return 'public';
     }
-    
+
     function handle_notice($notice)
     {
         try {
@@ -59,13 +61,16 @@ if (common_config('xmpp','enabled')==false) {
     exit();
 }
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
-
-$resource = ($argc > 1) ? $argv[1] : (common_config('xmpp','resource') . '-public');
+if (have_option('i')) {
+    $id = get_option_value('i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
-$handler = new PublicQueueHandler($resource);
+$handler = new PublicQueueHandler($id);
 
 $handler->runOnce();
index e332d856c007577e0d0a00fc1d4addccf27806fe..c644b557f0bbbb772a1d3044cf4362f7509413ab 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-# Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit(1);
-}
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
+$helptext = <<<END_OF_SNAPSHOT_HELP
+Batch script for sending snapshot information about this installation to devs.
 
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
+END_OF_SNAPSHOT_HELP;
 
-require_once(INSTALLDIR . '/lib/common.php');
+require_once INSTALLDIR.'/scripts/commandline.inc';
 
 Snapshot::check();
index d694eed095b4c2ffcfbae68dc728d9fadf6acb28..b70689f030a7a67f8ec06315a3de41a9a88eb9e5 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-# Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit(1);
-}
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
+$helptext = <<<END_OF_PASSWORD_HELP
+setpassword.php <username> <password>
 
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
+Sets the password of user with name <username> to <password>
 
-require_once(INSTALLDIR . '/lib/common.php');
+END_OF_PASSWORD_HELP;
 
-if ($argc != 3) {
-    print "USAGE: setpassword.php <username> <password>\n";
-    print "Sets the password of user with name <username> to <password>\n";
-    exit(1);
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+if (count($args) < 2) {
+    show_help();
 }
 
-$nickname = $argv[1];
-$password = $argv[2];
+$nickname = $args[0];
+$password = $args[1];
 
 if (mb_strlen($password) < 6) {
     print "Password must be 6 characters or more.\n";
diff --git a/scripts/setup.cfg.sample b/scripts/setup.cfg.sample
new file mode 100644 (file)
index 0000000..8d03b06
--- /dev/null
@@ -0,0 +1,14 @@
+# CONFIGURATION FILE for setup_status_network.sh
+
+export DBHOST=localhost
+export DBHOSTNAME=masterdb.example.net
+export DBBASE=_example_net
+export USERBASE=_example_net
+export ADMIN=root
+export ADMINPASS=yourpassword
+export SITEDB=example_net_site
+export AVATARBASE=/var/www/avatar.example.net
+export BACKGROUNDBASE=/var/www/background.example.net
+export FILEBASE=/var/www/file.example.net
+export PWDGEN="pwgen 20"
+
diff --git a/scripts/setup_status_network.sh b/scripts/setup_status_network.sh
new file mode 100755 (executable)
index 0000000..1744064
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+source /etc/laconica/setup.cfg
+
+export nickname=$1
+export sitename=$2
+
+export password=`$PWDGEN`
+export database=$nickname$DBBASE
+export username=$nickname$USERBASE
+
+# Create the db
+
+mysqladmin -h $DBHOST -u $ADMIN --password=$ADMINPASS create $database
+
+for f in laconica.sql innodb.sql sms_carrier.sql foreign_services.sql notice_source.sql; do
+    mysql -h $DBHOST -u $ADMIN --password=$ADMINPASS $database < ../db/$f;
+done
+
+mysql -h $DBHOST -u $ADMIN --password=$ADMINPASS $SITEDB << ENDOFCOMMANDS
+
+GRANT INSERT,SELECT,UPDATE,DELETE ON $database.* TO '$username'@'localhost' IDENTIFIED BY '$password';
+GRANT INSERT,SELECT,UPDATE,DELETE ON $database.* TO '$username'@'%' IDENTIFIED BY '$password';
+INSERT INTO status_network (nickname, dbhost, dbuser, dbpass, dbname, sitename, created)
+VALUES ('$nickname', '$DBHOSTNAME', '$username', '$password', '$database', '$sitename', now());
+
+ENDOFCOMMANDS
+
+for top in $AVATARBASE $FILEBASE $BACKGROUNDBASE; do
+    mkdir $top/$nickname
+    chmod a+w $top/$nickname
+done
index 39eb859bbad1f10e678d604cb87f08b42f6c5749..88ca2ba7aac4f56d60c7109a962e1a4b56a9ccc1 100755 (executable)
@@ -1,10 +1,37 @@
+#!/usr/bin/env php
 <?php
+/*
+ * Laconica - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
 
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
-require_once(INSTALLDIR . '/lib/util.php');
+$shortoptions = 'f:d:u:';
+
+$helptext = <<<END_OF_SITEMAP_HELP
+Script for creating sitemaps files per http://sitemaps.org/
+
+    -f <indexfile>   Use <indexfile> as output file
+    -d <outputdir>   Use <outputdir> for new sitemaps
+    -u <outputurl>   Use <outputurl> as root for URLs
+
+END_OF_SITEMAP_HELP;
+
+require_once INSTALLDIR . '/scripts/commandline.inc';
 
 $output_paths = parse_args();
 
@@ -13,11 +40,11 @@ notices_map();
 user_map();
 index_map();
 
-# ------------------------------------------------------------------------------
-# Main functions: get data out and turn them into sitemaps
-# ------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------
+// Main functions: get data out and turn them into sitemaps
+// ------------------------------------------------------------------------------
 
-# Generate index sitemap of all other sitemaps.
+// Generate index sitemap of all other sitemaps.
 function index_map()
 {
     global $output_paths;
@@ -26,7 +53,7 @@ function index_map()
 
     foreach (glob("$output_dir*.xml") as $file_name) {
 
-        # Just the file name please.
+        // Just the file name please.
         $file_name = preg_replace("|$output_dir|", '', $file_name);
 
         $index_urls .= sitemap(
@@ -40,7 +67,7 @@ function index_map()
     write_file($output_paths['index_file'], sitemapindex($index_urls));
 }
 
-# Generate sitemap of standard site elements.
+// Generate sitemap of standard site elements.
 function standard_map()
 {
     global $output_paths;
@@ -61,7 +88,7 @@ function standard_map()
                                     )
                               );
 
-    $docs = array('about', 'faq', 'contact', 'im', 'openid', 'openmublog', 
+    $docs = array('about', 'faq', 'contact', 'im', 'openid', 'openmublog',
         'privacy', 'source', 'badge');
 
     foreach($docs as $title) {
@@ -79,7 +106,7 @@ function standard_map()
     write_file($urlset_path, urlset($standard_map_urls));
 }
 
-# Generate sitemaps of all notices.
+// Generate sitemaps of all notices.
 function notices_map()
 {
     global $output_paths;
@@ -93,14 +120,14 @@ function notices_map()
 
     while ($notices->fetch()) {
 
-        # Maximum 50,000 URLs per sitemap file.
+        // Maximum 50,000 URLs per sitemap file.
         if ($notice_count == 50000) {
             $notice_count = 0;
             $map_count++;
         }
 
-        # remote notices have an URL
-        
+        // remote notices have an URL
+
         if (!$notices->url && $notices->uri) {
             $notice = array(
                         'url'        => ($notices->uri) ? $notices->uri : common_local_url('shownotice', array('notice' => $notices->id)),
@@ -114,11 +141,11 @@ function notices_map()
         }
     }
 
-    # Make full sitemaps from the lists and save them.
+    // Make full sitemaps from the lists and save them.
     array_to_map($notice_list, 'notice');
 }
 
-# Generate sitemaps of all users.
+// Generate sitemaps of all users.
 function user_map()
 {
     global $output_paths;
@@ -132,7 +159,7 @@ function user_map()
 
     while ($users->fetch()) {
 
-        # Maximum 50,000 URLs per sitemap file.
+        // Maximum 50,000 URLs per sitemap file.
         if ($user_count == 50000) {
             $user_count = 0;
             $map_count++;
@@ -140,7 +167,7 @@ function user_map()
 
         $user_args = array('nickname' => $users->nickname);
 
-        # Define parameters for generating <url></url> elements.
+        // Define parameters for generating <url></url> elements.
         $user = array(
                       'url'        => common_local_url('showstream', $user_args),
                       'changefreq' => 'daily',
@@ -183,8 +210,8 @@ function user_map()
                       'priority'   => '0.5',
                       );
 
-        # Construct a <url></url> element for each user facet and add it
-        # to our existing list of those.
+        // Construct a <url></url> element for each user facet and add it
+        // to our existing list of those.
         $user_list[$map_count]        .= url($user);
         $user_rss_list[$map_count]    .= url($user_rss);
         $all_list[$map_count]         .= url($all);
@@ -196,9 +223,9 @@ function user_map()
         $user_count++;
     }
 
-    # Make full sitemaps from the lists and save them.
-    # Possible factoring: put all the lists into a master array, thus allowing
-    # calling with single argument (i.e., array_to_map('user')).
+    // Make full sitemaps from the lists and save them.
+    // Possible factoring: put all the lists into a master array, thus allowing
+    // calling with single argument (i.e., array_to_map('user')).
     array_to_map($user_list, 'user');
     array_to_map($user_rss_list, 'user_rss');
     array_to_map($all_list, 'all');
@@ -208,14 +235,14 @@ function user_map()
     array_to_map($foaf_list, 'foaf');
 }
 
-# ------------------------------------------------------------------------------
-# XML generation functions
-# ------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------
+// XML generation functions
+// ------------------------------------------------------------------------------
 
-# Generate a <url></url> element.
+// Generate a <url></url> element.
 function url($url_args)
 {
-    $url        = preg_replace('/&/', '&amp;', $url_args['url']); # escape ampersands for XML
+    $url        = preg_replace('/&/', '&amp;', $url_args['url']); // escape ampersands for XML
     $lastmod    = $url_args['lastmod'];
     $changefreq = $url_args['changefreq'];
     $priority   = $url_args['priority'];
@@ -246,7 +273,7 @@ function url($url_args)
 
 function sitemap($sitemap_args)
 {
-    $url        = preg_replace('/&/', '&amp;', $sitemap_args['url']); # escape ampersands for XML
+    $url        = preg_replace('/&/', '&amp;', $sitemap_args['url']); // escape ampersands for XML
     $lastmod    = $sitemap_args['lastmod'];
 
     if (is_null($url)) {
@@ -265,7 +292,7 @@ function sitemap($sitemap_args)
     return $sitemap_out;
 }
 
-# Generate a <urlset></urlset> element.
+// Generate a <urlset></urlset> element.
 function urlset($urlset_text)
 {
     $urlset = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" .
@@ -276,7 +303,7 @@ function urlset($urlset_text)
     return $urlset;
 }
 
-# Generate a <urlset></urlset> element.
+// Generate a <urlset></urlset> element.
 function sitemapindex($sitemapindex_text)
 {
     $sitemapindex = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" .
@@ -287,49 +314,31 @@ function sitemapindex($sitemapindex_text)
     return $sitemapindex;
 }
 
-# Generate a sitemap from an array containing <url></url> elements and write it to a file.
+// Generate a sitemap from an array containing <url></url> elements and write it to a file.
 function array_to_map($url_list, $filename_prefix)
 {
     global $output_paths;
 
     if ($url_list) {
-        # $map_urls is a long string containing concatenated <url></url> elements.
+        // $map_urls is a long string containing concatenated <url></url> elements.
         while (list($map_idx, $map_urls) = each($url_list)) {
             $urlset_path = $output_paths['output_dir'] . "$filename_prefix-$map_idx.xml";
-            
+
             write_file($urlset_path, urlset($map_urls));
         }
     }
 }
 
-# ------------------------------------------------------------------------------
-# Internal functions
-# ------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------
+// Internal functions
+// ------------------------------------------------------------------------------
 
-# Parse command line arguments.
+// Parse command line arguments.
 function parse_args()
 {
-    $args = getopt('f:d:u:');
-
-    if (is_null($args[f]) && is_null($args[d]) && is_null($args[u])) {
-        error('Mandatory arguments: -f <index file path> -d <output directory path> -u <URL of sitemaps directory>');
-    }
-
-    if (is_null($args[f])) {
-        error('You must specify an index file name with the -f option.');
-    }
-
-    if (is_null($args[d])) {
-        error('You must specify a directory for the output file with the -d option.');
-    }
-
-    if (is_null($args[u])) {
-        error('You must specify a URL for the directory where the sitemaps will be kept with the -u option.');
-    }
-
-    $index_file = $args[f];
-    $output_dir = $args[d];
-    $output_url = $args[u];
+    $index_file = get_option_value('f');
+    $output_dir = get_option_value('d');
+    $output_url = get_option_value('u');
 
     if (file_exists($output_dir)) {
         if (is_writable($output_dir) === false) {
@@ -348,7 +357,7 @@ function parse_args()
     return $paths;
 }
 
-# Ensure paths end with a "/".
+// Ensure paths end with a "/".
 function trailing_slash($path)
 {
     if (preg_match('/\/$/', $path) == 0) {
@@ -358,7 +367,7 @@ function trailing_slash($path)
     return $path;
 }
 
-# Write data to disk.
+// Write data to disk.
 function write_file($path, $data)
 {
     if (is_null($path)) {
@@ -376,7 +385,7 @@ function write_file($path, $data)
     }
 }
 
-# Display an error message and exit.
+// Display an error message and exit.
 function error ($error_msg)
 {
     if (is_null($error_msg)) {
index 38f2f11febb4e6f812da9eb8f65d7a75348c481e..94b846d987bc22f3a6fdb7016c58ef7b73f5c24b 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-# Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
-require_once(INSTALLDIR . '/lib/mail.php');
-require_once(INSTALLDIR . '/lib/queuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_SMS_HELP
+Daemon script for pushing new notices to local subscribers using SMS.
+
+    -i --id           Identity (default none)
+
+END_OF_SMS_HELP;
 
-set_error_handler('common_error_handler');
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+require_once INSTALLDIR . '/lib/mail.php';
+require_once INSTALLDIR . '/lib/queuehandler.php';
 
 class SmsQueueHandler extends QueueHandler
 {
-    
     function transport()
     {
         return 'sms';
@@ -51,18 +52,21 @@ class SmsQueueHandler extends QueueHandler
     {
         return mail_broadcast_notice_sms($notice);
     }
-    
+
     function finish()
     {
     }
 }
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
-
-$id = ($argc > 1) ? $argv[1] : null;
+if (have_option('i')) {
+    $id = get_option_value('i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
 $handler = new SmsQueueHandler($id);
 
index 9759adbd07b83f6798e934ed438f5107a6e22a2a..c16af3c4be17eda218dd5525c3a3fec8fca2414d 100755 (executable)
@@ -2,7 +2,7 @@
 
 # Laconica - a distributed open-source microblogging tool
 
-# Copyright (C) 2008, Controlez-Vous, Inc.
+# Copyright (C) 2008, 2009, Control Yourself, Inc.
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
index 3311b2ed140b175a3286c9a12103388eb3af365b..fe7c16bea1d436e2a1c403eb75710b0eee80152c 100755 (executable)
@@ -2,7 +2,7 @@
 
 # Laconica - a distributed open-source microblogging tool
 
-# Copyright (C) 2008, Controlez-Vous, Inc.
+# Copyright (C) 2008, 2009, Control Yourself, Inc.
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
index 3869e95c4c3cf5ae2735f54dbff600349af6f7d8..9ead20acd6c0230f0b42af6a24f6cd670404c297 100755 (executable)
@@ -2,7 +2,7 @@
 
 # Laconica - a distributed open-source microblogging tool
 
-# Copyright (C) 2008, Controlez-Vous, Inc.
+# Copyright (C) 2008, 2009, Control Yourself, Inc.
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
 # This program tries to start the daemons for Laconica.
 # Note that the 'maildaemon' needs to run as a mail filter.
 
+ARGSG=
+ARGSD=
+
+if [ $# -gt 0 ]; then
+    ARGSG="$ARGSG -s$1"
+    ID=`echo $1 | sed s/\\\\./_/g`
+    ARGSD="$ARGSD -s$1 -i$ID"
+fi
+
+if [ $# -gt 1 ]; then
+    ARGSD="$ARGSD -p$2"
+    ARGSG="$ARGSG -p$2"
+fi
+
 DIR=`dirname $0`
-DAEMONS=`php $DIR/getvaliddaemons.php`
+DAEMONS=`php $DIR/getvaliddaemons.php $ARGSG`
 
 for f in $DAEMONS; do
 
-         echo -n "Starting $f...";
-        php $DIR/$f
-        echo "DONE."
+         printf "Starting $f...";
+        php $DIR/$f $ARGSD
+        printf "DONE.\n"
+
 done
index 2134b4ab00f2af90e95750a5a24fb9153d97a917..60ffd83ad1e45d8991aa4115a3e8c305acba5ddb 100755 (executable)
@@ -2,7 +2,7 @@
 
 # Laconica - a distributed open-source microblogging tool
 
-# Copyright (C) 2008, Controlez-Vous, Inc.
+# Copyright (C) 2008, 2009, Control Yourself, Inc.
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -24,7 +24,8 @@ SDIR=`dirname $0`
 DIR=`php $SDIR/getpiddir.php`
 
 for f in jabberhandler ombhandler publichandler smshandler pinghandler \
-        xmppconfirmhandler xmppdaemon twitterhandler facebookhandler; do
+        xmppconfirmhandler xmppdaemon twitterhandler facebookhandler \
+        twitterstatusfetcher; do
 
        FILES="$DIR/$f.*.pid"
        for ff in "$FILES" ; do
index bd08ba58d6a9bcba0e1bcfd3af3941f4aaa461d5..fe53ff44d634fa92895dd00c34ccc804fa245295 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * along with this program.     If not, see <http://www.gnu.org/licenses/>.
  */
 
-// Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
 // Uncomment this to get useful console output
-//define('SCRIPT_DEBUG', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
+$helptext = <<<END_OF_TWITTER_HELP
+Batch script for synching local friends with Twitter friends.
+
+END_OF_TWITTER_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
 
 // Make a lockfile
 $lockfilename = lockFilename();
index 0d2eaeaf09263e0d3bed6f50110855b38c752e26..b2135d6825ee2f52eccbe481a8c56cbfa3958861 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-# Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit(1);
-}
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
+$shortoptions = 'u::';
+$longoptions = array('start-user-id::');
 
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
+$helptext = <<<END_OF_TRIM_HELP
+Batch script for trimming notice inboxes to a reasonable size.
+
+    -u <id>
+    --start-user-id=<id>   User ID to start after. Default is all.
 
-require_once(INSTALLDIR . '/lib/common.php');
+END_OF_TRIM_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+$id = null;
+
+if (have_option('u')) {
+    $id = get_option_value('u');
+} else if (have_option('--start-user-id')) {
+    $id = get_option_value('--start-user-id');
+} else {
+    $id = null;
+}
 
 $user = new User();
-if ($argc > 1) {
-    $user->whereAdd('id > ' . $argv[1]);
+
+if (!empty($id)) {
+    $user->whereAdd('id > ' . $id);
 }
+
 $cnt = $user->find();
 
 while ($user->fetch()) {
@@ -74,10 +85,10 @@ while ($user->fetch()) {
     $delay = 3.0 * ($finish - $start);
 
     print "Delaying $delay seconds...";
-    
+
     // Wait to let slaves catch up
 
     usleep($delay * 1000000);
-    
+
     print "DONE.\n";
 }
index 7da4f1e20aa1fc2f0e6fe8fc3d1ef13a12ec5792..00e735d983610b99bf689c8a8bc740cbf33af1ae 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-# Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
-require_once(INSTALLDIR . '/lib/twitter.php');
-require_once(INSTALLDIR . '/lib/queuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_ENJIT_HELP
+Daemon script for pushing new notices to Twitter.
+
+    -i --id           Identity (default none)
+
+END_OF_ENJIT_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
 
-set_error_handler('common_error_handler');
+require_once INSTALLDIR . '/lib/twitter.php';
+require_once INSTALLDIR . '/lib/queuehandler.php';
 
 class TwitterQueueHandler extends QueueHandler
 {
-    
     function transport()
     {
         return 'twitter';
     }
-    
+
     function start()
     {
         $this->log(LOG_INFO, "INITIALIZE");
@@ -51,20 +52,22 @@ class TwitterQueueHandler extends QueueHandler
     {
         return broadcast_twitter($notice);
     }
-    
+
     function finish()
     {
     }
 
 }
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-
-mb_internal_encoding('UTF-8');
-
-$id = ($argc > 1) ? $argv[1] : null;
+if (have_option('i')) {
+    $id = get_option_value('i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
 $handler = new TwitterQueueHandler($id);
 
index 9287b6d73332ee1cca765df095883703cf17575d..5ffdda58fafb8fdcd2ad643bfa1ca4bd918d1a3c 100755 (executable)
@@ -1,8 +1,8 @@
 #!/usr/bin/env php
 <?php
-/*
+/**
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * along with this program.     If not, see <http://www.gnu.org/licenses/>.
  */
 
-// Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
 // Tune number of processes and how often to poll Twitter
 // XXX: Should these things be in config.php?
 define('MAXCHILDREN', 2);
 define('POLL_INTERVAL', 60); // in seconds
 
-// Uncomment this to get useful logging
-define('SCRIPT_DEBUG', true);
+$helptext = <<<END_OF_TRIM_HELP
+Batch script for retrieving Twitter messages from foreign service.
+
+END_OF_TRIM_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+require_once INSTALLDIR . '/lib/common.php';
+require_once INSTALLDIR . '/lib/daemon.php';
+
+/**
+ * Fetcher for statuses from Twitter
+ *
+ * Fetches statuses from Twitter and inserts them as notices in local
+ * system.
+ *
+ * @category Twitter
+ * @package  Laconica
+ * @author   Zach Copley <zach@controlyourself.ca>
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://laconi.ca/
+ */
 
-require_once(INSTALLDIR . '/lib/common.php');
-require_once(INSTALLDIR . '/lib/daemon.php');
+// NOTE: an Avatar path MUST be set in config.php for this
+// script to work: e.g.: $config['avatar']['path'] = '/laconica/avatar';
 
 class TwitterStatusFetcher extends Daemon
 {
+    private $_children = array();
 
-    private $children = array();
+    /**
+     * Name of this daemon
+     *
+     * @return string Name of the daemon.
+     */
 
     function name()
     {
         return ('twitterstatusfetcher.generic');
     }
 
+    /**
+     * Run the daemon
+     *
+     * @return void
+     */
+
     function run()
     {
         do {
 
             $flinks = $this->refreshFlinks();
 
-            foreach ($flinks as $f){
+            foreach ($flinks as $f) {
 
                 // We have to disconnect from the DB before forking so
                 // each sub-process will open its own connection and
@@ -73,10 +98,11 @@ class TwitterStatusFetcher extends Daemon
 
                     // Parent
                     if (defined('SCRIPT_DEBUG')) {
-                        common_debug("Parent: forked new status fetcher process " . $pid);
+                        common_debug("Parent: forked new status ".
+                                     " fetcher process " . $pid);
                     }
 
-                    $this->children[] = $pid;
+                    $this->_children[] = $pid;
 
                 } else {
 
@@ -86,41 +112,41 @@ class TwitterStatusFetcher extends Daemon
                 }
 
                 // Remove child from ps list as it finishes
-                while(($c = pcntl_wait($status, WNOHANG OR WUNTRACED)) > 0) {
+                while (($c = pcntl_wait($status, WNOHANG OR WUNTRACED)) > 0) {
 
                     if (defined('SCRIPT_DEBUG')) {
                         common_debug("Child $c finished.");
                     }
 
-                    $this->remove_ps($this->children, $c);
+                    $this->removePs($this->_children, $c);
                 }
 
                 // Wait! We have too many damn kids.
-                if (sizeof($this->children) > MAXCHILDREN) {
+                if (sizeof($this->_children) > MAXCHILDREN) {
 
                     if (defined('SCRIPT_DEBUG')) {
                         common_debug('Too many children. Waiting...');
                     }
 
-                    if (($c = pcntl_wait($status, WUNTRACED)) > 0){
+                    if (($c = pcntl_wait($status, WUNTRACED)) > 0) {
 
                         if (defined('SCRIPT_DEBUG')) {
                             common_debug("Finished waiting for $c");
                         }
 
-                        $this->remove_ps($this->children, $c);
+                        $this->removePs($this->_children, $c);
                     }
                 }
             }
 
             // Remove all children from the process list before restarting
-            while(($c = pcntl_wait($status, WUNTRACED)) > 0) {
+            while (($c = pcntl_wait($status, WUNTRACED)) > 0) {
 
                 if (defined('SCRIPT_DEBUG')) {
                     common_debug("Child $c finished.");
                 }
 
-                $this->remove_ps($this->children, $c);
+                $this->removePs($this->_children, $c);
             }
 
             // Rest for a bit before we fetch more statuses
@@ -137,10 +163,18 @@ class TwitterStatusFetcher extends Daemon
         } while (true);
     }
 
-    function refreshFlinks() {
+    /**
+     * Refresh the foreign links for this user
+     *
+     * @return void
+     */
 
+    function refreshFlinks()
+    {
         $flink = new Foreign_link();
+
         $flink->service = 1; // Twitter
+
         $flink->orderBy('last_noticesync');
 
         $cnt = $flink->find();
@@ -166,7 +200,18 @@ class TwitterStatusFetcher extends Daemon
         return $flinks;
     }
 
-    function remove_ps(&$plist, $ps){
+    /**
+     * Unknown
+     *
+     * @param array  &$plist unknown.
+     * @param string $ps     unknown.
+     *
+     * @return unknown
+     * @todo document
+     */
+
+    function removePs(&$plist, $ps)
+    {
         for ($i = 0; $i < sizeof($plist); $i++) {
             if ($plist[$i] == $ps) {
                 unset($plist[$i]);
@@ -178,7 +223,6 @@ class TwitterStatusFetcher extends Daemon
 
     function getTimeline($flink)
     {
-
         if (empty($flink)) {
             common_log(LOG_WARNING,
                 "Can't retrieve Foreign_link for foreign ID $fid");
@@ -247,23 +291,32 @@ class TwitterStatusFetcher extends Daemon
             return null;
         }
 
+        // XXX: change of screen name?
+
         $uri = 'http://twitter.com/' . $status->user->screen_name .
             '/status/' . $status->id;
 
         $notice = Notice::staticGet('uri', $uri);
 
         // check to see if we've already imported the status
-        if (!$notice) {
-
-            $created = strftime('%Y-%m-%d %H:%M:%S',
-                                strtotime($status->created_at));;
 
-            $notice = Notice::saveNew($id, $status->text, 'twitter',
-                                      -2, null, $uri, $created);
+        if (!$notice) {
 
-            if (defined('SCRIPT_DEBUG')) {
-                common_debug("Saved status $status->id" .
-                    " as notice $notice->id.");
+            $notice = new Notice();
+
+            $notice->profile_id = $id;
+            $notice->uri        = $uri;
+            $notice->created    = strftime('%Y-%m-%d %H:%M:%S',
+                                           strtotime($status->created_at));
+            $notice->content    = common_shorten_links($status->text); // XXX
+            $notice->rendered   = common_render_content($notice->content, $notice);
+            $notice->source     = 'twitter';
+            $notice->reply_to   = null; // XXX lookup reply
+            $notice->is_local   = NOTICE_GATEWAY;
+
+            if (Event::handle('StartNoticeSave', array(&$notice))) {
+                $id = $notice->insert();
+                Event::handle('EndNoticeSave', array($notice));
             }
         }
 
@@ -271,9 +324,11 @@ class TwitterStatusFetcher extends Daemon
                                          'user_id' => $flink->user_id))) {
             // Add to inbox
             $inbox = new Notice_inbox();
-            $inbox->user_id = $flink->user_id;
+
+            $inbox->user_id   = $flink->user_id;
             $inbox->notice_id = $notice->id;
-            $inbox->created = $notice->created;
+            $inbox->created   = $notice->created;
+            $inbox->source    = NOTICE_INBOX_SOURCE_GATEWAY; // From a private source
 
             $inbox->insert();
         }
@@ -348,12 +403,13 @@ class TwitterStatusFetcher extends Daemon
         }
     }
 
-    function checkAvatar($user, $profile)
+    function checkAvatar($twitter_user, $profile)
     {
         global $config;
 
-        $path_parts = pathinfo($user->profile_image_url);
-        $newname = 'Twitter_' . $user->id . '_' .
+        $path_parts = pathinfo($twitter_user->profile_image_url);
+
+        $newname = 'Twitter_' . $twitter_user->id . '_' .
             $path_parts['basename'];
 
         $oldname = $profile->getAvatar(48)->filename;
@@ -366,21 +422,56 @@ class TwitterStatusFetcher extends Daemon
                 common_debug("old: $oldname new: $newname");
             }
 
-            $img_root = substr($path_parts['basename'], 0, -11);
-            $ext = $path_parts['extension'];
-            $mediatype = $this->getMediatype($ext);
+            $this->updateAvatars($twitter_user, $profile);
+        }
 
-            foreach (array('mini', 'normal', 'bigger') as $size) {
-                $url = $path_parts['dirname'] . '/' .
-                    $img_root . '_' . $size . ".$ext";
-                $filename = 'Twitter_' . $user->id . '_' .
-                    $img_root . "_$size.$ext";
+        if ($this->missingAvatarFile($profile)) {
 
-                if ($this->fetchAvatar($url, $filename)) {
-                    $this->updateAvatar($profile->id, $size, $mediatype, $filename);
-                }
+            if (defined('SCRIPT_DEBUG')) {
+                common_debug('Twitter user ' . $profile->nickname .
+                    ' is missing one or more local avatars.');
+                common_debug("old: $oldname new: $newname");
             }
+
+            $this->updateAvatars($twitter_user, $profile);
         }
+
+    }
+
+    function updateAvatars($twitter_user, $profile) {
+
+        global $config;
+
+        $path_parts = pathinfo($twitter_user->profile_image_url);
+
+        $img_root = substr($path_parts['basename'], 0, -11);
+        $ext = $path_parts['extension'];
+        $mediatype = $this->getMediatype($ext);
+
+        foreach (array('mini', 'normal', 'bigger') as $size) {
+            $url = $path_parts['dirname'] . '/' .
+                $img_root . '_' . $size . ".$ext";
+            $filename = 'Twitter_' . $twitter_user->id . '_' .
+                $img_root . "_$size.$ext";
+
+            $this->updateAvatar($profile->id, $size, $mediatype, $filename);
+            $this->fetchAvatar($url, $filename);
+        }
+    }
+
+    function missingAvatarFile($profile) {
+
+        foreach (array(24, 48, 73) as $size) {
+
+            $filename = $profile->getAvatar($size)->filename;
+            $avatarpath = Avatar::path($filename);
+
+            if (file_exists($avatarpath) == FALSE) {
+                return true;
+            }
+        }
+
+        return false;
     }
 
     function getMediatype($ext)
@@ -433,7 +524,7 @@ class TwitterStatusFetcher extends Daemon
 
         $profile = Profile::staticGet($profile_id);
 
-        if (!$profile) {
+        if (empty($profile)) {
             if (defined('SCRIPT_DEBUG')) {
                 common_debug("Couldn't get profile: $profile_id!");
             }
@@ -443,11 +534,8 @@ class TwitterStatusFetcher extends Daemon
         $sizes = array('mini' => 24, 'normal' => 48, 'bigger' => 73);
         $avatar = $profile->getAvatar($sizes[$size]);
 
+        // Delete the avatar, if present
         if ($avatar) {
-            if (defined('SCRIPT_DEBUG')) {
-                common_debug("Deleting $size avatar for $profile->nickname.");
-            }
-            @unlink(INSTALLDIR . '/avatar/' . $avatar->filename);
             $avatar->delete();
         }
 
@@ -492,7 +580,7 @@ class TwitterStatusFetcher extends Daemon
 
         $id = $avatar->insert();
 
-        if (!$id) {
+        if (empty($id)) {
             common_log_db_error($avatar, 'INSERT', __FILE__);
             return null;
         }
@@ -535,10 +623,6 @@ class TwitterStatusFetcher extends Daemon
     }
 }
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
 declare(ticks = 1);
 
 $fetcher = new TwitterStatusFetcher();
index fa0fb64cda575004298d3af4ca67aad316674742..b0b576eb44ff3a2295f479ad62fe3f5f7dc3d1ad 100644 (file)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * You should have received a copy of the GNU Affero General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
 
-# Abort if called from a web server
-
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
+$helptext = <<<ENDOFHELP
+uncache_users.php <idfile>
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
+Uncache users listed in an ID file, default 'ids.txt'.
 
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
+ENDOFHELP;
 
-require_once(INSTALLDIR . '/lib/common.php');
+require_once INSTALLDIR.'/scripts/commandline.inc';
 
-$id_file = ($argc > 1) ? $argv[1] : 'ids.txt';
+$id_file = (count($args) > 1) ? $args[0] : 'ids.txt';
 
 common_log(LOG_INFO, 'Updating user inboxes.');
 
 $ids = file($id_file);
 
+$memc = common_memcache();
+
 foreach ($ids as $id) {
-       
+
        $user = User::staticGet('id', $id);
 
        if (!$user) {
@@ -51,9 +46,7 @@ foreach ($ids as $id) {
        }
 
     $user->decache();
-    
-    $memc = common_memcache();
-    
+
     $memc->delete(common_cache_key('user:notices_with_friends:'. $user->id));
     $memc->delete(common_cache_key('user:notices_with_friends:'. $user->id . ';last'));
 }
index 7f39235fed8464f7e3847990c3976d75e7bd05b8..d6821ddefae03affd7595396f14489f6a74f8571 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-# Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
-require_once(INSTALLDIR . '/lib/jabber.php');
-require_once(INSTALLDIR . '/lib/xmppqueuehandler.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_JABBER_HELP
+Daemon script for pushing new confirmations to Jabber users.
+
+    -i --id           Identity (default none)
+
+END_OF_JABBER_HELP;
 
-set_error_handler('common_error_handler');
+require_once INSTALLDIR.'/scripts/commandline.inc';
+require_once INSTALLDIR . '/lib/jabber.php';
+require_once INSTALLDIR . '/lib/xmppqueuehandler.php';
 
 define('CLAIM_TIMEOUT', 1200);
 
 class XmppConfirmHandler extends XmppQueueHandler
 {
-
     var $_id = 'confirm';
-    
+
     function class_name()
     {
         return 'XmppConfirmHandler';
     }
-    
+
     function run()
     {
         if (!$this->start()) {
@@ -147,14 +147,17 @@ if (common_config('xmpp','enabled')==false) {
     exit();
 }
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
-
-$resource = ($argc > 1) ? $argv[1] : (common_config('xmpp', 'resource').'-confirm');
+if (have_option('i')) {
+    $id = get_option_value('i');
+} else if (have_option('--id')) {
+    $id = get_option_value('--id');
+} else if (count($args) > 0) {
+    $id = $args[0];
+} else {
+    $id = null;
+}
 
-$handler = new XmppConfirmHandler($resource);
+$handler = new XmppConfirmHandler($id);
 
 $handler->runOnce();
 
index b79fa1b3ba0145101e167ddafea80fdd67ca3fc8..3eecfec29a13725aa46684428b30dc1d301b3052 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /*
  * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
+ * Copyright (C) 2008, 2009, Control Yourself, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-# Abort if called from a web server
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
-
 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('LACONICA', true);
 
-require_once(INSTALLDIR . '/lib/common.php');
-require_once(INSTALLDIR . '/lib/jabber.php');
-require_once(INSTALLDIR . '/lib/daemon.php');
+$shortoptions = 'i::';
+$longoptions = array('id::');
+
+$helptext = <<<END_OF_XMPP_HELP
+Daemon script for receiving new notices from Jabber users.
+
+    -i --id           Identity (default none)
 
-set_error_handler('common_error_handler');
+END_OF_XMPP_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+require_once INSTALLDIR . '/lib/common.php';
+require_once INSTALLDIR . '/lib/jabber.php';
+require_once INSTALLDIR . '/lib/daemon.php';
 
 # This is kind of clunky; we create a class to call the global functions
 # in jabber.php, which create a new XMPP class. A more elegant (?) solution
@@ -39,7 +42,6 @@ set_error_handler('common_error_handler');
 
 class XMPPDaemon extends Daemon
 {
-
     function XMPPDaemon($resource=null)
     {
         static $attrs = array('server', 'port', 'user', 'password', 'host');
@@ -50,7 +52,7 @@ class XMPPDaemon extends Daemon
         }
 
         if ($resource) {
-            $this->resource = $resource;
+            $this->resource = $resource . 'daemon';
         } else {
             $this->resource = common_config('xmpp', 'resource') . 'daemon';
         }
@@ -321,13 +323,16 @@ if (common_config('xmpp','enabled')==false) {
     exit();
 }
 
-ini_set("max_execution_time", "0");
-ini_set("max_input_time", "0");
-set_time_limit(0);
-mb_internal_encoding('UTF-8');
-
-$resource = ($argc > 1) ? $argv[1] : (common_config('xmpp','resource') . '-listen');
+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;
+}
 
-$daemon = new XMPPDaemon($resource);
+$daemon = new XMPPDaemon($id);
 
 $daemon->runOnce();
index b79adf15c0e08140a2aac575aaa699ab582d380d..8204b9db6c61708aeadb86d7bcf05284a6101188 100644 (file)
@@ -4,68 +4,68 @@
 
 source src1
 {
-       type                                    = mysql
-       sql_host                                = localhost
-       sql_user                                = USERNAME
-       sql_pass                                = PASSWORD
-       sql_db                                  = identi_ca
-       sql_port                                = 3306
-       sql_query                               = SELECT id, UNIX_TIMESTAMP(created) as created_ts, nickname, fullname, location, bio, homepage FROM profile
-       sql_query_info                  = SELECT * FROM profile where id = $id
-       sql_attr_timestamp              = created_ts
+    type                    = mysql
+    sql_host                = localhost
+    sql_user                = USERNAME
+    sql_pass                = PASSWORD
+    sql_db                  = identi_ca
+    sql_port                = 3306
+    sql_query               = SELECT id, UNIX_TIMESTAMP(created) as created_ts, nickname, fullname, location, bio, homepage FROM profile
+    sql_query_info          = SELECT * FROM profile where id = $id
+    sql_attr_timestamp      = created_ts
 }
 
 
 source src2
 {
-       type                                    = mysql
-       sql_host                                = localhost
-       sql_user                                = USERNAME
-       sql_pass                                = PASSWORD
-       sql_db                                  = identi_ca
-       sql_port                                = 3306
-       sql_query                               = SELECT id, UNIX_TIMESTAMP(created) as created_ts, content FROM notice
-       sql_query_info                  = SELECT * FROM notice where id = $id
-       sql_attr_timestamp              = created_ts
+    type                    = mysql
+    sql_host                = localhost
+    sql_user                = USERNAME
+    sql_pass                = PASSWORD
+    sql_db                  = identi_ca
+    sql_port                = 3306
+    sql_query               = SELECT id, UNIX_TIMESTAMP(created) as created_ts, content FROM notice
+    sql_query_info          = SELECT * FROM notice where notice.id = $id AND notice.is_local != -2
+    sql_attr_timestamp      = created_ts
 }
 
 index identica_notices
 {
-       source                                  = src2
-       path                                    = DIRECTORY/data/identica_notices
-       docinfo                                 = extern
-       charset_type                    = utf-8
-       min_word_len                    = 3
-       stopwords                               = DIRECTORY/data/stopwords-en.txt
+    source                  = src2
+    path                    = DIRECTORY/data/identica_notices
+    docinfo                 = extern
+    charset_type            = utf-8
+    min_word_len            = 3
+    stopwords               = DIRECTORY/data/stopwords-en.txt
 }
 
 
 index identica_people
 {
-       source                                  = src1
-       path                                    = DIRECTORY/data/identica_people
-       docinfo                                 = extern
-       charset_type                    = utf-8
-       min_word_len                    = 3
-       stopwords                               = DIRECTORY/data/stopwords-en.txt
+    source                  = src1
+    path                    = DIRECTORY/data/identica_people
+    docinfo                 = extern
+    charset_type            = utf-8
+    min_word_len            = 3
+    stopwords               = DIRECTORY/data/stopwords-en.txt
 }
 
 indexer
 {
-       mem_limit                               = 32M
+    mem_limit               = 32M
 }
 
 searchd
 {
-       port                                    = 3312
-       log                                             = DIRECTORY/log/searchd.log
-       query_log                               = DIRECTORY/log/query.log
-       read_timeout                    = 5
-       max_children                    = 30
-       pid_file                                = DIRECTORY/log/searchd.pid
-       max_matches                             = 1000
-       seamless_rotate                 = 1
-       preopen_indexes                 = 0
-       unlink_old                              = 1
+    port                    = 3312
+    log                     = DIRECTORY/log/searchd.log
+    query_log               = DIRECTORY/log/query.log
+    read_timeout            = 5
+    max_children            = 30
+    pid_file                = DIRECTORY/log/searchd.pid
+    max_matches             = 1000
+    seamless_rotate         = 1
+    preopen_indexes         = 0
+    unlink_old              = 1
 }
 
index dc275e19f71df226e2917aad3c299ae6e0eee272..78fcd7ecefcca0187e3a620f6dcd6eb8c4cf4d91 100644 (file)
@@ -12,9 +12,9 @@ img { display:block; border:0; }
 a abbr { cursor: pointer; border-bottom:0; }
 table { border-collapse:collapse; }
 ol { list-style-position:inside; }
-html { font-size: 87.5%; background-color:#fff; height:100%; }
+html { font-size: 87.5%; }
 body {
-background-color:#fff;
+background-color:#FFFFFF;
 color:#000;
 font-family:sans-serif;
 font-size:1em;
@@ -77,7 +77,8 @@ margin:0 0 18px 0;
 form label {
 font-weight:bold;
 }
-input.checkbox {
+input.checkbox,
+input.radio {
 position:relative;
 top:2px;
 left:0;
@@ -154,7 +155,8 @@ font-weight:bold;
 #form_invite legend,
 #form_notice_delete legend,
 #form_password_recover legend,
-#form_password_change legend {
+#form_password_change legend,
+.form_entity_block legend {
 display:none;
 }
 
@@ -168,7 +170,8 @@ margin-bottom:0;
 margin-bottom:11px;
 }
 
-.form_settings input.checkbox {
+.form_settings input.checkbox,
+.form_settings input.radio {
 margin-top:3px;
 margin-left:0;
 }
@@ -180,13 +183,19 @@ margin-left:11px;
 float:left;
 width:90%;
 }
-
+.form_settings label.radio {
+margin-top:0;
+margin-right:47px;
+margin-left:11px;
+width:auto;
+}
 
 #form_login p.form_guide,
 #form_register #settings_rememberme p.form_guide,
 #form_openid_login #settings_rememberme p.form_guide,
 #settings_twitter_remove p.form_guide,
-#form_search ul.form_data #q {
+#form_search ul.form_data #q,
+#design_background-image_onoff p.form_guide {
 margin-left:0;
 }
 
@@ -306,7 +315,6 @@ padding:4px 11px;
 border-width:1px;
 border-style:solid;
 border-bottom:0;
-text-shadow: 2px 2px 2px #ddd;
 font-weight:bold;
 }
 #site_nav_local_views .nav {
@@ -396,8 +404,8 @@ border-radius:7px;
 -moz-border-radius-topleft:0;
 -webkit-border-radius:7px;
 -webkit-border-top-left-radius:0;
-border-style:solid;
 border-width:1px;
+border-style:solid;
 }
 #shownotice #content {
 min-height:0;
@@ -413,7 +421,7 @@ float:left;
 width:27.917%;
 min-height:259px;
 float:left;
-margin-left:0.385%;
+margin-left:0.5%;
 padding:1.795%;
 border-radius:7px;
 -moz-border-radius:7px;
@@ -445,6 +453,8 @@ width:80.789%;
 height:67px;
 line-height:1.5;
 padding:7px 7px 16px 7px;
+position:relative;
+z-index:2;
 }
 #form_notice label {
 display:block;
@@ -452,23 +462,22 @@ float:left;
 font-size:1.3em;
 margin-bottom:7px;
 }
-#form_notice label[for=notice_data-attach] {
-text-indent:-9999px;
-}
 #form_notice label[for=notice_data-attach],
 #form_notice #notice_data-attach {
 position:absolute;
 top:25px;
-right:49px;
+cursor:pointer;
+}
+#form_notice label[for=notice_data-attach] {
+text-indent:-9999px;
+left:394px;
 width:16px;
 height:16px;
-cursor:pointer;
 }
 #form_notice #notice_data-attach {
-text-indent:-279px;
-}
-#form_notice #notice_submit label {
-display:none;
+left:183px;
+padding:0;
+height:16px;
 }
 #form_notice .form_note {
 position:absolute;
@@ -501,13 +510,26 @@ margin-bottom:7px;
 margin-left:18px;
 float:left;
 }
-#form_notice .error {
+#form_notice .error,
+#form_notice .success {
 float:left;
 clear:both;
-width:96.9%;
+width:81.5%;
 margin-bottom:0;
 line-height:1.618;
 }
+#form_notice #notice_data-attach_selected code {
+float:left;
+width:90%;
+display:block;
+font-size:1.1em;
+line-height:1.8;
+overflow:auto;
+}
+#form_notice #notice_data-attach_selected button {
+float:right;
+font-size:0.8em;
+}
 
 /* entity_profile */
 .entity_profile {
@@ -539,7 +561,8 @@ margin-bottom:18px;
 .entity_profile .entity_location,
 .entity_profile .entity_url,
 .entity_profile .entity_note,
-.entity_profile .entity_tags {
+.entity_profile .entity_tags,
+.entity_profile .entity_aliases {
 margin-left:113px;
 margin-bottom:4px;
 }
@@ -616,10 +639,13 @@ display:block;
 
 .form_user_block input.submit,
 .form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit,
 .entity_send-a-message a,
 .entity_edit a,
 .form_user_nudge input.submit,
-.entity_nudge p {
+.entity_nudge p,
+.form_make_admin input.submit {
 border:0;
 padding-left:20px;
 }
@@ -1006,6 +1032,22 @@ border-radius:7px;
 -webkit-border-radius:7px;
 }
 
+#attachment_view #oembed_info {
+margin-top:11px;
+}
+#attachment_view #oembed_info dt,
+#attachment_view #oembed_info dd {
+float:left;
+}
+#attachment_view #oembed_info dt {
+clear:left;
+margin-right:11px;
+font-weight:bold;
+}
+#attachment_view #oembed_info dt:after {
+content: ":";
+}
+
 #usergroups #new_group {
 float: left;
 margin-right: 2em;
index 8183fee67974d1ea71418ad3914da768ebd23397..43fb01492abadf734c9576824031ce48be0ac97c 100644 (file)
@@ -1,17 +1,30 @@
 /* IE specific styles */
-legend {
-margin-left:-7px;
-}
-input.checkbox {
+input.checkbox,
+input.radio {
 top:0;
 }
 #form_notice textarea {
 width:78%;
 }
+#form_notice .form_note + label {
+position:absolute;
+top:25px;
+left:380px;
+text-indent:-9999px;
+height:16px;
+width:16px;
+display:block;
+}
 #form_notice #notice_action-submit {
 width:17%;
 max-width:17%;
 }
+#form_notice #notice_data-attach_selected {
+width:78.5%;
+}
+#form_notice #notice_data-attach_selected button {
+padding:0 4px;
+}
 #anon_notice {
 max-width:39%;
 }
index 76a82c0042d32841784b46fd0bb50d340ddd16d8..dde4d6fc77081906566f6f7ec0cebea44c30dacc 100644 (file)
@@ -5,6 +5,12 @@ margin-left:7px;
 address .fn {
 display:none;
 }
+
+#wrap {
+width:1003px;
+margin:0 auto;
+}
+
 #content {
 width:70%;
 }
@@ -26,5 +32,6 @@ margin-bottom:123px;
 width:20%;
 }
 .notice div.entry-content {
-width:63%;
+width:50%;
+margin-left:30px;
 }
diff --git a/theme/base/images/icons/twotone/green/admin.gif b/theme/base/images/icons/twotone/green/admin.gif
new file mode 100644 (file)
index 0000000..10fa431
Binary files /dev/null and b/theme/base/images/icons/twotone/green/admin.gif differ
index 34f6b3b8a6f3791acfc87eb32aed373e29f8bf13..89197bddb941dd39056e7bd352071f1233a56a33 100644 (file)
@@ -9,17 +9,16 @@
 
 @import url(../../base/css/display.css);
 
-html,
 body,
 a:active {
-background-color:#C3D6DF;
+background-color:#CEE1E9;
 }
 body {
 font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
 font-size:1em;
 }
 address {
-margin-right:7.18%;
+margin-right:7.2%;
 }
 
 input, textarea, select, option {
@@ -27,10 +26,10 @@ font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
 }
 input, textarea, select,
 .entity_remote_subscribe {
-border-color:#aaa;
+border-color:#AAAAAA;
 }
 #filter_tags ul li {
-border-color:#C3D6DF;
+border-color:#DDDDDD;
 }
 
 .form_settings input.form_action-primary {
@@ -41,46 +40,52 @@ input.submit,
 #form_notice.warning #notice_text-count,
 .form_settings .form_note,
 .entity_remote_subscribe {
-background-color:#A9BF4F;
+background-color:#9BB43E;
 }
 
 input:focus, textarea:focus, select:focus,
 #form_notice.warning #notice_data-text {
-border-color:#A9BF4F;
+border-color:#9BB43E;
+box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
+-moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
+-webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
 }
 input.submit,
 .entity_remote_subscribe {
-color:#fff;
+color:#FFFFFF;
 }
 
 a,
 div.notice-options input,
 .form_user_block input.submit,
 .form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit,
 .entity_send-a-message a,
 .form_user_nudge input.submit,
 .entity_nudge p,
-.form_settings input.form_action-primary {
+.form_settings input.form_action-primary,
+.form_make_admin input.submit {
 color:#002E6E;
 }
 
 .notice,
 .profile {
-border-top-color:#D1D9E4;
+border-top-color:#C8D1D5;
 }
 .section .profile {
-border-top-color:#C3D6DF;
+border-top-color:#87B4C8;
 }
 
 #aside_primary {
-background-color:#CEE1E9;
+background-color:#C8D1D5;
 }
 
 #notice_text-count {
-color:#333;
+color:#333333;
 }
 #form_notice.warning #notice_text-count {
-color:#000;
+color:#000000;
 }
 #form_notice label[for=notice_data-attach] {
 background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no-repeat 0 45%;
@@ -90,27 +95,38 @@ opacity:0;
 }
 
 #form_notice.processing #notice_action-submit {
-background:#fff url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
+background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
 cursor:wait;
 text-indent:-9999px;
 }
 
+#content {
+box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3);
+-moz-box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3);
+-webkit-box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3);
+}
 #content,
 #site_nav_local_views a,
 #aside_primary {
-border-color:#fff;
+border-color:transparent;
 }
 #content,
 #site_nav_local_views .current a {
-background-color:#fff;
+background-color:#FFFFFF;
 }
 
 #site_nav_local_views a {
-background-color:rgba(255, 255, 255, 0.2);
+background-color:rgba(194, 194, 194, 0.5);
+box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
+-moz-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
+-webkit-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
 }
 #site_nav_local_views a:hover {
 background-color:rgba(255, 255, 255, 0.7);
 }
+#site_nav_local_views .current a {
+text-shadow: rgba(194,194,194,0.5) 1px 1px 1px;
+}
 
 .error {
 background-color:#F7E8E8;
@@ -120,13 +136,13 @@ background-color:#EFF3DC;
 }
 
 #anon_notice {
-background-color:#C3D6DF;
-color:#fff;
-border-color:#fff;
+background-color:#87B4C8;
+color:#FFFFFF;
+border-color:#FFFFFF;
 }
 
 #showstream #anon_notice {
-background-color:#A9BF4F;
+background-color:#9BB43E;
 }
 
 #export_data li a {
@@ -148,7 +164,10 @@ background-image:url(../../base/images/icons/icon_foaf.gif);
 .form_user_nudge input.submit,
 .form_user_block input.submit,
 .form_user_unblock input.submit,
-.entity_nudge p {
+.form_group_block input.submit,
+.form_group_unblock input.submit,
+.entity_nudge p,
+.form_make_admin input.submit {
 background-position: 0 40%;
 background-repeat: no-repeat;
 background-color:transparent;
@@ -157,13 +176,13 @@ background-color:transparent;
 .form_group_leave input.submit
 .form_user_subscribe input.submit,
 .form_user_unsubscribe input.submit {
-background-color:#A9BF4F;
-color:#fff;
+background-color:#9BB43E;
+color:#FFFFFF;
 }
 .form_user_unsubscribe input.submit,
 .form_group_leave input.submit,
 .form_user_authorization input.reject {
-background-color:#C3D6DF;
+background-color:#87B4C8;
 }
 
 .entity_edit a {
@@ -177,9 +196,14 @@ background-image:url(../../base/images/icons/twotone/green/quote.gif);
 background-image:url(../../base/images/icons/twotone/green/mail.gif);
 }
 .form_user_block input.submit,
-.form_user_unblock input.submit {
+.form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit {
 background-image:url(../../base/images/icons/twotone/green/shield.gif);
 }
+.form_make_admin input.submit {
+background-image:url(../../base/images/icons/twotone/green/admin.gif);
+}
 
 /* NOTICES */
 .notice .attachment {
@@ -206,24 +230,25 @@ background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-r
 }
 
 .notices div.entry-content,
-.notices div.notice-options,
-.notices li.hover .notices div.entry-content,
-.notices li.hover .notices div.notice-options  {
+.notices div.notice-options {
 opacity:0.4;
 }
-.notices li.hover div.entry-content,
-.notices li.hover div.notice-options {
+.notices li:hover div.entry-content,
+.notices li:hover div.notice-options {
 opacity:1;
 }
 div.entry-content {
-color:#333;
+color:#333333;
 }
 div.notice-options a,
 div.notice-options input {
 font-family:sans-serif;
 }
-.notices li.hover {
-background-color:#fcfcfc;
+#content .notices li:hover {
+background-color:rgba(240, 240, 240, 0.2);
+}
+#conversation .notices li:hover {
+background-color:transparent;
 }
 
 .notices .notices {
@@ -247,7 +272,7 @@ background:transparent url(../../base/images/icons/twotone/green/news.gif) no-re
 .pagination .nav_prev a,
 .pagination .nav_next a {
 background-repeat:no-repeat;
-border-color:#D1D9E4;
+border-color:#C8D1D5;
 }
 .pagination .nav_prev a {
 background-image:url(../../base/images/icons/twotone/green/arrow-left.gif);
index 2b06768ea4687c35baf04dd3b8e7b086f0dfe7a7..cbbd49ce6ca6a87a7d928834d158d764ad1a55af 100644 (file)
@@ -1,9 +1,14 @@
 /* IE specific styles */
 
 .notice-options input.submit {
-color:#fff;
+color:#FFFFFF;
 }
-
 #site_nav_local_views a {
-background-color:#ACCCDA;
+background-color:#C8D1D5;
+}
+#form_notice .form_note + label {
+background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no-repeat 0 45%;
+}
+#form_notice #notice_data-attach {
+filter: alpha(opacity=0);
 }
index 8a03a4d7727d514d2449b0478e6c03c821a23302..025debf34caf91049c7f2e6c597d2a5017af3c3c 100644 (file)
@@ -9,7 +9,6 @@
 
 @import url(../../base/css/display.css);
 
-html,
 body,
 a:active {
 background-color:#F0F2F5;
@@ -19,7 +18,7 @@ font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
 font-size:1em;
 }
 address {
-margin-right:7.18%;
+margin-right:7.2%;
 }
 
 input, textarea, select, option {
@@ -27,10 +26,10 @@ font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
 }
 input, textarea, select,
 .entity_remote_subscribe {
-border-color:#aaa;
+border-color:#AAAAAA;
 }
 #filter_tags ul li {
-border-color:#ddd;
+border-color:#DDDDDD;
 }
 
 .form_settings input.form_action-primary {
@@ -47,20 +46,26 @@ background-color:#9BB43E;
 input:focus, textarea:focus, select:focus,
 #form_notice.warning #notice_data-text {
 border-color:#9BB43E;
+box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
+-moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
+-webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
 }
 input.submit,
 .entity_remote_subscribe {
-color:#fff;
+color:#FFFFFF;
 }
 
 a,
 div.notice-options input,
 .form_user_block input.submit,
 .form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit,
 .entity_send-a-message a,
 .form_user_nudge input.submit,
 .entity_nudge p,
-.form_settings input.form_action-primary {
+.form_settings input.form_action-primary,
+.form_make_admin input.submit {
 color:#002E6E;
 }
 
@@ -77,10 +82,10 @@ background-color:#CEE1E9;
 }
 
 #notice_text-count {
-color:#333;
+color:#333333;
 }
 #form_notice.warning #notice_text-count {
-color:#000;
+color:#000000;
 }
 #form_notice label[for=notice_data-attach] {
 background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no-repeat 0 45%;
@@ -90,27 +95,38 @@ opacity:0;
 }
 
 #form_notice.processing #notice_action-submit {
-background:#fff url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
+background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
 cursor:wait;
 text-indent:-9999px;
 }
 
+#content {
+box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3);
+-moz-box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3);
+-webkit-box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3);
+}
 #content,
 #site_nav_local_views a,
 #aside_primary {
-border-color:#fff;
+border-color:transparent;
 }
 #content,
 #site_nav_local_views .current a {
-background-color:#fff;
+background-color:#FFFFFF;
 }
 
 #site_nav_local_views a {
-background-color:rgba(135, 180, 200, 0.3);
+background-color:rgba(194, 194, 194, 0.5);
+box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
+-moz-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
+-webkit-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5);
 }
 #site_nav_local_views a:hover {
 background-color:rgba(255, 255, 255, 0.7);
 }
+#site_nav_local_views .current a {
+text-shadow: rgba(194,194,194,0.5) 1px 1px 1px;
+}
 
 .error {
 background-color:#F7E8E8;
@@ -121,8 +137,8 @@ background-color:#EFF3DC;
 
 #anon_notice {
 background-color:#87B4C8;
-color:#fff;
-border-color:#fff;
+color:#FFFFFF;
+border-color:#FFFFFF;
 }
 
 #showstream #anon_notice {
@@ -148,7 +164,10 @@ background-image:url(../../base/images/icons/icon_foaf.gif);
 .form_user_nudge input.submit,
 .form_user_block input.submit,
 .form_user_unblock input.submit,
-.entity_nudge p {
+.form_group_block input.submit,
+.form_group_unblock input.submit,
+.entity_nudge p,
+.form_make_admin input.submit {
 background-position: 0 40%;
 background-repeat: no-repeat;
 background-color:transparent;
@@ -158,7 +177,7 @@ background-color:transparent;
 .form_user_subscribe input.submit,
 .form_user_unsubscribe input.submit {
 background-color:#9BB43E;
-color:#fff;
+color:#FFFFFF;
 }
 .form_user_unsubscribe input.submit,
 .form_group_leave input.submit,
@@ -177,9 +196,14 @@ background-image:url(../../base/images/icons/twotone/green/quote.gif);
 background-image:url(../../base/images/icons/twotone/green/mail.gif);
 }
 .form_user_block input.submit,
-.form_user_unblock input.submit {
+.form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit {
 background-image:url(../../base/images/icons/twotone/green/shield.gif);
 }
+.form_make_admin input.submit {
+background-image:url(../../base/images/icons/twotone/green/admin.gif);
+}
 
 /* NOTICES */
 .notice .attachment {
@@ -206,24 +230,25 @@ background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-r
 }
 
 .notices div.entry-content,
-.notices div.notice-options,
-.notices li.hover .notices div.entry-content,
-.notices li.hover .notices div.notice-options {
+.notices div.notice-options {
 opacity:0.4;
 }
-.notices li.hover div.entry-content,
-.notices li.hover div.notice-options {
+.notices li:hover div.entry-content,
+.notices li:hover div.notice-options {
 opacity:1;
 }
 div.entry-content {
-color:#333;
+color:#333333;
 }
 div.notice-options a,
 div.notice-options input {
 font-family:sans-serif;
 }
-.notices li.hover {
-background-color:#fcfcfc;
+#content .notices li:hover {
+background-color:rgba(240, 240, 240, 0.2);
+}
+#conversation .notices li:hover {
+background-color:transparent;
 }
 
 .notices .notices {
index 2f463bb44dfee9a14fd382a3b745bfe2a274429f..97cabc30a534a505d2b1ea92c6407aedd087493a 100644 (file)
@@ -1,9 +1,14 @@
 /* IE specific styles */
 
 .notice-options input.submit {
-color:#fff;
+color:#FFFFFF;
 }
-
 #site_nav_local_views a {
-background-color:#D0DFE7;
+background-color:#D9DADB;
+}
+#form_notice .form_note + label {
+background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no-repeat 0 45%;
+}
+#form_notice #notice_data-attach {
+filter: alpha(opacity=0);
 }
index 08427d3c847f9b61f0dfa37683d60811d7490ea2..9866e2d2cca4ae5b9e78cf01ba4ab4f5d838fbf2 100644 (file)
@@ -12,9 +12,9 @@ img { display:block; border:0; }
 a abbr { cursor: pointer; border-bottom:0; }
 table { border-collapse:collapse; }
 ol { list-style-position:inside; }
-html { font-size: 87.5%; background-color:#fff; }
+html { font-size: 87.5%; }
 body {
-background-color:#fff;
+background-color:#FFFFFF;
 color:#000;
 font-family:sans-serif;
 font-size:1em;
@@ -78,7 +78,8 @@ margin:0 0 18px 0;
 form label {
 font-weight:bold;
 }
-input.checkbox {
+input.checkbox,
+input.radio {
 position:relative;
 top:2px;
 left:0;
@@ -155,7 +156,8 @@ font-weight:bold;
 #form_invite legend,
 #form_notice_delete legend,
 #form_password_recover legend,
-#form_password_change legend {
+#form_password_change legend,
+.form_entity_block legend {
 display:none;
 }
 
@@ -181,13 +183,19 @@ margin-left:11px;
 float:left;
 width:90%;
 }
-
+.form_settings label.radio {
+margin-top:0;
+margin-right:47px;
+margin-left:11px;
+width:auto;
+}
 
 #form_login p.form_guide,
 #form_register #settings_rememberme p.form_guide,
 #form_openid_login #settings_rememberme p.form_guide,
 #settings_twitter_remove p.form_guide,
-#form_search ul.form_data #q {
+#form_search ul.form_data #q,
+#design_background-image_onoff p.form_guide {
 margin-left:0;
 }
 
@@ -375,10 +383,10 @@ margin-bottom:1em;
 }
 
 #content {
-width:50.009%;
+width:49.009%;
 min-height:259px;
 float:left;
-margin-left:18px;
+padding:0 18px;
 }
 #shownotice #content {
 min-height:0;
@@ -421,6 +429,8 @@ width:80.789%;
 height:46px;
 line-height:1.5;
 padding:7px 7px 16px 7px;
+position:relative;
+z-index:2;
 }
 #form_notice label {
 display:block;
@@ -428,8 +438,22 @@ float:left;
 font-size:1.3em;
 margin-bottom:7px;
 }
-#form_notice #notice_submit label {
-display:none;
+#form_notice label[for=notice_data-attach],
+#form_notice #notice_data-attach {
+position:absolute;
+top:25px;
+cursor:pointer;
+}
+#form_notice label[for=notice_data-attach] {
+text-indent:-9999px;
+left:394px;
+width:16px;
+height:16px;
+}
+#form_notice #notice_data-attach {
+left:183px;
+padding:0;
+height:16px;
 }
 #form_notice .form_note {
 position:absolute;
@@ -509,12 +533,15 @@ margin-bottom:4px;
 .entity_profile .entity_nickname {
 margin-left:11px;
 display:inline;
-font-weight:bold;
 }
 .entity_profile .entity_nickname {
 margin-left:0;
 }
-
+.entity_profile .fn,
+.entity_profile .nickname {
+font-size:1.1em;
+font-weight:bold;
+}
 .entity_profile .entity_fn dd:before {
 content: "(";
 font-weight:normal;
@@ -574,10 +601,13 @@ display:block;
 
 .form_user_block input.submit,
 .form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit,
 .entity_send-a-message a,
 .entity_edit a,
 .form_user_nudge input.submit,
-.entity_nudge p {
+.entity_nudge p,
+.form_make_admin input.submit {
 border:0;
 padding-left:20px;
 }
@@ -640,6 +670,7 @@ list-style-type:none;
 float:left;
 margin-right:7px;
 margin-bottom:7px;
+display:inline;
 }
 .section .entities li .photo {
 margin-right:0;
@@ -712,12 +743,17 @@ float:left;
 width:96.41%;
 border-width:1px;
 border-style:solid;
-padding:1.795%;
 margin-bottom:11px;
 }
 .notices li {
 list-style-type:none;
 }
+.notices .notices {
+margin-top:7px;
+margin-left:5%;
+width:95%;
+float:left;
+}
 
 #aside_primary .notice,
 #aside_primary .profile {
@@ -773,6 +809,9 @@ float:left;
 width:100%;
 overflow:hidden;
 }
+.notice .entry-title.ov {
+overflow:visible;
+}
 #shownotice .notice .entry-title {
 font-size:2.2em;
 }
@@ -797,7 +836,7 @@ clear:left;
 float:left;
 font-size:0.95em;
 margin-left:59px;
-width:65%;
+width:60%;
 }
 #showstream .notice div.entry-content,
 #shownotice .notice div.entry-content {
@@ -827,15 +866,12 @@ display:inline-block;
 text-transform:lowercase;
 }
 
-
 .notice-options {
-padding-left:2%;
-float:left;
-width:50%;
 position:relative;
 font-size:0.95em;
-width:12.5%;
+width:90px;
 float:right;
+margin-right:11px;
 }
 
 .notice-options a {
@@ -896,6 +932,74 @@ border:0;
 padding:0;
 }
 
+.notice .attachment {
+position:relative;
+padding-left:16px;
+}
+#attachments .attachment {
+padding-left:0;
+}
+.notice .attachment img {
+position:absolute;
+top:18px;
+left:0;
+z-index:99;
+}
+#shownotice .notice .attachment img {
+position:static;
+}
+
+#attachments {
+clear:both;
+float:left;
+width:100%;
+margin-top:18px;
+}
+#attachments dt {
+font-weight:bold;
+font-size:1.3em;
+margin-bottom:4px;
+}
+
+#attachments ol li {
+margin-bottom:18px;
+list-style-type:decimal;
+float:left;
+clear:both;
+}
+
+#jOverlayContent,
+#jOverlayContent #content,
+#jOverlayContent #content_inner {
+width: auto !important;
+margin-bottom:0;
+}
+#jOverlayContent #content {
+padding:11px;
+min-height:auto;
+}
+#jOverlayContent .external span {
+display:block;
+margin-bottom:11px;
+}
+#jOverlayContent button {
+position:absolute;
+top:0;
+right:0;
+width:29px;
+height:29px;
+text-align:center;
+font-weight:bold;
+padding:0;
+}
+#jOverlayContent h1 {
+max-width:475px;
+}
+#jOverlayContent #content {
+border-radius:7px;
+-moz-border-radius:7px;
+-webkit-border-radius:7px;
+}
 
 #usergroups #new_group {
 float: left;
@@ -1019,8 +1123,6 @@ margin-left:18px;
 }
 
 
-
-
 /* TOP_POSTERS */
 .section tbody td {
 padding-right:11px;
@@ -1140,6 +1242,18 @@ width:400px;
 margin-right:28px;
 }
 
+#settings_design_color .form_data li {
+width:33%;
+}
+#settings_design_color .form_data label {
+float:none;
+display:block;
+}
+#settings_design_color .form_data .swatch {
+padding:11px;
+margin-left:0;
+}
+
 .instructions ul {
 list-style-position:inside;
 }
index af31cf78d65c15f86eade643057722a9438ecd2c..01af500bfcb3c693bd234d5346a0dc89fc1546bd 100644 (file)
@@ -10,7 +10,7 @@
 @import url(base.css);
 
 html {
-background:#fff url(../images/illustrations/illu_pigeons-01.png) no-repeat 0 100%;
+background:url(../images/illustrations/illu_pigeons-01.png) no-repeat 0 100%;
 }
 
 body,
@@ -30,10 +30,10 @@ font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
 }
 input, textarea, select,
 .entity_remote_subscribe {
-border-color:#aaa;
+border-color:#AAAAAA;
 }
 #filter_tags ul li {
-border-color:#ddd;
+border-color:#DDDDDD;
 }
 
 .form_settings input.form_action-primary {
@@ -50,35 +50,41 @@ background-color:#8F0000;
 input:focus, textarea:focus, select:focus,
 #form_notice.warning #notice_data-text {
 border-color:#8F0000;
+box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
+-moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
+-webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
 }
 input.submit,
 .entity_remote_subscribe {
-color:#fff;
+color:#FFFFFF;
 }
 
 a,
 div.notice-options input,
 .form_user_block input.submit,
 .form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit,
 .entity_send-a-message a,
 .form_user_nudge input.submit,
 .entity_nudge p,
-.form_settings input.form_action-primary {
-color:#000;
+.form_settings input.form_action-primary,
+.form_make_admin input.submit {
+color:#000000;
 }
 
 .notice,
 .profile {
-border-color:#000;
+border-color:#000000;
 }
 .notice a,
 .profile a {
-color:#fff;
+color:#FFFFFF;
 }
 
 .notice:nth-child(3n-1),
 .profile:nth-child(3n-1) {
-border-color:#fff;
+border-color:#FFFFFF;
 }
 .notice:nth-child(3n-1) a,
 .profile:nth-child(3n-1) a {
@@ -90,7 +96,7 @@ border-color:#7F1114;
 }
 .notice:nth-child(3n) a,
 .profile:nth-child(3n) a {
-color:#000;
+color:#000000;
 }
 
 .aside .section .notice,
@@ -100,30 +106,30 @@ color:#000;
 .aside .section .notice:nth-child(3n),
 .aside .section .profile:nth-child(3n) {
 background-color:transparent;
-color:#000;
+color:#000000;
 }
 
 
 .aside .section {
-border-color:#fff;
-background-color:#fff;
-color:#000;
+border-color:#FFFFFF;
+background-color:#FFFFFF;
+color:#000000;
 }
 
 .aside .section:nth-child(n) {
-border-color:#000;
-background-color:#000;
-color:#fff;
+border-color:#000000;
+background-color:#000000;
+color:#FFFFFF;
 }
 .aside .section:nth-child(3n-1) {
-border-color:#fff;
-background-color:#fff;
-color:#000;
+border-color:#FFFFFF;
+background-color:#FFFFFF;
+color:#000000;
 }
 .aside .section:nth-child(3n) {
 background-color:#7F1114;
 border-color:#7F1114;
-color:#000;
+color:#000000;
 }
 .aside .section a {
 color:#7F1114;
@@ -132,7 +138,7 @@ color:#7F1114;
 color:#7F1114;
 }
 .aside .section:nth-child(3n) a {
-color:#fff;
+color:#FFFFFF;
 }
 
 
@@ -145,33 +151,43 @@ background:url(../images/illustrations/illu_pigeons-02.png) no-repeat 10% 100%;
 }
 
 #notice_text-count {
-color:#333;
+color:#333333;
 }
 #form_notice.warning #notice_text-count {
-color:#000;
+color:#000000;
+}
+#form_notice label[for=notice_data-attach] {
+background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no-repeat 0 45%;
+}
+#form_notice #notice_data-attach {
+opacity:0;
 }
+
 #form_notice.processing #notice_action-submit {
-background:#fff url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
+background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
 cursor:wait;
 text-indent:-9999px;
 }
 
 #content,
 #site_nav_local_views a {
-border-color:#fff;
+border-color:#FFFFFF;
 }
 #site_nav_local_views .current a {
 background-color:rgba(143, 0, 0, 0.8);
-color:#fff;
+color:#FFFFFF;
 }
 
 #site_nav_local_views a {
-background-color:rgba(255, 255, 255, 0.3);
+background-color:rgba(255, 255, 255, 0.5);
 }
 #site_nav_local_views a:hover {
-background-color:#fff;
+background-color:rgba(255, 255, 255, 0.9);
 color:#8F0000;
 }
+#site_nav_local_views .current a {
+text-shadow: rgba(194,194,194,0.5) 1px 1px 1px;
+}
 
 .error {
 background-color:#F7E8E8;
@@ -181,7 +197,7 @@ background-color:#EFF3DC;
 }
 
 #anon_notice {
-color:#000;
+color:#000000;
 }
 
 
@@ -204,7 +220,10 @@ background-image:url(../../base/images/icons/icon_foaf.gif);
 .form_user_nudge input.submit,
 .form_user_block input.submit,
 .form_user_unblock input.submit,
-.entity_nudge p {
+.form_group_block input.submit,
+.form_group_unblock input.submit,
+.entity_nudge p,
+.form_make_admin input.submit {
 background-position: 0 40%;
 background-repeat: no-repeat;
 background-color:transparent;
@@ -214,7 +233,7 @@ background-color:transparent;
 .form_user_subscribe input.submit,
 .form_user_unsubscribe input.submit {
 background-color:#8F0000;
-color:#fff;
+color:#FFFFFF;
 }
 .form_user_unsubscribe input.submit,
 .form_group_leave input.submit,
@@ -233,15 +252,22 @@ background-image:url(../../base/images/icons/twotone/green/quote.gif);
 background-image:url(../../base/images/icons/twotone/green/mail.gif);
 }
 .form_user_block input.submit,
-.form_user_unblock input.submit {
+.form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit {
 background-image:url(../../base/images/icons/twotone/green/shield.gif);
 }
+.form_make_admin input.submit {
+background-image:url(../../base/images/icons/twotone/green/admin.gif);
+}
 
 /* NOTICES */
-.notices li.over {
-background-color:#fcfcfc;
+.notice .attachment {
+background:transparent url(../../base/images/icons/twotone/green/clip-02.gif) no-repeat 0 45%;
+}
+#attachments .attachment {
+background:none;
 }
-
 .notice-options .notice_reply a,
 .notice-options form input.submit {
 background-color:transparent;
@@ -263,17 +289,36 @@ background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-r
 .notices div.notice-options {
 opacity:0.4;
 }
-.notices li.hover div.entry-content,
-.notices li.hover div.notice-options {
+.notices li:hover div.entry-content,
+.notices li:hover div.notice-options {
 opacity:1;
 }
 div.entry-content {
-color:#333;
+color:#333333;
 }
 div.notice-options a,
 div.notice-options input {
 font-family:sans-serif;
 }
+#content .notices li:hover {
+background-color:transparent;
+}
+#conversation .notices li:hover {
+background-color:transparent;
+}
+
+.notices .notices {
+background-color:rgba(200, 200, 200, 0.050);
+}
+.notices .notices .notices {
+background-color:rgba(200, 200, 200, 0.100);
+}
+.notices .notices .notices .notices {
+background-color:rgba(200, 200, 200, 0.150);
+}
+.notices .notices .notices .notices .notices {
+background-color:rgba(200, 200, 200, 0.300);
+}
 /*END: NOTICES */
 
 #new_group a {
@@ -283,7 +328,7 @@ background:transparent url(../../base/images/icons/twotone/green/news.gif) no-re
 .pagination .nav_prev a,
 .pagination .nav_next a {
 background-repeat:no-repeat;
-border-color:#000;
+border-color:#000000;
 }
 .pagination .nav_prev a {
 background-image:url(../../base/images/icons/twotone/green/arrow-left.gif);