summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeffery To <jeffery.to@gmail.com>2009-06-26 15:31:42 +0800
committerJeffery To <jeffery.to@gmail.com>2009-06-26 15:31:42 +0800
commit6328add622641e5f5721cc34d27d4d872c86a561 (patch)
tree31be2d91646bc3deed194f7c7e8ad64c9ee17971
parent6d308f6ffe3f894c60fafaea3ceaef86d30dfe1d (diff)
parent97ee517680979bf12e82eab99ecf943712fe97c9 (diff)
Merge branch '0.8.x' into invite-enabled
-rw-r--r--.gitignore2
-rw-r--r--README63
-rw-r--r--actions/accesstoken.php2
-rw-r--r--actions/all.php10
-rw-r--r--actions/allrss.php10
-rw-r--r--actions/api.php2
-rw-r--r--actions/attachment.php42
-rw-r--r--actions/attachment_ajax.php41
-rw-r--r--actions/avatarbynickname.php2
-rw-r--r--actions/block.php21
-rw-r--r--actions/blockedfromgroup.php315
-rw-r--r--actions/conversation.php14
-rw-r--r--actions/designsettings.php264
-rw-r--r--actions/disfavor.php2
-rw-r--r--actions/doc.php2
-rw-r--r--actions/editgroup.php65
-rw-r--r--actions/facebookhome.php60
-rw-r--r--actions/facebookinvite.php2
-rw-r--r--actions/facebooklogin.php2
-rw-r--r--actions/facebookremove.php2
-rw-r--r--actions/facebooksettings.php2
-rw-r--r--actions/favor.php2
-rw-r--r--actions/favoritesrss.php2
-rw-r--r--actions/featured.php5
-rw-r--r--actions/file.php46
-rw-r--r--actions/finishopenidlogin.php2
-rw-r--r--actions/finishremotesubscribe.php2
-rw-r--r--actions/foaf.php2
-rw-r--r--actions/groupblock.php215
-rw-r--r--actions/groupdesignsettings.php328
-rw-r--r--actions/grouplogo.php2
-rw-r--r--actions/groupmembers.php327
-rw-r--r--actions/groups.php12
-rw-r--r--actions/groupsearch.php2
-rw-r--r--actions/groupunblock.php149
-rw-r--r--actions/invite.php4
-rw-r--r--actions/joingroup.php5
-rw-r--r--actions/logout.php2
-rw-r--r--actions/makeadmin.php166
-rw-r--r--actions/microsummary.php2
-rw-r--r--actions/newgroup.php51
-rw-r--r--actions/newnotice.php130
-rw-r--r--actions/noticesearch.php2
-rw-r--r--actions/noticesearchrss.php2
-rw-r--r--actions/nudge.php2
-rw-r--r--actions/openidlogin.php2
-rw-r--r--actions/opensearch.php2
-rw-r--r--actions/peoplesearch.php2
-rw-r--r--actions/peopletag.php2
-rw-r--r--actions/postnotice.php2
-rw-r--r--actions/public.php8
-rw-r--r--actions/publicrss.php2
-rw-r--r--actions/publicxrds.php2
-rw-r--r--actions/recoverpassword.php2
-rw-r--r--actions/remotesubscribe.php2
-rw-r--r--actions/replies.php3
-rw-r--r--actions/repliesrss.php2
-rw-r--r--actions/requesttoken.php2
-rw-r--r--actions/showfavorites.php19
-rw-r--r--actions/showgroup.php20
-rw-r--r--actions/shownotice.php28
-rw-r--r--actions/showstream.php10
-rw-r--r--actions/subedit.php2
-rw-r--r--actions/subscribe.php2
-rw-r--r--actions/subscribers.php32
-rw-r--r--actions/subscriptions.php34
-rw-r--r--actions/sup.php2
-rw-r--r--actions/tag.php2
-rw-r--r--actions/tagother.php2
-rw-r--r--actions/tagrss.php2
-rw-r--r--actions/twitapiaccount.php2
-rw-r--r--actions/twitapiblocks.php2
-rw-r--r--actions/twitapidirect_messages.php2
-rw-r--r--actions/twitapifavorites.php73
-rw-r--r--actions/twitapifriendships.php2
-rw-r--r--actions/twitapihelp.php2
-rw-r--r--actions/twitapinotifications.php2
-rw-r--r--actions/twitapistatuses.php13
-rw-r--r--actions/twitapiusers.php4
-rw-r--r--actions/unblock.php4
-rw-r--r--actions/unsubscribe.php2
-rw-r--r--actions/updateprofile.php2
-rw-r--r--actions/userauthorization.php2
-rw-r--r--actions/userbyid.php2
-rw-r--r--actions/userdesignsettings.php206
-rw-r--r--actions/usergroups.php3
-rw-r--r--actions/userrss.php2
-rw-r--r--actions/xrds.php2
-rw-r--r--background/.gitignore0
-rw-r--r--classes/Avatar.php30
-rw-r--r--classes/Design.php155
-rw-r--r--classes/Fave.php46
-rw-r--r--classes/File.php80
-rw-r--r--classes/File_oembed.php39
-rw-r--r--classes/File_redirection.php126
-rw-r--r--classes/File_thumbnail.php19
-rw-r--r--classes/File_to_post.php30
-rw-r--r--classes/Foreign_user.php22
-rw-r--r--classes/Group_alias.php41
-rw-r--r--classes/Group_block.php115
-rw-r--r--classes/Group_inbox.php6
-rw-r--r--classes/Memcached_DataObject.php22
-rw-r--r--classes/Notice.php248
-rw-r--r--classes/Notice_inbox.php18
-rw-r--r--classes/Notice_tag.php2
-rw-r--r--classes/Profile.php87
-rw-r--r--classes/Profile_block.php2
-rw-r--r--classes/Remote_profile.php2
-rw-r--r--classes/Status_network.php143
-rw-r--r--classes/Subscription.php2
-rw-r--r--classes/User.php91
-rw-r--r--classes/User_group.php133
-rwxr-xr-x[-rw-r--r--]classes/laconica.ini65
-rw-r--r--[-rwxr-xr-x]classes/statusnet.ini5
-rw-r--r--config.php.sample8
-rw-r--r--db/074to080.sql109
-rw-r--r--db/innodb.sql2
-rw-r--r--db/laconica.sql120
-rw-r--r--db/site.sql6
-rw-r--r--extlib/Console/Getopt.php290
-rw-r--r--extlib/System/Command.php587
-rw-r--r--index.php30
-rw-r--r--install.php18
-rw-r--r--js/farbtastic/farbtastic.js20
-rw-r--r--js/userdesign.go.js (renamed from js/farbtastic/farbtastic.go.js)35
-rw-r--r--js/util.js33
-rw-r--r--lib/Shorturl_api.php2
-rw-r--r--lib/accountsettingsaction.php2
-rw-r--r--lib/arraywrapper.php2
-rw-r--r--lib/attachmentlist.php53
-rw-r--r--lib/channel.php2
-rw-r--r--lib/clienterroraction.php2
-rw-r--r--lib/command.php2
-rw-r--r--lib/commandinterpreter.php2
-rw-r--r--lib/common.php156
-rw-r--r--lib/currentuserdesignaction.php (renamed from lib/personal.php)56
-rw-r--r--lib/daemon.php2
-rw-r--r--lib/dberroraction.php2
-rw-r--r--lib/designsettings.php406
-rw-r--r--lib/error.php2
-rw-r--r--lib/facebookutil.php2
-rw-r--r--lib/galleryaction.php5
-rw-r--r--lib/groupdesignaction.php87
-rw-r--r--lib/groupeditform.php11
-rw-r--r--lib/grouplist.php2
-rw-r--r--lib/groupnav.php12
-rw-r--r--lib/grouptagcloudsection.php21
-rw-r--r--lib/imagefile.php3
-rw-r--r--lib/mailbox.php23
-rw-r--r--lib/noticeform.php18
-rw-r--r--lib/noticelist.php34
-rw-r--r--lib/oauthstore.php2
-rw-r--r--lib/omb.php2
-rw-r--r--lib/openid.php2
-rw-r--r--lib/ownerdesignaction.php88
-rw-r--r--lib/peoplesearchresults.php17
-rw-r--r--lib/profileaction.php7
-rw-r--r--lib/profilelist.php170
-rw-r--r--lib/profileminilist.php24
-rw-r--r--lib/queuehandler.php17
-rw-r--r--lib/router.php24
-rw-r--r--lib/rssaction.php4
-rw-r--r--lib/search_engines.php41
-rw-r--r--lib/searchaction.php6
-rw-r--r--lib/servererroraction.php2
-rw-r--r--lib/settingsaction.php2
-rw-r--r--lib/stream.php32
-rw-r--r--lib/subs.php2
-rw-r--r--lib/subscriptionlist.php131
-rw-r--r--lib/theme.php36
-rw-r--r--lib/twitter.php2
-rw-r--r--lib/twitterapi.php10
-rw-r--r--lib/util.php258
-rw-r--r--lib/webcolor.php192
-rw-r--r--lib/xmppqueuehandler.php11
-rwxr-xr-xscripts/allsites.php40
-rw-r--r--scripts/commandline.inc138
-rw-r--r--scripts/decache.php37
-rwxr-xr-xscripts/delete_status_network.sh21
-rwxr-xr-xscripts/enjitqueuehandler.php47
-rwxr-xr-xscripts/facebookqueuehandler.php47
-rwxr-xr-xscripts/fixup_conversations.php67
-rwxr-xr-xscripts/fixup_hashtags.php2
-rwxr-xr-xscripts/fixup_inboxes.php2
-rwxr-xr-xscripts/fixup_notices_rendered.php2
-rwxr-xr-xscripts/fixup_replies.php2
-rw-r--r--scripts/fixup_utf8.php27
-rwxr-xr-xscripts/getpiddir.php18
-rwxr-xr-xscripts/getvaliddaemons.php19
-rwxr-xr-xscripts/inbox_users.php54
-rwxr-xr-xscripts/jabberqueuehandler.php45
-rwxr-xr-xscripts/maildaemon.php33
-rwxr-xr-xscripts/ombqueuehandler.php47
-rw-r--r--scripts/pingqueuehandler.php41
-rwxr-xr-xscripts/publicqueuehandler.php47
-rw-r--r--scripts/reportsnapshot.php17
-rwxr-xr-xscripts/setpassword.php31
-rw-r--r--scripts/setup.cfg.sample14
-rwxr-xr-xscripts/setup_status_network.sh32
-rwxr-xr-xscripts/sitemap.php133
-rwxr-xr-xscripts/smsqueuehandler.php44
-rwxr-xr-xscripts/sphinx-cron.sh2
-rwxr-xr-xscripts/sphinx-indexer.sh2
-rwxr-xr-xscripts/startdaemons.sh25
-rwxr-xr-xscripts/stopdaemons.sh5
-rwxr-xr-xscripts/synctwitterfriends.php17
-rw-r--r--scripts/triminboxes.php43
-rwxr-xr-xscripts/twitterqueuehandler.php47
-rwxr-xr-xscripts/twitterstatusfetcher.php208
-rw-r--r--scripts/uncache_users.php31
-rwxr-xr-xscripts/xmppconfirmhandler.php47
-rwxr-xr-xscripts/xmppdaemon.php47
-rw-r--r--sphinx.conf.sample82
-rw-r--r--theme/base/css/display.css88
-rw-r--r--theme/base/css/ie.css21
-rw-r--r--theme/base/css/ie6.css9
-rw-r--r--theme/base/images/icons/twotone/green/admin.gifbin0 -> 100 bytes
-rw-r--r--theme/default/css/display.css97
-rw-r--r--theme/default/css/ie.css11
-rw-r--r--theme/identica/css/display.css75
-rw-r--r--theme/identica/css/ie.css11
-rw-r--r--theme/pigeonthoughts/css/base.css158
-rw-r--r--theme/pigeonthoughts/css/display.css125
223 files changed, 7751 insertions, 2087 deletions
diff --git a/.gitignore b/.gitignore
index 3418d8ee5..f4c2bba5f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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 679fa8ee3..2545465ce 100644
--- 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
----
@@ -1107,6 +1121,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
------
@@ -1203,7 +1224,6 @@ reporturl: URL to post statistics to. Defaults to Laconica developers'
set 'run' to 'never' than to set this value to something
nonsensical.
-
attachments
-----------
@@ -1218,6 +1238,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
@@ -1233,6 +1258,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
===============
diff --git a/actions/accesstoken.php b/actions/accesstoken.php
index 46b43c702..2a8cd1713 100644
--- a/actions/accesstoken.php
+++ b/actions/accesstoken.php
@@ -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/all.php b/actions/all.php
index 03179a246..f06ead2a8 100644
--- a/actions/all.php
+++ b/actions/all.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
@@ -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);
diff --git a/actions/allrss.php b/actions/allrss.php
index 45f3946a6..885a67f61 100644
--- a/actions/allrss.php
+++ b/actions/allrss.php
@@ -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();
diff --git a/actions/api.php b/actions/api.php
index b8da852b5..1fe5875ad 100644
--- a/actions/api.php
+++ b/actions/api.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
diff --git a/actions/attachment.php b/actions/attachment.php
index 16ee723d9..e4dc0e054 100644
--- a/actions/attachment.php
+++ b/actions/attachment.php
@@ -99,48 +99,6 @@ class AttachmentAction extends Action
}
/**
- * 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
*
* Only handles get, so just show the page.
diff --git a/actions/attachment_ajax.php b/actions/attachment_ajax.php
index 3d83393c5..5d6773010 100644
--- a/actions/attachment_ajax.php
+++ b/actions/attachment_ajax.php
@@ -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)) . '"';
- }
-*/
}
diff --git a/actions/avatarbynickname.php b/actions/avatarbynickname.php
index e92a99372..3e615261f 100644
--- a/actions/avatarbynickname.php
+++ b/actions/avatarbynickname.php
@@ -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/block.php b/actions/block.php
index 34f991dc6..06f92254e 100644
--- a/actions/block.php
+++ b/actions/block.php
@@ -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
index 000000000..5c1eab354
--- /dev/null
+++ b/actions/blockedfromgroup.php
@@ -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'));
+ }
+}
diff --git a/actions/conversation.php b/actions/conversation.php
index 20c68986c..654a670f5 100644
--- a/actions/conversation.php
+++ b/actions/conversation.php
@@ -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
index 5774b8537..000000000
--- a/actions/designsettings.php
+++ /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));
- }
-}
diff --git a/actions/disfavor.php b/actions/disfavor.php
index bc13b09da..740f7de93 100644
--- a/actions/disfavor.php
+++ b/actions/disfavor.php
@@ -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/doc.php b/actions/doc.php
index e6508030b..54ae13803 100644
--- a/actions/doc.php
+++ b/actions/doc.php
@@ -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/editgroup.php b/actions/editgroup.php
index 39dad0465..6aa6f8b11 100644
--- a/actions/editgroup.php
+++ b/actions/editgroup.php
@@ -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;
}
}
diff --git a/actions/facebookhome.php b/actions/facebookhome.php
index 00b35ef68..34989c978 100644
--- a/actions/facebookhome.php
+++ b/actions/facebookhome.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
@@ -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');
}
}
-
}
diff --git a/actions/facebookinvite.php b/actions/facebookinvite.php
index 2207580f7..f43d04e27 100644
--- a/actions/facebookinvite.php
+++ b/actions/facebookinvite.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
diff --git a/actions/facebooklogin.php b/actions/facebooklogin.php
index 94d494a82..22007da4f 100644
--- a/actions/facebooklogin.php
+++ b/actions/facebooklogin.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
diff --git a/actions/facebookremove.php b/actions/facebookremove.php
index 376e12a2e..9ca7a77a8 100644
--- a/actions/facebookremove.php
+++ b/actions/facebookremove.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
diff --git a/actions/facebooksettings.php b/actions/facebooksettings.php
index 227e12316..ee2c279ab 100644
--- a/actions/facebooksettings.php
+++ b/actions/facebooksettings.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
diff --git a/actions/favor.php b/actions/favor.php
index 3b7d979eb..ec86b17e6 100644
--- a/actions/favor.php
+++ b/actions/favor.php
@@ -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
diff --git a/actions/favoritesrss.php b/actions/favoritesrss.php
index 6b46b8dec..c439a9a62 100644
--- a/actions/favoritesrss.php
+++ b/actions/favoritesrss.php
@@ -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/featured.php b/actions/featured.php
index 79eba2aa6..04365687d 100644
--- a/actions/featured.php
+++ b/actions/featured.php
@@ -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();
}
diff --git a/actions/file.php b/actions/file.php
index 1179dbe9a..271f57ab9 100644
--- a/actions/file.php
+++ b/actions/file.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
@@ -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);
}
}
diff --git a/actions/finishopenidlogin.php b/actions/finishopenidlogin.php
index b08b96df6..e9f7c746b 100644
--- a/actions/finishopenidlogin.php
+++ b/actions/finishopenidlogin.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
diff --git a/actions/finishremotesubscribe.php b/actions/finishremotesubscribe.php
index 3e3a81715..5c764aeb0 100644
--- a/actions/finishremotesubscribe.php
+++ b/actions/finishremotesubscribe.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
diff --git a/actions/foaf.php b/actions/foaf.php
index fb0172eb9..b481b2437 100644
--- a/actions/foaf.php
+++ b/actions/foaf.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
diff --git a/actions/groupblock.php b/actions/groupblock.php
new file mode 100644
index 000000000..ce2c6c144
--- /dev/null
+++ b/actions/groupblock.php
@@ -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
index 000000000..79c192ac4
--- /dev/null
+++ b/actions/groupdesignsettings.php
@@ -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();
+ }
+ }
+
+}
diff --git a/actions/grouplogo.php b/actions/grouplogo.php
index fe6127da2..8f6158dac 100644
--- a/actions/grouplogo.php
+++ b/actions/grouplogo.php
@@ -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;
diff --git a/actions/groupmembers.php b/actions/groupmembers.php
index 21e5ebbaa..d132cdf96 100644
--- a/actions/groupmembers.php
+++ b/actions/groupmembers.php
@@ -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'));
+ }
+}
diff --git a/actions/groups.php b/actions/groups.php
index 26b52a5fc..b49d80f37 100644
--- a/actions/groups.php
+++ b/actions/groups.php
@@ -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;
diff --git a/actions/groupsearch.php b/actions/groupsearch.php
index 06b4a7755..c50466ce6 100644
--- a/actions/groupsearch.php
+++ b/actions/groupsearch.php
@@ -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
index 000000000..6beb46352
--- /dev/null
+++ b/actions/groupunblock.php
@@ -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);
+ }
+ }
+}
+
diff --git a/actions/invite.php b/actions/invite.php
index 9631f7a7f..bdea4807d 100644
--- a/actions/invite.php
+++ b/actions/invite.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
@@ -19,7 +19,7 @@
if (!defined('LACONICA')) { exit(1); }
-class InviteAction extends Action
+class InviteAction extends CurrentUserDesignAction
{
var $mode = null;
var $error = null;
diff --git a/actions/joingroup.php b/actions/joingroup.php
index a5d82ddc7..0e4f96eaf 100644
--- a/actions/joingroup.php
+++ b/actions/joingroup.php
@@ -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;
}
diff --git a/actions/logout.php b/actions/logout.php
index c34b10987..3fcfb4f4e 100644
--- a/actions/logout.php
+++ b/actions/logout.php
@@ -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
index 000000000..6fc2cf9ab
--- /dev/null
+++ b/actions/makeadmin.php
@@ -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);
+ }
+ }
+}
diff --git a/actions/microsummary.php b/actions/microsummary.php
index 0b408ec95..6884a919a 100644
--- a/actions/microsummary.php
+++ b/actions/microsummary.php
@@ -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/newgroup.php b/actions/newgroup.php
index 67cd6b2f1..0289e77c2 100644
--- a/actions/newgroup.php
+++ b/actions/newgroup.php
@@ -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;
}
}
diff --git a/actions/newnotice.php b/actions/newnotice.php
index 02976a2ae..15caff6ea 100644
--- a/actions/newnotice.php
+++ b/actions/newnotice.php
@@ -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)));
}
/**
diff --git a/actions/noticesearch.php b/actions/noticesearch.php
index d996998fc..49b473d9e 100644
--- a/actions/noticesearch.php
+++ b/actions/noticesearch.php
@@ -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/noticesearchrss.php b/actions/noticesearchrss.php
index f6da969ee..c1bf3bf5f 100644
--- a/actions/noticesearchrss.php
+++ b/actions/noticesearchrss.php
@@ -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/nudge.php b/actions/nudge.php
index c23d3e643..78c0ee566 100644
--- a/actions/nudge.php
+++ b/actions/nudge.php
@@ -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/openidlogin.php b/actions/openidlogin.php
index 1a4372d73..a8d052096 100644
--- a/actions/openidlogin.php
+++ b/actions/openidlogin.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
diff --git a/actions/opensearch.php b/actions/opensearch.php
index d1f4895ce..4fe95c93b 100644
--- a/actions/opensearch.php
+++ b/actions/opensearch.php
@@ -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/peoplesearch.php b/actions/peoplesearch.php
index 65d970dd1..c61e0e273 100644
--- a/actions/peoplesearch.php
+++ b/actions/peoplesearch.php
@@ -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/peopletag.php b/actions/peopletag.php
index 5add75485..dd3c1c089 100644
--- a/actions/peopletag.php
+++ b/actions/peopletag.php
@@ -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,
diff --git a/actions/postnotice.php b/actions/postnotice.php
index 3e98b3cd5..eb2d63b61 100644
--- a/actions/postnotice.php
+++ b/actions/postnotice.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
diff --git a/actions/public.php b/actions/public.php
index 27153f131..9851285c4 100644
--- a/actions/public.php
+++ b/actions/public.php
@@ -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;
diff --git a/actions/publicrss.php b/actions/publicrss.php
index bc52f2952..7e8df9625 100644
--- a/actions/publicrss.php
+++ b/actions/publicrss.php
@@ -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/publicxrds.php b/actions/publicxrds.php
index 283a932ca..0a1421550 100644
--- a/actions/publicxrds.php
+++ b/actions/publicxrds.php
@@ -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/recoverpassword.php b/actions/recoverpassword.php
index 82263fcd5..2afd052a7 100644
--- a/actions/recoverpassword.php
+++ b/actions/recoverpassword.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
diff --git a/actions/remotesubscribe.php b/actions/remotesubscribe.php
index 0b1174896..e658f8d37 100644
--- a/actions/remotesubscribe.php
+++ b/actions/remotesubscribe.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
diff --git a/actions/replies.php b/actions/replies.php
index eac4d0a3a..d7ed440e9 100644
--- a/actions/replies.php
+++ b/actions/replies.php
@@ -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;
/**
diff --git a/actions/repliesrss.php b/actions/repliesrss.php
index 2017c4309..a87e2870d 100644
--- a/actions/repliesrss.php
+++ b/actions/repliesrss.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
diff --git a/actions/requesttoken.php b/actions/requesttoken.php
index 9507e3d6c..8d1e3f004 100644
--- a/actions/requesttoken.php
+++ b/actions/requesttoken.php
@@ -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/showfavorites.php b/actions/showfavorites.php
index 865045337..b723924a5 100644
--- a/actions/showfavorites.php
+++ b/actions/showfavorites.php
@@ -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;
}
diff --git a/actions/showgroup.php b/actions/showgroup.php
index 29b6fa1e6..b6a0f4844 100644
--- a/actions/showgroup.php
+++ b/actions/showgroup.php
@@ -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)'));
diff --git a/actions/shownotice.php b/actions/shownotice.php
index b0d973a99..0d89af5ac 100644
--- a/actions/shownotice.php
+++ b/actions/shownotice.php
@@ -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();
+ }
+}
diff --git a/actions/showstream.php b/actions/showstream.php
index e2f4e24d4..cd5d4bb70 100644
--- a/actions/showstream.php
+++ b/actions/showstream.php
@@ -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();
diff --git a/actions/subedit.php b/actions/subedit.php
index 8ca2d7914..2e1bf5538 100644
--- a/actions/subedit.php
+++ b/actions/subedit.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
diff --git a/actions/subscribe.php b/actions/subscribe.php
index 0bc522867..15b89a312 100644
--- a/actions/subscribe.php
+++ b/actions/subscribe.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
diff --git a/actions/subscribers.php b/actions/subscribers.php
index 4482de9a7..66ac00fb1 100644
--- a/actions/subscribers.php
+++ b/actions/subscribers.php
@@ -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();
+ }
}
}
diff --git a/actions/subscriptions.php b/actions/subscriptions.php
index 095b18ad8..4124abea4 100644
--- a/actions/subscriptions.php
+++ b/actions/subscriptions.php
@@ -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'));
diff --git a/actions/sup.php b/actions/sup.php
index 691153d6a..e446a7b0d 100644
--- a/actions/sup.php
+++ b/actions/sup.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
diff --git a/actions/tag.php b/actions/tag.php
index d0ad797eb..888aba062 100644
--- a/actions/tag.php
+++ b/actions/tag.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
diff --git a/actions/tagother.php b/actions/tagother.php
index 0c5bb7cf3..96246f799 100644
--- a/actions/tagother.php
+++ b/actions/tagother.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
diff --git a/actions/tagrss.php b/actions/tagrss.php
index 83cf3afe2..f69374fca 100644
--- a/actions/tagrss.php
+++ b/actions/tagrss.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
diff --git a/actions/twitapiaccount.php b/actions/twitapiaccount.php
index b5e7b91da..f2a7534a2 100644
--- a/actions/twitapiaccount.php
+++ b/actions/twitapiaccount.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
diff --git a/actions/twitapiblocks.php b/actions/twitapiblocks.php
index 0e3509162..d8e72efb1 100644
--- a/actions/twitapiblocks.php
+++ b/actions/twitapiblocks.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
diff --git a/actions/twitapidirect_messages.php b/actions/twitapidirect_messages.php
index 85c788d6a..bd27e9d20 100644
--- a/actions/twitapidirect_messages.php
+++ b/actions/twitapidirect_messages.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
diff --git a/actions/twitapifavorites.php b/actions/twitapifavorites.php
index 8656adbe8..8256668f3 100644
--- a/actions/twitapifavorites.php
+++ b/actions/twitapifavorites.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
@@ -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.
diff --git a/actions/twitapifriendships.php b/actions/twitapifriendships.php
index b1c277748..29eb4cc0f 100644
--- a/actions/twitapifriendships.php
+++ b/actions/twitapifriendships.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
diff --git a/actions/twitapihelp.php b/actions/twitapihelp.php
index bdef1314a..dab2b34f9 100644
--- a/actions/twitapihelp.php
+++ b/actions/twitapihelp.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
diff --git a/actions/twitapinotifications.php b/actions/twitapinotifications.php
index 411971af1..09b11766b 100644
--- a/actions/twitapinotifications.php
+++ b/actions/twitapinotifications.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
diff --git a/actions/twitapistatuses.php b/actions/twitapistatuses.php
index 94f624afb..555c746cb 100644
--- a/actions/twitapistatuses.php
+++ b/actions/twitapistatuses.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
@@ -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':
diff --git a/actions/twitapiusers.php b/actions/twitapiusers.php
index 13a8746cd..4057b63e7 100644
--- a/actions/twitapiusers.php
+++ b/actions/twitapiusers.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
@@ -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');
diff --git a/actions/unblock.php b/actions/unblock.php
index 8573b2a87..05d57c60d 100644
--- a/actions/unblock.php
+++ b/actions/unblock.php
@@ -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);
}
diff --git a/actions/unsubscribe.php b/actions/unsubscribe.php
index 7dcab04c0..19275041a 100644
--- a/actions/unsubscribe.php
+++ b/actions/unsubscribe.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
diff --git a/actions/updateprofile.php b/actions/updateprofile.php
index 08cb31ae0..d8b62fb09 100644
--- a/actions/updateprofile.php
+++ b/actions/updateprofile.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
diff --git a/actions/userauthorization.php b/actions/userauthorization.php
index 168019149..8dc2c808d 100644
--- a/actions/userauthorization.php
+++ b/actions/userauthorization.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
diff --git a/actions/userbyid.php b/actions/userbyid.php
index 4a985fcd7..8b686ae10 100644
--- a/actions/userbyid.php
+++ b/actions/userbyid.php
@@ -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
index 000000000..6e745e96f
--- /dev/null
+++ b/actions/userdesignsettings.php
@@ -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);
+ }
+}
diff --git a/actions/usergroups.php b/actions/usergroups.php
index e3088dcbd..7ead6e6e4 100644
--- a/actions/usergroups.php
+++ b/actions/usergroups.php
@@ -46,9 +46,8 @@ require_once INSTALLDIR.'/lib/grouplist.php';
* @link http://laconi.ca/
*/
-class UsergroupsAction extends Action
+class UsergroupsAction extends OwnerDesignAction
{
- var $user = null;
var $page = null;
var $profile = null;
diff --git a/actions/userrss.php b/actions/userrss.php
index 2280509b2..8a940865f 100644
--- a/actions/userrss.php
+++ b/actions/userrss.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
diff --git a/actions/xrds.php b/actions/xrds.php
index 1335b6b80..9327a3c83 100644
--- a/actions/xrds.php
+++ b/actions/xrds.php
@@ -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
index 000000000..e69de29bb
--- /dev/null
+++ b/background/.gitignore
diff --git a/classes/Avatar.php b/classes/Avatar.php
index db9d78e47..5e8b315fe 100644
--- a/classes/Avatar.php
+++ b/classes/Avatar.php
@@ -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
index 000000000..da4b670be
--- /dev/null
+++ b/classes/Design.php
@@ -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;
+ }
+ }
+
+}
diff --git a/classes/Fave.php b/classes/Fave.php
index 572334ce4..f4cf6256f 100644
--- a/classes/Fave.php
+++ b/classes/Fave.php
@@ -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;
}
}
diff --git a/classes/File.php b/classes/File.php
index 24ab11b8e..5dd7cd865 100644
--- a/classes/File.php
+++ b/classes/File.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
@@ -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;
+ }
}
diff --git a/classes/File_oembed.php b/classes/File_oembed.php
index f1b2cb13c..69230e4a4 100644
--- a/classes/File_oembed.php
+++ b/classes/File_oembed.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
@@ -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
}
}
-
diff --git a/classes/File_redirection.php b/classes/File_redirection.php
index 212cc3615..d6fa0bcb6 100644
--- a/classes/File_redirection.php
+++ b/classes/File_redirection.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
@@ -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://') {
diff --git a/classes/File_thumbnail.php b/classes/File_thumbnail.php
index 1a65b92c9..44b92a2fa 100644
--- a/classes/File_thumbnail.php
+++ b/classes/File_thumbnail.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
@@ -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;
diff --git a/classes/File_to_post.php b/classes/File_to_post.php
index 00ddebe6b..d35febb77 100644
--- a/classes/File_to_post.php
+++ b/classes/File_to_post.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
@@ -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);
}
}
diff --git a/classes/Foreign_user.php b/classes/Foreign_user.php
index 61727abe5..8b3e03dfb 100644
--- a/classes/Foreign_user.php
+++ b/classes/Foreign_user.php
@@ -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
index 000000000..e801e50e1
--- /dev/null
+++ b/classes/Group_alias.php
@@ -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
index 000000000..7922c19a9
--- /dev/null
+++ b/classes/Group_block.php
@@ -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;
+ }
+
+}
diff --git a/classes/Group_inbox.php b/classes/Group_inbox.php
index b80ba4272..1af7439f7 100644
--- a/classes/Group_inbox.php
+++ b/classes/Group_inbox.php
@@ -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);
+ }
}
diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php
index 33ac70dd0..f7cbb9d5b 100644
--- a/classes/Memcached_DataObject.php
+++ b/classes/Memcached_DataObject.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
@@ -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;
}
+
}
diff --git a/classes/Notice.php b/classes/Notice.php
index 1c4858149..fdcef1bc2 100644
--- a/classes/Notice.php
+++ b/classes/Notice.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
@@ -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()
diff --git a/classes/Notice_inbox.php b/classes/Notice_inbox.php
index 673e187c7..940381f84 100644
--- a/classes/Notice_inbox.php
+++ b/classes/Notice_inbox.php
@@ -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);
}
diff --git a/classes/Notice_tag.php b/classes/Notice_tag.php
index 758a66594..4e52ef269 100644
--- a/classes/Notice_tag.php
+++ b/classes/Notice_tag.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
diff --git a/classes/Profile.php b/classes/Profile.php
index 4a459b974..a0ed6b3ca 100644
--- a/classes/Profile.php
+++ b/classes/Profile.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
@@ -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;
+ }
}
diff --git a/classes/Profile_block.php b/classes/Profile_block.php
index 551e690e2..feadea42d 100644
--- a/classes/Profile_block.php
+++ b/classes/Profile_block.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
diff --git a/classes/Remote_profile.php b/classes/Remote_profile.php
index 5aa6d913e..975852dd9 100644
--- a/classes/Remote_profile.php
+++ b/classes/Remote_profile.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
diff --git a/classes/Status_network.php b/classes/Status_network.php
index f7747f71d..f8d6756b6 100644
--- a/classes/Status_network.php
+++ b/classes/Status_network.php
@@ -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;
}
}
diff --git a/classes/Subscription.php b/classes/Subscription.php
index 3fe0d167f..d4580fcba 100644
--- a/classes/Subscription.php
+++ b/classes/Subscription.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
diff --git a/classes/User.php b/classes/User.php
index 08a166d5a..62a3f8a66 100644
--- a/classes/User.php
+++ b/classes/User.php
@@ -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);
}
@@ -443,6 +442,33 @@ class User extends Memcached_DataObject
$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 =
+ 'SELECT notice.* ' .
+ 'FROM notice JOIN subscription ON notice.profile_id = subscription.subscribed ' .
'WHERE subscription.subscriber = %d ';
return Notice::getStream(sprintf($qry, $this->id),
'user:notices_with_friends:' . $this->id,
@@ -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);
+ }
}
diff --git a/classes/User_group.php b/classes/User_group.php
index a135015ba..9b4b01ead 100644
--- a/classes/User_group.php
+++ b/classes/User_group.php
@@ -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;
+ }
}
diff --git a/classes/laconica.ini b/classes/laconica.ini
index 92bbb35d4..7e9b2b791 100644..100755
--- a/classes/laconica.ini
+++ b/classes/laconica.ini
@@ -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
diff --git a/classes/statusnet.ini b/classes/statusnet.ini
index a70cd4122..8123265e4 100755..100644
--- a/classes/statusnet.ini
+++ b/classes/statusnet.ini
@@ -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
diff --git a/config.php.sample b/config.php.sample
index 2e5bcaca0..a23b41b31 100644
--- a/config.php.sample
+++ b/config.php.sample
@@ -156,6 +156,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.
@@ -225,4 +231,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
index 000000000..ff0819159
--- /dev/null
+++ b/db/074to080.sql
@@ -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
index 000000000..f3ab6cd69
--- /dev/null
+++ b/db/innodb.sql
@@ -0,0 +1,2 @@
+alter table profile drop index nickname, engine=InnoDB;
+alter table notice drop index content, engine=InnoDB;
diff --git a/db/laconica.sql b/db/laconica.sql
index a11e31692..3f8918de6 100644
--- a/db/laconica.sql
+++ b/db/laconica.sql
@@ -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;
diff --git a/db/site.sql b/db/site.sql
index 660ba475b..a9f64e5a5 100644
--- a/db/site.sql
+++ b/db/site.sql
@@ -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
index 000000000..bb9d69ca2
--- /dev/null
+++ b/extlib/Console/Getopt.php
@@ -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
index 000000000..f5c3ec6b9
--- /dev/null
+++ b/extlib/System/Command.php
@@ -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);
+ }
+ }
+
+ // }}}
+}
+?>
diff --git a/index.php b/index.php
index 4eff99dff..cb6a0fe60 100644
--- 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."));
diff --git a/install.php b/install.php
index 133f2b30f..b94a92936 100644
--- a/install.php
+++ b/install.php
@@ -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>
@@ -117,11 +116,6 @@ function showForm()
<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" />
<p class="form_guide">Database hostname</p>
@@ -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.js b/js/farbtastic/farbtastic.js
index 24a377803..d8b5ad9cd 100644
--- a/js/farbtastic/farbtastic.js
+++ b/js/farbtastic/farbtastic.js
@@ -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/farbtastic/farbtastic.go.js b/js/userdesign.go.js
index 0149eca7d..833b19adc 100644
--- a/js/farbtastic/farbtastic.go.js
+++ b/js/userdesign.go.js
@@ -10,19 +10,19 @@ $(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});
+ case 1: default:
+ $('html, body').css({'background-color':C});
break;
case 2:
- $('#aside_primary').css({'background-color':C});
+ $('#content, #site_nav_local_views .current a').css({'background-color':C});
break;
case 3:
- $('body').css({'color':C});
+ $('#aside_primary').css({'background-color':C});
break;
case 4:
+ $('html body').css({'color':C});
+ break;
+ case 5:
$('a').css({'color':C});
break;
}
@@ -49,7 +49,7 @@ $(document).ready(function() {
}
}
- function Init() {
+ function InitFarbtastic() {
$('#settings_design_color').append('<div id="color-picker"></div>');
$('#color-picker').hide();
@@ -59,7 +59,9 @@ $(document).ready(function() {
swatches
.each(SynchColors)
.blur(function() {
- $(this).val($(this).val().toUpperCase());
+ 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();
@@ -73,13 +75,24 @@ $(document).ready(function() {
}
var f, swatches;
- Init();
+ InitFarbtastic();
$('#form_settings_design').bind('reset', function(){
setTimeout(function(){
swatches.each(function(){UpdateColors(this);});
$('#color-picker').remove();
swatches.unbind();
- Init();
+ 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'});
+ });
});
diff --git a/js/util.js b/js/util.js
index fd2500d44..e7c54b74a 100644
--- a/js/util.js
+++ b/js/util.js
@@ -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('');
+ });
+ });
+}
diff --git a/lib/Shorturl_api.php b/lib/Shorturl_api.php
index 924aa93a8..29f4eb3a6 100644
--- a/lib/Shorturl_api.php
+++ b/lib/Shorturl_api.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
diff --git a/lib/accountsettingsaction.php b/lib/accountsettingsaction.php
index 86800d2a3..4ab50abce 100644
--- a/lib/accountsettingsaction.php
+++ b/lib/accountsettingsaction.php
@@ -115,7 +115,7 @@ class AccountSettingsNav extends Widget
'openidsettings' =>
array(_('OpenID'),
_('Add or remove OpenIDs')),
- 'designsettings' =>
+ 'userdesignsettings' =>
array(_('Design'),
_('Design your profile')),
'othersettings' =>
diff --git a/lib/arraywrapper.php b/lib/arraywrapper.php
index ef0eeffa5..a8a12b3bb 100644
--- a/lib/arraywrapper.php
+++ b/lib/arraywrapper.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
diff --git a/lib/attachmentlist.php b/lib/attachmentlist.php
index 45e4fa319..f6a1b59d0 100644
--- a/lib/attachmentlist.php
+++ b/lib/attachmentlist.php
@@ -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();
}
diff --git a/lib/channel.php b/lib/channel.php
index f1e205546..38c1d4d67 100644
--- a/lib/channel.php
+++ b/lib/channel.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
diff --git a/lib/clienterroraction.php b/lib/clienterroraction.php
index 0c48414d5..7ddc35eb6 100644
--- a/lib/clienterroraction.php
+++ b/lib/clienterroraction.php
@@ -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/lib/command.php b/lib/command.php
index 507990a0b..564661382 100644
--- a/lib/command.php
+++ b/lib/command.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
diff --git a/lib/commandinterpreter.php b/lib/commandinterpreter.php
index 49c733c03..6538d4442 100644
--- a/lib/commandinterpreter.php
+++ b/lib/commandinterpreter.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
diff --git a/lib/common.php b/lib/common.php
index b51661f2a..c9e5fb0c9 100644
--- a/lib/common.php
+++ b/lib/common.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
@@ -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
@@ -152,6 +197,7 @@ $config =
'memcached' =>
array('enabled' => false,
'server' => 'localhost',
+ 'base' => null,
'port' => 11211),
'ping' =>
array('notify' => array()),
@@ -165,41 +211,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');
@@ -225,14 +281,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/personal.php b/lib/currentuserdesignaction.php
index f92732375..7c2520cf6 100644
--- a/lib/personal.php
+++ b/lib/currentuserdesignaction.php
@@ -2,7 +2,7 @@
/**
* Laconica, the distributed open-source microblogging tool
*
- * User profile page
+ * Base class for actions that use the current user's design
*
* PHP version 5
*
@@ -19,11 +19,10 @@
* 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
+ * @category Action
* @package Laconica
* @author Evan Prodromou <evan@controlyourself.ca>
- * @author Sarven Capadisli <csarven@controlyourself.ca>
- * @copyright 2008-2009 Control Yourself, Inc.
+ * @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/
*/
@@ -33,28 +32,57 @@ if (!defined('LACONICA')) {
}
/**
- * Base class for user profile page
+ * Base class for actions that use the current user's design
*
- * @category Personal
+ * 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 PersonalAction extends Action
+class CurrentUserDesignAction extends Action
{
- var $user = null;
+ /**
+ * Show the user's design stylesheet
+ *
+ * @return nothing
+ */
+ function showStylesheets()
+ {
+ parent::showStylesheets();
- function isReadOnly($args)
- {
- return true;
- }
+ $design = $this->getDesign();
+
+ if (!empty($design)) {
+ $design->showCSS($this);
+ }
+ }
- function handle($args)
+ /**
+ * 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()
{
- parent::handle($args);
+ $cur = common_current_user();
+
+ if (empty($cur)) {
+ return null;
+ }
+
+ return $cur->getDesign();
}
+
}
diff --git a/lib/daemon.php b/lib/daemon.php
index 9c1ae50a0..a0df00bdc 100644
--- a/lib/daemon.php
+++ b/lib/daemon.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
diff --git a/lib/dberroraction.php b/lib/dberroraction.php
index 0dc92490c..a04e5f74f 100644
--- a/lib/dberroraction.php
+++ b/lib/dberroraction.php
@@ -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
index 000000000..9650679ac
--- /dev/null
+++ b/lib/designsettings.php
@@ -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;
+ }
+ }
+ }
+
+}
diff --git a/lib/error.php b/lib/error.php
index 282682133..bbf9987cf 100644
--- a/lib/error.php
+++ b/lib/error.php
@@ -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/lib/facebookutil.php b/lib/facebookutil.php
index 242d2e06f..4d0df797b 100644
--- a/lib/facebookutil.php
+++ b/lib/facebookutil.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
diff --git a/lib/galleryaction.php b/lib/galleryaction.php
index 8fa11a756..b389fc00f 100644
--- a/lib/galleryaction.php
+++ b/lib/galleryaction.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
@@ -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
index 000000000..bc95921f1
--- /dev/null
+++ b/lib/groupdesignaction.php
@@ -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();
+ }
+
+}
diff --git a/lib/groupeditform.php b/lib/groupeditform.php
index ca674f3c8..7e8d6eea3 100644
--- a/lib/groupeditform.php
+++ b/lib/groupeditform.php
@@ -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');
}
diff --git a/lib/grouplist.php b/lib/grouplist.php
index 1b8547499..1ded5160b 100644
--- a/lib/grouplist.php
+++ b/lib/grouplist.php
@@ -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();
}
diff --git a/lib/groupnav.php b/lib/groupnav.php
index 90bdc1014..9e530c447 100644
--- a/lib/groupnav.php
+++ b/lib/groupnav.php
@@ -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');
}
diff --git a/lib/grouptagcloudsection.php b/lib/grouptagcloudsection.php
index 5d68af28b..9b7a10f6b 100644
--- a/lib/grouptagcloudsection.php
+++ b/lib/grouptagcloudsection.php
@@ -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;
}
-
}
diff --git a/lib/imagefile.php b/lib/imagefile.php
index 0c93b257e..52e4c4b22 100644
--- a/lib/imagefile.php
+++ b/lib/imagefile.php
@@ -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']);
diff --git a/lib/mailbox.php b/lib/mailbox.php
index 01bbf5721..f1f6e98c1 100644
--- a/lib/mailbox.php
+++ b/lib/mailbox.php
@@ -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;
+ }
+
}
diff --git a/lib/noticeform.php b/lib/noticeform.php
index 3212f382a..4e2a2edd6 100644
--- a/lib/noticeform.php
+++ b/lib/noticeform.php
@@ -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');
}
diff --git a/lib/noticelist.php b/lib/noticelist.php
index fadc238a4..44726a17b 100644
--- a/lib/noticelist.php
+++ b/lib/noticelist.php
@@ -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');
diff --git a/lib/oauthstore.php b/lib/oauthstore.php
index 183164e17..f224c6c22 100644
--- a/lib/oauthstore.php
+++ b/lib/oauthstore.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
diff --git a/lib/omb.php b/lib/omb.php
index 40cb847df..4f6a96095 100644
--- a/lib/omb.php
+++ b/lib/omb.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
diff --git a/lib/openid.php b/lib/openid.php
index 3af7a39cf..0b7633284 100644
--- a/lib/openid.php
+++ b/lib/openid.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
diff --git a/lib/ownerdesignaction.php b/lib/ownerdesignaction.php
new file mode 100644
index 000000000..424474f42
--- /dev/null
+++ b/lib/ownerdesignaction.php
@@ -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();
+ }
+}
diff --git a/lib/peoplesearchresults.php b/lib/peoplesearchresults.php
index d3f840852..9f6696b5f 100644
--- a/lib/peoplesearchresults.php
+++ b/lib/peoplesearchresults.php
@@ -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/profileaction.php b/lib/profileaction.php
index a3437ff4d..2519922b2 100644
--- a/lib/profileaction.php
+++ b/lib/profileaction.php
@@ -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)'));
diff --git a/lib/profilelist.php b/lib/profilelist.php
index a4cc23555..a604230f8 100644
--- a/lib/profilelist.php
+++ b/lib/profilelist.php
@@ -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()
- {
- }
}
diff --git a/lib/profileminilist.php b/lib/profileminilist.php
index 57496d0e9..09bef6f7c 100644
--- a/lib/profileminilist.php
+++ b/lib/profileminilist.php
@@ -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(),
diff --git a/lib/queuehandler.php b/lib/queuehandler.php
index f76f16e07..ae403c65e 100644
--- a/lib/queuehandler.php
+++ b/lib/queuehandler.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
@@ -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());
diff --git a/lib/router.php b/lib/router.php
index 456d1793e..1f39c60dc 100644
--- a/lib/router.php
+++ b/lib/router.php
@@ -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',
diff --git a/lib/rssaction.php b/lib/rssaction.php
index eafdbf131..6f6c9a8cb 100644
--- a/lib/rssaction.php
+++ b/lib/rssaction.php
@@ -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;
}
diff --git a/lib/search_engines.php b/lib/search_engines.php
index 7b9dbb618..772f41883 100644
--- a/lib/search_engines.php
+++ b/lib/search_engines.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
@@ -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);
diff --git a/lib/searchaction.php b/lib/searchaction.php
index e74450e11..34fe9373f 100644
--- a/lib/searchaction.php
+++ b/lib/searchaction.php
@@ -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');
diff --git a/lib/servererroraction.php b/lib/servererroraction.php
index 595dcf147..db7352166 100644
--- a/lib/servererroraction.php
+++ b/lib/servererroraction.php
@@ -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/lib/settingsaction.php b/lib/settingsaction.php
index db20c5804..17d3a2f64 100644
--- a/lib/settingsaction.php
+++ b/lib/settingsaction.php
@@ -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
index 0cb9e0bf4..000000000
--- a/lib/stream.php
+++ /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();
- }
-}
diff --git a/lib/subs.php b/lib/subs.php
index 0e7b9ded5..3bd67b39c 100644
--- a/lib/subs.php
+++ b/lib/subs.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
diff --git a/lib/subscriptionlist.php b/lib/subscriptionlist.php
new file mode 100644
index 000000000..23da64cca
--- /dev/null
+++ b/lib/subscriptionlist.php
@@ -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');
+ }
+}
diff --git a/lib/theme.php b/lib/theme.php
index 0d8824822..2fe6ab69b 100644
--- a/lib/theme.php
+++ b/lib/theme.php
@@ -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;
}
diff --git a/lib/twitter.php b/lib/twitter.php
index c1d0dc254..3ec082686 100644
--- a/lib/twitter.php
+++ b/lib/twitter.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
diff --git a/lib/twitterapi.php b/lib/twitterapi.php
index 569bc6d7a..f538a0298 100644
--- a/lib/twitterapi.php
+++ b/lib/twitterapi.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
@@ -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;
diff --git a/lib/util.php b/lib/util.php
index b3a94a5a0..f6d50b180 100644
--- a/lib/util.php
+++ b/lib/util.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
@@ -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
index 000000000..f3ca6e94a
--- /dev/null
+++ b/lib/webcolor.php
@@ -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
diff --git a/lib/xmppqueuehandler.php b/lib/xmppqueuehandler.php
index 91015fd45..986e09c25 100644
--- a/lib/xmppqueuehandler.php
+++ b/lib/xmppqueuehandler.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
@@ -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
index 000000000..d6768c278
--- /dev/null
+++ b/scripts/allsites.php
@@ -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
index 000000000..4a7757fb9
--- /dev/null
+++ b/scripts/commandline.inc
@@ -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
diff --git a/scripts/decache.php b/scripts/decache.php
index b18eaa2cd..90e1ec63c 100644
--- a/scripts/decache.php
+++ b/scripts/decache.php
@@ -18,35 +18,26 @@
* 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
index 000000000..32187382c
--- /dev/null
+++ b/scripts/delete_status_network.sh
@@ -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
diff --git a/scripts/enjitqueuehandler.php b/scripts/enjitqueuehandler.php
index 40f60da5d..05e1d9366 100755
--- a/scripts/enjitqueuehandler.php
+++ b/scripts/enjitqueuehandler.php
@@ -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
@@ -18,24 +18,27 @@
* 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);
diff --git a/scripts/facebookqueuehandler.php b/scripts/facebookqueuehandler.php
index c6859cb21..05a35577f 100755
--- a/scripts/facebookqueuehandler.php
+++ b/scripts/facebookqueuehandler.php
@@ -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
@@ -18,29 +18,30 @@
* 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
index 000000000..2cfa422e6
--- /dev/null
+++ b/scripts/fixup_conversations.php
@@ -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";
+}
diff --git a/scripts/fixup_hashtags.php b/scripts/fixup_hashtags.php
index 6f65c78a1..bd38e3105 100755
--- a/scripts/fixup_hashtags.php
+++ b/scripts/fixup_hashtags.php
@@ -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
diff --git a/scripts/fixup_inboxes.php b/scripts/fixup_inboxes.php
index a5c8a0a5a..3e55edef1 100755
--- a/scripts/fixup_inboxes.php
+++ b/scripts/fixup_inboxes.php
@@ -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
diff --git a/scripts/fixup_notices_rendered.php b/scripts/fixup_notices_rendered.php
index c27185546..3e7eb7acb 100755
--- a/scripts/fixup_notices_rendered.php
+++ b/scripts/fixup_notices_rendered.php
@@ -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
diff --git a/scripts/fixup_replies.php b/scripts/fixup_replies.php
index 6010e21d1..9d8cfda08 100755
--- a/scripts/fixup_replies.php
+++ b/scripts/fixup_replies.php
@@ -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
diff --git a/scripts/fixup_utf8.php b/scripts/fixup_utf8.php
index 169376091..8c9a9127f 100644
--- a/scripts/fixup_utf8.php
+++ b/scripts/fixup_utf8.php
@@ -19,21 +19,18 @@
*/
# 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,
diff --git a/scripts/getpiddir.php b/scripts/getpiddir.php
index 4f5704249..9927cc6d9 100755
--- a/scripts/getpiddir.php
+++ b/scripts/getpiddir.php
@@ -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
@@ -18,15 +18,13 @@
* 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');
diff --git a/scripts/getvaliddaemons.php b/scripts/getvaliddaemons.php
index 4e49f9bd4..97c230784 100755
--- a/scripts/getvaliddaemons.php
+++ b/scripts/getvaliddaemons.php
@@ -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
@@ -25,24 +25,19 @@
* 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 ";
}
diff --git a/scripts/inbox_users.php b/scripts/inbox_users.php
index 7d14f0efe..4883fea20 100755
--- a/scripts/inbox_users.php
+++ b/scripts/inbox_users.php
@@ -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
@@ -20,59 +20,55 @@
# 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'));
diff --git a/scripts/jabberqueuehandler.php b/scripts/jabberqueuehandler.php
index 8b6e974c0..5b581629d 100755
--- a/scripts/jabberqueuehandler.php
+++ b/scripts/jabberqueuehandler.php
@@ -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
@@ -18,24 +18,26 @@
* 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();
diff --git a/scripts/maildaemon.php b/scripts/maildaemon.php
index b9facec1a..cfb11a36f 100755
--- a/scripts/maildaemon.php
+++ b/scripts/maildaemon.php
@@ -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
@@ -18,16 +18,16 @@
* 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)
diff --git a/scripts/ombqueuehandler.php b/scripts/ombqueuehandler.php
index cdcea51dc..1587192b6 100755
--- a/scripts/ombqueuehandler.php
+++ b/scripts/ombqueuehandler.php
@@ -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
@@ -18,29 +18,33 @@
* 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);
diff --git a/scripts/pingqueuehandler.php b/scripts/pingqueuehandler.php
index ada6ecdba..23678ea4b 100644
--- a/scripts/pingqueuehandler.php
+++ b/scripts/pingqueuehandler.php
@@ -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
@@ -18,20 +18,22 @@
* 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);
diff --git a/scripts/publicqueuehandler.php b/scripts/publicqueuehandler.php
index b0fa22d43..701d50e01 100755
--- a/scripts/publicqueuehandler.php
+++ b/scripts/publicqueuehandler.php
@@ -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
@@ -18,29 +18,31 @@
* 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();
diff --git a/scripts/reportsnapshot.php b/scripts/reportsnapshot.php
index e332d856c..c644b557f 100644
--- a/scripts/reportsnapshot.php
+++ b/scripts/reportsnapshot.php
@@ -18,20 +18,13 @@
* 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();
diff --git a/scripts/setpassword.php b/scripts/setpassword.php
index d694eed09..b70689f03 100755
--- a/scripts/setpassword.php
+++ b/scripts/setpassword.php
@@ -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
@@ -18,30 +18,23 @@
* 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
index 000000000..8d03b06f5
--- /dev/null
+++ b/scripts/setup.cfg.sample
@@ -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
index 000000000..17440640e
--- /dev/null
+++ b/scripts/setup_status_network.sh
@@ -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
diff --git a/scripts/sitemap.php b/scripts/sitemap.php
index 39eb859bb..88ca2ba7a 100755
--- a/scripts/sitemap.php
+++ b/scripts/sitemap.php
@@ -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)) {
diff --git a/scripts/smsqueuehandler.php b/scripts/smsqueuehandler.php
index 38f2f11fe..94b846d98 100755
--- a/scripts/smsqueuehandler.php
+++ b/scripts/smsqueuehandler.php
@@ -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
@@ -18,24 +18,25 @@
* 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);
diff --git a/scripts/sphinx-cron.sh b/scripts/sphinx-cron.sh
index 9759adbd0..c16af3c4b 100755
--- a/scripts/sphinx-cron.sh
+++ b/scripts/sphinx-cron.sh
@@ -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
diff --git a/scripts/sphinx-indexer.sh b/scripts/sphinx-indexer.sh
index 3311b2ed1..fe7c16bea 100755
--- a/scripts/sphinx-indexer.sh
+++ b/scripts/sphinx-indexer.sh
@@ -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
diff --git a/scripts/startdaemons.sh b/scripts/startdaemons.sh
index 3869e95c4..9ead20acd 100755
--- a/scripts/startdaemons.sh
+++ b/scripts/startdaemons.sh
@@ -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
@@ -20,12 +20,27 @@
# 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
diff --git a/scripts/stopdaemons.sh b/scripts/stopdaemons.sh
index 2134b4ab0..60ffd83ad 100755
--- a/scripts/stopdaemons.sh
+++ b/scripts/stopdaemons.sh
@@ -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
diff --git a/scripts/synctwitterfriends.php b/scripts/synctwitterfriends.php
index bd08ba58d..fe53ff44d 100755
--- a/scripts/synctwitterfriends.php
+++ b/scripts/synctwitterfriends.php
@@ -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
@@ -18,19 +18,16 @@
* 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();
diff --git a/scripts/triminboxes.php b/scripts/triminboxes.php
index 0d2eaeaf0..b2135d682 100644
--- a/scripts/triminboxes.php
+++ b/scripts/triminboxes.php
@@ -18,26 +18,37 @@
* 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";
}
diff --git a/scripts/twitterqueuehandler.php b/scripts/twitterqueuehandler.php
index 7da4f1e20..00e735d98 100755
--- a/scripts/twitterqueuehandler.php
+++ b/scripts/twitterqueuehandler.php
@@ -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
@@ -18,29 +18,30 @@
* 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);
diff --git a/scripts/twitterstatusfetcher.php b/scripts/twitterstatusfetcher.php
index 9287b6d73..5ffdda58f 100755
--- a/scripts/twitterstatusfetcher.php
+++ b/scripts/twitterstatusfetcher.php
@@ -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
@@ -18,43 +18,68 @@
* 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();
diff --git a/scripts/uncache_users.php b/scripts/uncache_users.php
index fa0fb64cd..b0b576eb4 100644
--- a/scripts/uncache_users.php
+++ b/scripts/uncache_users.php
@@ -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
@@ -17,32 +17,27 @@
* 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'));
}
diff --git a/scripts/xmppconfirmhandler.php b/scripts/xmppconfirmhandler.php
index 7f39235fe..d6821ddef 100755
--- a/scripts/xmppconfirmhandler.php
+++ b/scripts/xmppconfirmhandler.php
@@ -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
@@ -18,33 +18,33 @@
* 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();
diff --git a/scripts/xmppdaemon.php b/scripts/xmppdaemon.php
index b79fa1b3b..3eecfec29 100755
--- a/scripts/xmppdaemon.php
+++ b/scripts/xmppdaemon.php
@@ -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
@@ -18,20 +18,23 @@
* 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();
diff --git a/sphinx.conf.sample b/sphinx.conf.sample
index b79adf15c..8204b9db6 100644
--- a/sphinx.conf.sample
+++ b/sphinx.conf.sample
@@ -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
}
diff --git a/theme/base/css/display.css b/theme/base/css/display.css
index dc275e19f..78fcd7ece 100644
--- a/theme/base/css/display.css
+++ b/theme/base/css/display.css
@@ -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;
diff --git a/theme/base/css/ie.css b/theme/base/css/ie.css
index 8183fee67..43fb01492 100644
--- a/theme/base/css/ie.css
+++ b/theme/base/css/ie.css
@@ -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%;
}
diff --git a/theme/base/css/ie6.css b/theme/base/css/ie6.css
index 76a82c004..dde4d6fc7 100644
--- a/theme/base/css/ie6.css
+++ b/theme/base/css/ie6.css
@@ -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
index 000000000..10fa431ce
--- /dev/null
+++ b/theme/base/images/icons/twotone/green/admin.gif
Binary files differ
diff --git a/theme/default/css/display.css b/theme/default/css/display.css
index 34f6b3b8a..89197bddb 100644
--- a/theme/default/css/display.css
+++ b/theme/default/css/display.css
@@ -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);
diff --git a/theme/default/css/ie.css b/theme/default/css/ie.css
index 2b06768ea..cbbd49ce6 100644
--- a/theme/default/css/ie.css
+++ b/theme/default/css/ie.css
@@ -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);
}
diff --git a/theme/identica/css/display.css b/theme/identica/css/display.css
index 8a03a4d77..025debf34 100644
--- a/theme/identica/css/display.css
+++ b/theme/identica/css/display.css
@@ -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 {
diff --git a/theme/identica/css/ie.css b/theme/identica/css/ie.css
index 2f463bb44..97cabc30a 100644
--- a/theme/identica/css/ie.css
+++ b/theme/identica/css/ie.css
@@ -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);
}
diff --git a/theme/pigeonthoughts/css/base.css b/theme/pigeonthoughts/css/base.css
index 08427d3c8..9866e2d2c 100644
--- a/theme/pigeonthoughts/css/base.css
+++ b/theme/pigeonthoughts/css/base.css
@@ -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;
}
diff --git a/theme/pigeonthoughts/css/display.css b/theme/pigeonthoughts/css/display.css
index af31cf78d..01af500bf 100644
--- a/theme/pigeonthoughts/css/display.css
+++ b/theme/pigeonthoughts/css/display.css
@@ -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);