summaryrefslogtreecommitdiff
path: root/_darcs/pristine/classes
diff options
context:
space:
mode:
authorEvan Prodromou <evan@prodromou.name>2008-05-06 11:17:29 -0400
committerEvan Prodromou <evan@prodromou.name>2008-05-06 11:17:29 -0400
commit1d4f1f6bf6bd8313cbb51dbf61d675408171d1b8 (patch)
tree8c622e1c4c1cbfc78abe335c1a153e354f29eee4 /_darcs/pristine/classes
parentd4fd1c505e14bdef4945e51d4f46a949d3abfb98 (diff)
add standard directories
Added some of the standard directories darcs-hash:20080506151729-84dde-563da8505e06a7302041c93ab157ced31165876c.gz
Diffstat (limited to '_darcs/pristine/classes')
-rw-r--r--_darcs/pristine/classes/Avatar.php95
-rw-r--r--_darcs/pristine/classes/Channel.php200
-rw-r--r--_darcs/pristine/classes/Command.php376
-rw-r--r--_darcs/pristine/classes/CommandInterpreter.php195
-rw-r--r--_darcs/pristine/classes/Confirm_address.php29
-rw-r--r--_darcs/pristine/classes/Consumer.php23
-rw-r--r--_darcs/pristine/classes/Fave.php37
-rw-r--r--_darcs/pristine/classes/Foreign_link.php76
-rw-r--r--_darcs/pristine/classes/Foreign_service.php24
-rw-r--r--_darcs/pristine/classes/Foreign_subscription.php23
-rw-r--r--_darcs/pristine/classes/Foreign_user.php70
-rw-r--r--_darcs/pristine/classes/Invitation.php24
-rw-r--r--_darcs/pristine/classes/Memcached_DataObject.php194
-rw-r--r--_darcs/pristine/classes/Message.php68
-rw-r--r--_darcs/pristine/classes/Nonce.php25
-rw-r--r--_darcs/pristine/classes/Notice.php539
-rw-r--r--_darcs/pristine/classes/NoticeWrapper.php59
-rw-r--r--_darcs/pristine/classes/Notice_inbox.php40
-rw-r--r--_darcs/pristine/classes/Notice_source.php24
-rw-r--r--_darcs/pristine/classes/Notice_tag.php55
-rw-r--r--_darcs/pristine/classes/Profile.php159
-rw-r--r--_darcs/pristine/classes/Profile_block.php49
-rw-r--r--_darcs/pristine/classes/Profile_tag.php101
-rw-r--r--_darcs/pristine/classes/Queue_item.php55
-rw-r--r--_darcs/pristine/classes/Remember_me.php24
-rw-r--r--_darcs/pristine/classes/Remote_profile.php45
-rw-r--r--_darcs/pristine/classes/Reply.php23
-rw-r--r--_darcs/pristine/classes/Sms_carrier.php28
-rw-r--r--_darcs/pristine/classes/Subscription.php51
-rw-r--r--_darcs/pristine/classes/Token.php26
-rw-r--r--_darcs/pristine/classes/User.php473
-rw-r--r--_darcs/pristine/classes/User_openid.php24
-rw-r--r--_darcs/pristine/classes/laconica.ini344
-rw-r--r--_darcs/pristine/classes/laconica.links.ini43
34 files changed, 3621 insertions, 0 deletions
diff --git a/_darcs/pristine/classes/Avatar.php b/_darcs/pristine/classes/Avatar.php
new file mode 100644
index 000000000..901c47c51
--- /dev/null
+++ b/_darcs/pristine/classes/Avatar.php
@@ -0,0 +1,95 @@
+<?php
+/**
+ * Table Definition for avatar
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Avatar extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'avatar'; // table name
+ public $profile_id; // int(4) primary_key not_null
+ public $original; // tinyint(1)
+ public $width; // int(4) primary_key not_null
+ public $height; // int(4) primary_key not_null
+ public $mediatype; // varchar(32) not_null
+ public $filename; // varchar(255)
+ public $url; // varchar(255) unique_key
+ public $created; // datetime() not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Avatar',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ # We clean up the file, too
+
+ function delete() {
+ $filename = $this->filename;
+ if (parent::delete()) {
+ @unlink(common_avatar_path($filename));
+ }
+ }
+
+ # Create and save scaled version of this avatar
+ # XXX: maybe break into different methods
+
+ function scale($size) {
+
+ $image_s = imagecreatetruecolor($size, $size);
+ $image_a = $this->to_image();
+ $square = min($this->width, $this->height);
+ imagecolortransparent($image_s, imagecolorallocate($image_s, 0, 0, 0));
+ imagealphablending($image_s, false);
+ imagesavealpha($image_s, true);
+ imagecopyresampled($image_s, $image_a, 0, 0, 0, 0,
+ $size, $size, $square, $square);
+
+ $ext = ($this->mediattype == 'image/jpeg') ? ".jpeg" : ".png";
+
+ $filename = common_avatar_filename($this->profile_id, $ext, $size, common_timestamp());
+
+ if ($this->mediatype == 'image/jpeg') {
+ imagejpeg($image_s, common_avatar_path($filename));
+ } else {
+ imagepng($image_s, common_avatar_path($filename));
+ }
+
+ $scaled = DB_DataObject::factory('avatar');
+ $scaled->profile_id = $this->profile_id;
+ $scaled->width = $size;
+ $scaled->height = $size;
+ $scaled->original = false;
+ $scaled->mediatype = ($this->mediattype == 'image/jpeg') ? 'image/jpeg' : 'image/png';
+ $scaled->filename = $filename;
+ $scaled->url = common_avatar_url($filename);
+ $scaled->created = DB_DataObject_Cast::dateTime(); # current time
+
+ if ($scaled->insert()) {
+ return $scaled;
+ } else {
+ return NULL;
+ }
+ }
+
+ function to_image() {
+ $filepath = common_avatar_path($this->filename);
+ if ($this->mediatype == 'image/gif') {
+ return imagecreatefromgif($filepath);
+ } else if ($this->mediatype == 'image/jpeg') {
+ return imagecreatefromjpeg($filepath);
+ } else if ($this->mediatype == 'image/png') {
+ return imagecreatefrompng($filepath);
+ } else {
+ return NULL;
+ }
+ }
+
+ function &pkeyGet($kv) {
+ return Memcached_DataObject::pkeyGet('Avatar', $kv);
+ }
+}
diff --git a/_darcs/pristine/classes/Channel.php b/_darcs/pristine/classes/Channel.php
new file mode 100644
index 000000000..bcc0c36b5
--- /dev/null
+++ b/_darcs/pristine/classes/Channel.php
@@ -0,0 +1,200 @@
+<?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); }
+
+class Channel {
+
+ function on($user) {
+ return false;
+ }
+
+ function off($user) {
+ return false;
+ }
+
+ function output($user, $text) {
+ return false;
+ }
+
+ function error($user, $text) {
+ return false;
+ }
+
+ function source() {
+ return NULL;
+ }
+}
+
+class XMPPChannel extends Channel {
+
+ var $conn = NULL;
+
+ function source() {
+ return 'xmpp';
+ }
+
+ function __construct($conn) {
+ $this->conn = $conn;
+ }
+
+ function on($user) {
+ return $this->set_notify($user, 1);
+ }
+
+ function off($user) {
+ return $this->set_notify($user, 0);
+ }
+
+ function output($user, $text) {
+ $text = '['.common_config('site', 'name') . '] ' . $text;
+ jabber_send_message($user->jabber, $text);
+ }
+
+ function error($user, $text) {
+ $text = '['.common_config('site', 'name') . '] ' . $text;
+ jabber_send_message($user->jabber, $text);
+ }
+
+ function set_notify(&$user, $notify) {
+ $orig = clone($user);
+ $user->jabbernotify = $notify;
+ $result = $user->update($orig);
+ if (!$result) {
+ $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
+ common_log(LOG_ERR,
+ 'Could not set notify flag to ' . $notify .
+ ' for user ' . common_log_objstring($user) .
+ ': ' . $last_error->message);
+ return false;
+ } else {
+ common_log(LOG_INFO,
+ 'User ' . $user->nickname . ' set notify flag to ' . $notify);
+ return true;
+ }
+ }
+}
+
+
+class WebChannel extends Channel {
+
+ function source() {
+ return 'web';
+ }
+
+ function on($user) {
+ return false;
+ }
+
+ function off($user) {
+ return false;
+ }
+
+ function output($user, $text) {
+ # XXX: buffer all output and send it at the end
+ # XXX: even better, redirect to appropriate page
+ # depending on what command was run
+ common_show_header(_('Command results'));
+ common_element('p', NULL, $text);
+ common_show_footer();
+ }
+
+ function error($user, $text) {
+ common_user_error($text);
+ }
+}
+
+
+class AjaxWebChannel extends WebChannel {
+
+ function output($user, $text) {
+ common_start_html('text/xml;charset=utf-8', true);
+ common_element_start('head');
+ common_element('title', null, _('Command results'));
+ common_element_end('head');
+ common_element_start('body');
+ common_element('p', array('id' => 'command_result'), $text);
+ common_element_end('body');
+ common_element_end('html');
+ }
+
+ function error($user, $text) {
+ common_start_html('text/xml;charset=utf-8', true);
+ common_element_start('head');
+ common_element('title', null, _('Ajax Error'));
+ common_element_end('head');
+ common_element_start('body');
+ common_element('p', array('id' => 'error'), $text);
+ common_element_end('body');
+ common_element_end('html');
+ }
+}
+
+
+class MailChannel extends Channel {
+
+ var $addr = NULL;
+
+ function source() {
+ return 'mail';
+ }
+
+ function __construct($addr=NULL) {
+ $this->addr = $addr;
+ }
+
+ function on($user) {
+ return $this->set_notify($user, 1);
+ }
+
+ function off($user) {
+ return $this->set_notify($user, 0);
+ }
+
+ function output($user, $text) {
+
+ $headers['From'] = $user->incomingemail;
+ $headers['To'] = $this->addr;
+
+ $headers['Subject'] = _('Command complete');
+
+ return mail_send(array($this->addr), $headers, $text);
+ }
+
+ function error($user, $text) {
+
+ $headers['From'] = $user->incomingemail;
+ $headers['To'] = $this->addr;
+
+ $headers['Subject'] = _('Command failed');
+
+ return mail_send(array($this->addr), $headers, $text);
+ }
+
+ function set_notify($user, $value) {
+ $orig = clone($user);
+ $user->smsnotify = $value;
+ $result = $user->update($orig);
+ if (!$result) {
+ common_log_db_error($user, 'UPDATE', __FILE__);
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/_darcs/pristine/classes/Command.php b/_darcs/pristine/classes/Command.php
new file mode 100644
index 000000000..c2409d140
--- /dev/null
+++ b/_darcs/pristine/classes/Command.php
@@ -0,0 +1,376 @@
+<?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.'/classes/Channel.php');
+
+class Command {
+
+ var $user = NULL;
+
+ function __construct($user=NULL) {
+ $this->user = $user;
+ }
+
+ function execute($channel) {
+ return false;
+ }
+}
+
+class UnimplementedCommand extends Command {
+ function execute($channel) {
+ $channel->error($this->user, _("Sorry, this command is not yet implemented."));
+ }
+}
+
+class TrackingCommand extends UnimplementedCommand {
+}
+
+class TrackOffCommand extends UnimplementedCommand {
+}
+
+class TrackCommand extends UnimplementedCommand {
+ var $word = NULL;
+ function __construct($user, $word) {
+ parent::__construct($user);
+ $this->word = $word;
+ }
+}
+
+class UntrackCommand extends UnimplementedCommand {
+ var $word = NULL;
+ function __construct($user, $word) {
+ parent::__construct($user);
+ $this->word = $word;
+ }
+}
+
+class NudgeCommand extends UnimplementedCommand {
+ var $other = NULL;
+ function __construct($user, $other) {
+ parent::__construct($user);
+ $this->other = $other;
+ }
+}
+
+class InviteCommand extends UnimplementedCommand {
+ var $other = NULL;
+ function __construct($user, $other) {
+ parent::__construct($user);
+ $this->other = $other;
+ }
+}
+
+class StatsCommand extends Command {
+ function execute($channel) {
+
+ $subs = new Subscription();
+ $subs->subscriber = $this->user->id;
+ $subs_count = (int) $subs->count() - 1;
+
+ $subbed = new Subscription();
+ $subbed->subscribed = $this->user->id;
+ $subbed_count = (int) $subbed->count() - 1;
+
+ $notices = new Notice();
+ $notices->profile_id = $this->user->id;
+ $notice_count = (int) $notices->count();
+
+ $channel->output($this->user, sprintf(_("Subscriptions: %1\$s\n".
+ "Subscribers: %2\$s\n".
+ "Notices: %3\$s"),
+ $subs_count,
+ $subbed_count,
+ $notice_count));
+ }
+}
+
+class FavCommand extends Command {
+
+ var $other = NULL;
+
+ function __construct($user, $other) {
+ parent::__construct($user);
+ $this->other = $other;
+ }
+
+ function execute($channel) {
+
+ $recipient =
+ common_relative_profile($this->user, common_canonical_nickname($this->other));
+
+ if (!$recipient) {
+ $channel->error($this->user, _('No such user.'));
+ return;
+ }
+ $notice = $recipient->getCurrentNotice();
+ if (!$notice) {
+ $channel->error($this->user, _('User has no last notice'));
+ return;
+ }
+
+ $fave = Fave::addNew($this->user, $notice);
+
+ if (!$fave) {
+ $channel->error($this->user, _('Could not create favorite.'));
+ return;
+ }
+
+ $other = User::staticGet('id', $recipient->id);
+
+ if ($other && $other->id != $user->id) {
+ if ($other->email && $other->emailnotifyfav) {
+ mail_notify_fave($other, $this->user, $notice);
+ }
+ }
+
+ $this->user->blowFavesCache();
+
+ $channel->output($this->user, _('Notice marked as fave.'));
+ }
+}
+
+class WhoisCommand extends Command {
+ var $other = NULL;
+ function __construct($user, $other) {
+ parent::__construct($user);
+ $this->other = $other;
+ }
+
+ function execute($channel) {
+ $recipient =
+ common_relative_profile($this->user, common_canonical_nickname($this->other));
+
+ if (!$recipient) {
+ $channel->error($this->user, _('No such user.'));
+ return;
+ }
+
+ $whois = sprintf(_("%1\$s (%2\$s)"), $recipient->nickname,
+ $recipient->profileurl);
+ if ($recipient->fullname) {
+ $whois .= "\n" . sprintf(_('Fullname: %s'), $recipient->fullname);
+ }
+ if ($recipient->location) {
+ $whois .= "\n" . sprintf(_('Location: %s'), $recipient->location);
+ }
+ if ($recipient->homepage) {
+ $whois .= "\n" . sprintf(_('Homepage: %s'), $recipient->homepage);
+ }
+ if ($recipient->bio) {
+ $whois .= "\n" . sprintf(_('About: %s'), $recipient->bio);
+ }
+ $channel->output($this->user, $whois);
+ }
+}
+
+class MessageCommand extends Command {
+ var $other = NULL;
+ var $text = NULL;
+ function __construct($user, $other, $text) {
+ parent::__construct($user);
+ $this->other = $other;
+ $this->text = $text;
+ }
+
+ function execute($channel) {
+ $other = User::staticGet('nickname', common_canonical_nickname($this->other));
+ $len = mb_strlen($this->text);
+ if ($len == 0) {
+ $channel->error($this->user, _('No content!'));
+ return;
+ } else if ($len > 140) {
+ $content = common_shorten_links($content);
+ if (mb_strlen($content) > 140) {
+ $channel->error($this->user, sprintf(_('Message too long - maximum is 140 characters, you sent %d'), $len));
+ return;
+ }
+ }
+
+ if (!$other) {
+ $channel->error($this->user, _('No such user.'));
+ return;
+ } else if (!$this->user->mutuallySubscribed($other)) {
+ $channel->error($this->user, _('You can\'t send a message to this user.'));
+ return;
+ } else if ($this->user->id == $other->id) {
+ $channel->error($this->user, _('Don\'t send a message to yourself; just say it to yourself quietly instead.'));
+ return;
+ }
+ $message = Message::saveNew($this->user->id, $other->id, $this->text, $channel->source());
+ if ($message) {
+ $channel->output($this->user, sprintf(_('Direct message to %s sent'), $this->other));
+ } else {
+ $channel->error($this->user, _('Error sending direct message.'));
+ }
+ }
+}
+
+class GetCommand extends Command {
+
+ var $other = NULL;
+
+ function __construct($user, $other) {
+ parent::__construct($user);
+ $this->other = $other;
+ }
+
+ function execute($channel) {
+ $target_nickname = common_canonical_nickname($this->other);
+
+ $target =
+ common_relative_profile($this->user, $target_nickname);
+
+ if (!$target) {
+ $channel->error($this->user, _('No such user.'));
+ return;
+ }
+ $notice = $target->getCurrentNotice();
+ if (!$notice) {
+ $channel->error($this->user, _('User has no last notice'));
+ return;
+ }
+ $notice_content = $notice->content;
+
+ $channel->output($this->user, $target_nickname . ": " . $notice_content);
+ }
+}
+
+class SubCommand extends Command {
+
+ var $other = NULL;
+
+ function __construct($user, $other) {
+ parent::__construct($user);
+ $this->other = $other;
+ }
+
+ function execute($channel) {
+
+ if (!$this->other) {
+ $channel->error($this->user, _('Specify the name of the user to subscribe to'));
+ return;
+ }
+
+ $result = subs_subscribe_user($this->user, $this->other);
+
+ if ($result == 'true') {
+ $channel->output($this->user, sprintf(_('Subscribed to %s'), $this->other));
+ } else {
+ $channel->error($this->user, $result);
+ }
+ }
+}
+
+class UnsubCommand extends Command {
+
+ var $other = NULL;
+
+ function __construct($user, $other) {
+ parent::__construct($user);
+ $this->other = $other;
+ }
+
+ function execute($channel) {
+ if(!$this->other) {
+ $channel->error($this->user, _('Specify the name of the user to unsubscribe from'));
+ return;
+ }
+
+ $result=subs_unsubscribe_user($this->user, $this->other);
+
+ if ($result) {
+ $channel->output($this->user, sprintf(_('Unsubscribed from %s'), $this->other));
+ } else {
+ $channel->error($this->user, $result);
+ }
+ }
+}
+
+class OffCommand extends Command {
+ var $other = NULL;
+ function __construct($user, $other=NULL) {
+ parent::__construct($user);
+ $this->other = $other;
+ }
+ function execute($channel) {
+ if ($other) {
+ $channel->error($this->user, _("Command not yet implemented."));
+ } else {
+ if ($channel->off($this->user)) {
+ $channel->output($this->user, _('Notification off.'));
+ } else {
+ $channel->error($this->user, _('Can\'t turn off notification.'));
+ }
+ }
+ }
+}
+
+class OnCommand extends Command {
+ var $other = NULL;
+ function __construct($user, $other=NULL) {
+ parent::__construct($user);
+ $this->other = $other;
+ }
+
+ function execute($channel) {
+ if ($other) {
+ $channel->error($this->user, _("Command not yet implemented."));
+ } else {
+ if ($channel->on($this->user)) {
+ $channel->output($this->user, _('Notification on.'));
+ } else {
+ $channel->error($this->user, _('Can\'t turn on notification.'));
+ }
+ }
+ }
+}
+
+class HelpCommand extends Command {
+ function execute($channel) {
+ $channel->output($this->user,
+ _("Commands:\n".
+ "on - turn on notifications\n".
+ "off - turn off notifications\n".
+ "help - show this help\n".
+ "follow <nickname> - subscribe to user\n".
+ "leave <nickname> - unsubscribe from user\n".
+ "d <nickname> <text> - direct message to user\n".
+ "get <nickname> - get last notice from user\n".
+ "whois <nickname> - get profile info on user\n".
+ "fav <nickname> - add user's last notice as a 'fave'\n".
+ "stats - get your stats\n".
+ "stop - same as 'off'\n".
+ "quit - same as 'off'\n".
+ "sub <nickname> - same as 'follow'\n".
+ "unsub <nickname> - same as 'leave'\n".
+ "last <nickname> - same as 'get'\n".
+ "on <nickname> - not yet implemented.\n".
+ "off <nickname> - not yet implemented.\n".
+ "nudge <nickname> - not yet implemented.\n".
+ "invite <phone number> - not yet implemented.\n".
+ "track <word> - not yet implemented.\n".
+ "untrack <word> - not yet implemented.\n".
+ "track off - not yet implemented.\n".
+ "untrack all - not yet implemented.\n".
+ "tracks - not yet implemented.\n".
+ "tracking - not yet implemented.\n"));
+ }
+}
diff --git a/_darcs/pristine/classes/CommandInterpreter.php b/_darcs/pristine/classes/CommandInterpreter.php
new file mode 100644
index 000000000..4e27f8f79
--- /dev/null
+++ b/_darcs/pristine/classes/CommandInterpreter.php
@@ -0,0 +1,195 @@
+<?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.'/classes/Command.php');
+
+class CommandInterpreter {
+
+ function handle_command($user, $text) {
+ # XXX: localise
+
+ $text = preg_replace('/\s+/', ' ', trim($text));
+ list($cmd, $arg) = explode(' ', $text, 2);
+
+ # We try to support all the same commands as Twitter, see
+ # http://getsatisfaction.com/twitter/topics/what_are_the_twitter_commands
+ # There are a few compatibility commands from earlier versions of
+ # Laconica
+
+ switch(strtolower($cmd)) {
+ case 'help':
+ if ($arg) {
+ return NULL;
+ }
+ return new HelpCommand($user);
+ case 'on':
+ if ($arg) {
+ list($other, $extra) = explode(' ', $arg, 2);
+ if ($extra) {
+ return NULL;
+ } else {
+ return new OnCommand($user, $other);
+ }
+ } else {
+ return new OnCommand($user);
+ }
+ case 'off':
+ if ($arg) {
+ list($other, $extra) = explode(' ', $arg, 2);
+ if ($extra) {
+ return NULL;
+ } else {
+ return new OffCommand($user, $other);
+ }
+ } else {
+ return new OffCommand($user);
+ }
+ case 'stop':
+ case 'quit':
+ if ($arg) {
+ return NULL;
+ } else {
+ return new OffCommand($user);
+ }
+ case 'follow':
+ case 'sub':
+ if (!$arg) {
+ return NULL;
+ }
+ list($other, $extra) = explode(' ', $arg, 2);
+ if ($extra) {
+ return NULL;
+ } else {
+ return new SubCommand($user, $other);
+ }
+ case 'leave':
+ case 'unsub':
+ if (!$arg) {
+ return NULL;
+ }
+ list($other, $extra) = explode(' ', $arg, 2);
+ if ($extra) {
+ return NULL;
+ } else {
+ return new UnsubCommand($user, $other);
+ }
+ case 'get':
+ case 'last':
+ if (!$arg) {
+ return NULL;
+ }
+ list($other, $extra) = explode(' ', $arg, 2);
+ if ($extra) {
+ return NULL;
+ } else {
+ return new GetCommand($user, $other);
+ }
+ case 'd':
+ if (!$arg) {
+ return NULL;
+ }
+ list($other, $extra) = explode(' ', $arg, 2);
+ if (!$extra) {
+ return NULL;
+ } else {
+ return new MessageCommand($user, $other, $extra);
+ }
+ case 'whois':
+ if (!$arg) {
+ return NULL;
+ }
+ list($other, $extra) = explode(' ', $arg, 2);
+ if ($extra) {
+ return NULL;
+ } else {
+ return new WhoisCommand($user, $other);
+ }
+ case 'fav':
+ if (!$arg) {
+ return NULL;
+ }
+ list($other, $extra) = explode(' ', $arg, 2);
+ if ($extra) {
+ return NULL;
+ } else {
+ return new FavCommand($user, $other);
+ }
+ case 'nudge':
+ if (!$arg) {
+ return NULL;
+ }
+ list($other, $extra) = explode(' ', $arg, 2);
+ if ($extra) {
+ return NULL;
+ } else {
+ return new NudgeCommand($user, $other);
+ }
+ case 'stats':
+ if ($arg) {
+ return NULL;
+ }
+ return new StatsCommand($user);
+ case 'invite':
+ if (!$arg) {
+ return NULL;
+ }
+ list($other, $extra) = explode(' ', $arg, 2);
+ if ($extra) {
+ return NULL;
+ } else {
+ return new InviteCommand($user, $other);
+ }
+ case 'track':
+ if (!$arg) {
+ return NULL;
+ }
+ list($word, $extra) = explode(' ', $arg, 2);
+ if ($extra) {
+ return NULL;
+ } else if ($word == 'off') {
+ return new TrackOffCommand($user);
+ } else {
+ return new TrackCommand($user, $word);
+ }
+ case 'untrack':
+ if (!$arg) {
+ return NULL;
+ }
+ list($word, $extra) = explode(' ', $arg, 2);
+ if ($extra) {
+ return NULL;
+ } else if ($word == 'all') {
+ return new TrackOffCommand($user);
+ } else {
+ return new UntrackCommand($user, $word);
+ }
+ case 'tracks':
+ case 'tracking':
+ if ($arg) {
+ return NULL;
+ }
+ return new TrackingCommand($user);
+ default:
+ return false;
+ }
+ }
+}
+
diff --git a/_darcs/pristine/classes/Confirm_address.php b/_darcs/pristine/classes/Confirm_address.php
new file mode 100644
index 000000000..10661ff5c
--- /dev/null
+++ b/_darcs/pristine/classes/Confirm_address.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Table Definition for confirm_address
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Confirm_address extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'confirm_address'; // table name
+ public $code; // varchar(32) primary_key not_null
+ public $user_id; // int(4) not_null
+ public $address; // varchar(255) not_null
+ public $address_extra; // varchar(255) not_null
+ public $address_type; // varchar(8) not_null
+ public $claimed; // datetime()
+ public $sent; // datetime()
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Confirm_address',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ function sequenceKey() { return array(false, false); }
+}
diff --git a/_darcs/pristine/classes/Consumer.php b/_darcs/pristine/classes/Consumer.php
new file mode 100644
index 000000000..d18e6feeb
--- /dev/null
+++ b/_darcs/pristine/classes/Consumer.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Table Definition for consumer
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Consumer extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'consumer'; // table name
+ public $consumer_key; // varchar(255) primary_key not_null
+ public $seed; // char(32) not_null
+ public $created; // datetime() not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Consumer',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+}
diff --git a/_darcs/pristine/classes/Fave.php b/_darcs/pristine/classes/Fave.php
new file mode 100644
index 000000000..7cc3f585e
--- /dev/null
+++ b/_darcs/pristine/classes/Fave.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Table Definition for fave
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Fave extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'fave'; // table name
+ public $notice_id; // int(4) primary_key not_null
+ public $user_id; // int(4) primary_key not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Fave',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ static function addNew($user, $notice) {
+ $fave = new Fave();
+ $fave->user_id = $user->id;
+ $fave->notice_id = $notice->id;
+ if (!$fave->insert()) {
+ common_log_db_error($fave, 'INSERT', __FILE__);
+ return false;
+ }
+ return $fave;
+ }
+
+ function &pkeyGet($kv) {
+ return Memcached_DataObject::pkeyGet('Fave', $kv);
+ }
+}
diff --git a/_darcs/pristine/classes/Foreign_link.php b/_darcs/pristine/classes/Foreign_link.php
new file mode 100644
index 000000000..7a625a209
--- /dev/null
+++ b/_darcs/pristine/classes/Foreign_link.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * Table Definition for foreign_link
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Foreign_link extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'foreign_link'; // table name
+ public $user_id; // int(4) primary_key not_null
+ public $foreign_id; // int(4) primary_key not_null
+ public $service; // int(4) primary_key not_null
+ public $credentials; // varchar(255)
+ public $noticesync; // tinyint(1) not_null default_1
+ public $friendsync; // tinyint(1) not_null default_2
+ public $profilesync; // tinyint(1) not_null 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('Foreign_link',$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 getByUserID($user_id, $service) {
+ $flink = new Foreign_link();
+ $flink->service = $service;
+ $flink->user_id = $user_id;
+ $flink->limit(1);
+
+ if ($flink->find(TRUE)) {
+ return $flink;
+ }
+
+ return NULL;
+ }
+
+ static function getByForeignID($foreign_id, $service) {
+ $flink = new Foreign_link();
+ $flink->service = $service;
+ $flink->foreign_id = $foreign_id;
+ $flink->limit(1);
+
+ if ($flink->find(TRUE)) {
+ return $flink;
+ }
+
+ return NULL;
+ }
+
+ # Convenience methods
+ function getForeignUser() {
+ $fuser = new Foreign_user();
+ $fuser->service = $this->service;
+ $fuser->id = $this->foreign_id;
+
+ $fuser->limit(1);
+
+ if ($fuser->find(TRUE)) {
+ return $fuser;
+ }
+
+ return NULL;
+ }
+
+ function getUser() {
+ return User::staticGet($this->user_id);
+ }
+
+}
diff --git a/_darcs/pristine/classes/Foreign_service.php b/_darcs/pristine/classes/Foreign_service.php
new file mode 100644
index 000000000..18ef83d69
--- /dev/null
+++ b/_darcs/pristine/classes/Foreign_service.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Table Definition for foreign_service
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Foreign_service extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'foreign_service'; // table name
+ public $id; // int(4) primary_key not_null
+ public $name; // varchar(32) unique_key not_null
+ public $description; // 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_service',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+}
diff --git a/_darcs/pristine/classes/Foreign_subscription.php b/_darcs/pristine/classes/Foreign_subscription.php
new file mode 100644
index 000000000..315064067
--- /dev/null
+++ b/_darcs/pristine/classes/Foreign_subscription.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Table Definition for foreign_subscription
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Foreign_subscription extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'foreign_subscription'; // table name
+ public $service; // int(4) primary_key not_null
+ public $subscriber; // int(4) primary_key not_null
+ public $subscribed; // int(4) primary_key not_null
+ public $created; // datetime() not_null
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Foreign_subscription',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+}
diff --git a/_darcs/pristine/classes/Foreign_user.php b/_darcs/pristine/classes/Foreign_user.php
new file mode 100644
index 000000000..027fae69d
--- /dev/null
+++ b/_darcs/pristine/classes/Foreign_user.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Table Definition for foreign_user
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+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 $service; // int(4) primary_key not_null
+ public $uri; // varchar(255) unique_key not_null
+ 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); }
+
+ /* 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) {
+ $fuser = new Foreign_user();
+ $fuser->whereAdd("service = $service");
+ $fuser->whereAdd("id = $id");
+ $fuser->limit(1);
+
+ if ($fuser->find()) {
+ $fuser->fetch();
+ return $fuser;
+ }
+
+ return NULL;
+ }
+
+ function updateKeys(&$orig) {
+ $parts = array();
+ foreach (array('id', 'service', 'uri', 'nickname') as $k) {
+ if (strcmp($this->$k, $orig->$k) != 0) {
+ $parts[] = $k . ' = ' . $this->_quote($this->$k);
+ }
+ }
+ if (count($parts) == 0) {
+ # No changes
+ return true;
+ }
+ $toupdate = implode(', ', $parts);
+
+ $table = $this->tableName();
+ if(common_config('db','quote_identifiers')) {
+ $table = '"' . $table . '"';
+ }
+ $qry = 'UPDATE ' . $table . ' SET ' . $toupdate .
+ ' WHERE id = ' . $this->id;
+ $orig->decache();
+ $result = $this->query($qry);
+ if ($result) {
+ $this->encache();
+ }
+ return $result;
+ }
+
+
+}
diff --git a/_darcs/pristine/classes/Invitation.php b/_darcs/pristine/classes/Invitation.php
new file mode 100644
index 000000000..1477391b0
--- /dev/null
+++ b/_darcs/pristine/classes/Invitation.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Table Definition for invitation
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Invitation extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'invitation'; // table name
+ public $code; // varchar(32) primary_key not_null
+ public $user_id; // int(4) not_null
+ public $address; // varchar(255) multiple_key not_null
+ public $address_type; // varchar(8) multiple_key not_null
+ public $created; // datetime() not_null
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Invitation',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+}
diff --git a/_darcs/pristine/classes/Memcached_DataObject.php b/_darcs/pristine/classes/Memcached_DataObject.php
new file mode 100644
index 000000000..7a33e158d
--- /dev/null
+++ b/_darcs/pristine/classes/Memcached_DataObject.php
@@ -0,0 +1,194 @@
+<?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.'/classes/Memcached_DataObject.php';
+
+class Memcached_DataObject extends DB_DataObject
+{
+ function &staticGet($cls, $k, $v=NULL) {
+ if (is_null($v)) {
+ $v = $k;
+ # XXX: HACK!
+ $i = new $cls;
+ $keys = $i->keys();
+ $k = $keys[0];
+ unset($i);
+ }
+ $i = Memcached_DataObject::getcached($cls, $k, $v);
+ if ($i) {
+ return $i;
+ } else {
+ $i = DB_DataObject::staticGet($cls, $k, $v);
+ if ($i) {
+ $i->encache();
+ }
+ return $i;
+ }
+ }
+
+ function &pkeyGet($cls, $kv) {
+ $i = Memcached_DataObject::multicache($cls, $kv);
+ if ($i) {
+ return $i;
+ } else {
+ $i = new $cls();
+ foreach ($kv as $k => $v) {
+ $i->$k = $v;
+ }
+ if ($i->find(true)) {
+ $i->encache();
+ } else {
+ $i = NULL;
+ }
+ return $i;
+ }
+ }
+
+ function insert() {
+ $result = parent::insert();
+ return $result;
+ }
+
+ function update($orig=NULL) {
+ if (is_object($orig) && $orig instanceof Memcached_DataObject) {
+ $orig->decache(); # might be different keys
+ }
+ $result = parent::update($orig);
+ if ($result) {
+ $this->encache();
+ }
+ return $result;
+ }
+
+ function delete() {
+ $this->decache(); # while we still have the values!
+ return parent::delete();
+ }
+
+ static function memcache() {
+ return common_memcache();
+ }
+
+ static function cacheKey($cls, $k, $v) {
+ return common_cache_key(strtolower($cls).':'.$k.':'.$v);
+ }
+
+ static function getcached($cls, $k, $v) {
+ $c = Memcached_DataObject::memcache();
+ if (!$c) {
+ return false;
+ } else {
+ return $c->get(Memcached_DataObject::cacheKey($cls, $k, $v));
+ }
+ }
+
+ function keyTypes() {
+ global $_DB_DATAOBJECT;
+ if (!isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) {
+ $this->databaseStructure();
+
+ }
+ return $_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"];
+ }
+
+ function encache() {
+ $c = $this->memcache();
+ if (!$c) {
+ return false;
+ } else {
+ $pkey = array();
+ $pval = array();
+ $types = $this->keyTypes();
+ ksort($types);
+ foreach ($types as $key => $type) {
+ if ($type == 'K') {
+ $pkey[] = $key;
+ $pval[] = $this->$key;
+ } else {
+ $c->set($this->cacheKey($this->tableName(), $key, $this->$key), $this);
+ }
+ }
+ # XXX: should work for both compound and scalar pkeys
+ $pvals = implode(',', $pval);
+ $pkeys = implode(',', $pkey);
+ $c->set($this->cacheKey($this->tableName(), $pkeys, $pvals), $this);
+ }
+ }
+
+ function decache() {
+ $c = $this->memcache();
+ if (!$c) {
+ return false;
+ } else {
+ $pkey = array();
+ $pval = array();
+ $types = $this->keyTypes();
+ ksort($types);
+ foreach ($types as $key => $type) {
+ if ($type == 'K') {
+ $pkey[] = $key;
+ $pval[] = $this->$key;
+ } else {
+ $c->delete($this->cacheKey($this->tableName(), $key, $this->$key));
+ }
+ }
+ # should work for both compound and scalar pkeys
+ # XXX: comma works for now but may not be safe separator for future keys
+ $pvals = implode(',', $pval);
+ $pkeys = implode(',', $pkey);
+ $c->delete($this->cacheKey($this->tableName(), $pkeys, $pvals));
+ }
+ }
+
+ function multicache($cls, $kv) {
+ ksort($kv);
+ $c = Memcached_DataObject::memcache();
+ if (!$c) {
+ return false;
+ } else {
+ $pkeys = implode(',', array_keys($kv));
+ $pvals = implode(',', array_values($kv));
+ return $c->get(Memcached_DataObject::cacheKey($cls, $pkeys, $pvals));
+ }
+ }
+
+ function getSearchEngine($table) {
+ require_once INSTALLDIR.'/lib/search_engines.php';
+ static $search_engine;
+ if (!isset($search_engine)) {
+ $connected = false;
+ if (common_config('sphinx', 'enabled')) {
+ $search_engine = new SphinxSearch($this, $table);
+ $connected = $search_engine->is_connected();
+ }
+
+ // unable to connect to sphinx' search daemon
+ if (!$connected) {
+ if ('mysql' === common_config('db', 'type')) {
+ $search_engine = new MySQLSearch($this, $table);
+ } else {
+ $search_engine = new PGSearch($this, $table);
+ }
+ }
+ }
+ return $search_engine;
+ }
+}
diff --git a/_darcs/pristine/classes/Message.php b/_darcs/pristine/classes/Message.php
new file mode 100644
index 000000000..ef4bd0316
--- /dev/null
+++ b/_darcs/pristine/classes/Message.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * Table Definition for message
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Message extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'message'; // table name
+ public $id; // int(4) primary_key not_null
+ public $uri; // varchar(255) unique_key
+ public $from_profile; // int(4) not_null
+ public $to_profile; // int(4) not_null
+ public $content; // varchar(140)
+ public $rendered; // text()
+ public $url; // varchar(255)
+ public $created; // datetime() not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+ public $source; // varchar(32)
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Message',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ function getFrom() {
+ return Profile::staticGet('id', $this->from_profile);
+ }
+
+ function getTo() {
+ return Profile::staticGet('id', $this->to_profile);
+ }
+
+ static function saveNew($from, $to, $content, $source) {
+
+ $msg = new Message();
+
+ $msg->from_profile = $from;
+ $msg->to_profile = $to;
+ $msg->content = common_shorten_links($content);
+ $msg->rendered = common_render_text($content);
+ $msg->created = common_sql_now();
+ $msg->source = $source;
+
+ $result = $msg->insert();
+
+ if (!$result) {
+ common_log_db_error($msg, 'INSERT', __FILE__);
+ return _('Could not insert message.');
+ }
+
+ $orig = clone($msg);
+ $msg->uri = common_local_url('showmessage', array('message' => $msg->id));
+
+ $result = $msg->update($orig);
+
+ if (!$result) {
+ common_log_db_error($msg, 'UPDATE', __FILE__);
+ return _('Could not update message with new URI.');
+ }
+
+ return $msg;
+ }
+}
diff --git a/_darcs/pristine/classes/Nonce.php b/_darcs/pristine/classes/Nonce.php
new file mode 100644
index 000000000..89d673c53
--- /dev/null
+++ b/_darcs/pristine/classes/Nonce.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Table Definition for nonce
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Nonce extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'nonce'; // table name
+ public $consumer_key; // varchar(255) primary_key not_null
+ public $tok; // char(32) primary_key not_null
+ public $nonce; // char(32) primary_key not_null
+ public $ts; // datetime() not_null
+ public $created; // datetime() not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Nonce',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+}
diff --git a/_darcs/pristine/classes/Notice.php b/_darcs/pristine/classes/Notice.php
new file mode 100644
index 000000000..ca8283bce
--- /dev/null
+++ b/_darcs/pristine/classes/Notice.php
@@ -0,0 +1,539 @@
+<?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); }
+
+/**
+ * Table Definition for notice
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+/* We keep the first three 20-notice pages, plus one for pagination check,
+ * in the memcached cache. */
+
+define('NOTICE_CACHE_WINDOW', 61);
+
+class Notice extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'notice'; // table name
+ public $id; // int(4) primary_key not_null
+ public $profile_id; // int(4) not_null
+ public $uri; // varchar(255) unique_key
+ public $content; // varchar(140)
+ public $rendered; // text()
+ public $url; // varchar(255)
+ public $created; // datetime() not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+ public $reply_to; // int(4)
+ public $is_local; // tinyint(1)
+ public $source; // varchar(32)
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Notice',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ function getProfile() {
+ return Profile::staticGet('id', $this->profile_id);
+ }
+
+ function delete() {
+ $this->blowCaches(true);
+ $this->blowFavesCache(true);
+ $this->blowInboxes();
+ return parent::delete();
+ }
+
+ function saveTags() {
+ /* extract all #hastags */
+ $count = preg_match_all('/(?:^|\s)#([A-Za-z0-9_\-\.]{1,64})/', strtolower($this->content), $match);
+ if (!$count) {
+ return true;
+ }
+
+ /* elide characters we don't want in the tag */
+ $match[1] = str_replace(array('-', '_', '.'), '', $match[1]);
+
+ /* Add them to the database */
+ foreach(array_unique($match[1]) as $hashtag) {
+ $tag = DB_DataObject::factory('Notice_tag');
+ $tag->notice_id = $this->id;
+ $tag->tag = $hashtag;
+ $tag->created = $this->created;
+ $id = $tag->insert();
+ if (!$id) {
+ $last_error = PEAR::getStaticProperty('DB_DataObject','lastError');
+ common_log(LOG_ERR, 'DB error inserting hashtag: ' . $last_error->message);
+ common_server_error(sprintf(_('DB error inserting hashtag: %s'), $last_error->message));
+ return;
+ }
+ }
+ return true;
+ }
+
+ static function saveNew($profile_id, $content, $source=NULL, $is_local=1, $reply_to=NULL, $uri=NULL) {
+
+ $profile = Profile::staticGet($profile_id);
+
+ if (!$profile) {
+ common_log(LOG_ERR, 'Problem saving notice. Unknown user.');
+ return _('Problem saving notice. Unknown user.');
+ }
+
+ if (common_config('throttle', 'enabled') && !Notice::checkEditThrottle($profile_id)) {
+ common_log(LOG_WARNING, 'Excessive posting by profile #' . $profile_id . '; throttled.');
+ return _('Too many notices too fast; take a breather and post again in a few minutes.');
+ }
+
+ $banned = common_config('profile', 'banned');
+
+ if ( in_array($profile_id, $banned) || in_array($profile->nickname, $banned)) {
+ common_log(LOG_WARNING, "Attempted post from banned user: $profile->nickname (user id = $profile_id).");
+ return _('You are banned from posting notices on this site.');
+ }
+
+ $notice = new Notice();
+ $notice->profile_id = $profile_id;
+
+ $blacklist = common_config('public', 'blacklist');
+
+ # Blacklisted are non-false, but not 1, either
+
+ if ($blacklist && in_array($profile_id, $blacklist)) {
+ $notice->is_local = -1;
+ } else {
+ $notice->is_local = $is_local;
+ }
+
+ $notice->reply_to = $reply_to;
+ $notice->created = common_sql_now();
+ $notice->content = common_shorten_links($content);
+ $notice->rendered = common_render_content($notice->content, $notice);
+ $notice->source = $source;
+ $notice->uri = $uri;
+
+ $id = $notice->insert();
+
+ if (!$id) {
+ common_log_db_error($notice, 'INSERT', __FILE__);
+ return _('Problem saving notice.');
+ }
+
+ # Update the URI after the notice is in the database
+ if (!$uri) {
+ $orig = clone($notice);
+ $notice->uri = common_notice_uri($notice);
+
+ if (!$notice->update($orig)) {
+ common_log_db_error($notice, 'UPDATE', __FILE__);
+ return _('Problem saving notice.');
+ }
+ }
+
+ # XXX: do we need to change this for remote users?
+
+ common_save_replies($notice);
+ $notice->saveTags();
+
+ # Clear the cache for subscribed users, so they'll update at next request
+ # XXX: someone clever could prepend instead of clearing the cache
+
+ if (common_config('memcached', 'enabled')) {
+ $notice->blowCaches();
+ }
+
+ $notice->addToInboxes();
+ return $notice;
+ }
+
+ static function checkEditThrottle($profile_id) {
+ $profile = Profile::staticGet($profile_id);
+ if (!$profile) {
+ return false;
+ }
+ # Get the Nth notice
+ $notice = $profile->getNotices(common_config('throttle', 'count') - 1, 1);
+ if ($notice && $notice->fetch()) {
+ # If the Nth notice was posted less than timespan seconds ago
+ if (time() - strtotime($notice->created) <= common_config('throttle', 'timespan')) {
+ # Then we throttle
+ return false;
+ }
+ }
+ # Either not N notices in the stream, OR the Nth was not posted within timespan seconds
+ return true;
+ }
+
+ function blowCaches($blowLast=false) {
+ $this->blowSubsCache($blowLast);
+ $this->blowNoticeCache($blowLast);
+ $this->blowRepliesCache($blowLast);
+ $this->blowPublicCache($blowLast);
+ $this->blowTagCache($blowLast);
+ }
+
+ function blowTagCache($blowLast=false) {
+ $cache = common_memcache();
+ if ($cache) {
+ $tag = new Notice_tag();
+ $tag->notice_id = $this->id;
+ if ($tag->find()) {
+ while ($tag->fetch()) {
+ $cache->delete(common_cache_key('notice_tag:notice_stream:' . $tag->tag));
+ if ($blowLast) {
+ $cache->delete(common_cache_key('notice_tag:notice_stream:' . $tag->tag . ';last'));
+ }
+ }
+ }
+ $tag->free();
+ unset($tag);
+ }
+ }
+
+ function blowSubsCache($blowLast=false) {
+ $cache = common_memcache();
+ if ($cache) {
+ $user = new User();
+
+ $user->query('SELECT id ' .
+ 'FROM user JOIN subscription ON user.id = subscription.subscriber ' .
+ 'WHERE subscription.subscribed = ' . $this->profile_id);
+
+ while ($user->fetch()) {
+ $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id));
+ if ($blowLast) {
+ $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id . ';last'));
+ }
+ }
+ $user->free();
+ unset($user);
+ }
+ }
+
+ function blowNoticeCache($blowLast=false) {
+ if ($this->is_local) {
+ $cache = common_memcache();
+ if ($cache) {
+ $cache->delete(common_cache_key('profile:notices:'.$this->profile_id));
+ if ($blowLast) {
+ $cache->delete(common_cache_key('profile:notices:'.$this->profile_id.';last'));
+ }
+ }
+ }
+ }
+
+ function blowRepliesCache($blowLast=false) {
+ $cache = common_memcache();
+ if ($cache) {
+ $reply = new Reply();
+ $reply->notice_id = $this->id;
+ if ($reply->find()) {
+ while ($reply->fetch()) {
+ $cache->delete(common_cache_key('user:replies:'.$reply->profile_id));
+ if ($blowLast) {
+ $cache->delete(common_cache_key('user:replies:'.$reply->profile_id.';last'));
+ }
+ }
+ }
+ $reply->free();
+ unset($reply);
+ }
+ }
+
+ function blowPublicCache($blowLast=false) {
+ if ($this->is_local == 1) {
+ $cache = common_memcache();
+ if ($cache) {
+ $cache->delete(common_cache_key('public'));
+ if ($blowLast) {
+ $cache->delete(common_cache_key('public').';last');
+ }
+ }
+ }
+ }
+
+ function blowFavesCache($blowLast=false) {
+ $cache = common_memcache();
+ if ($cache) {
+ $fave = new Fave();
+ $fave->notice_id = $this->id;
+ if ($fave->find()) {
+ while ($fave->fetch()) {
+ $cache->delete(common_cache_key('user:faves:'.$fave->user_id));
+ if ($blowLast) {
+ $cache->delete(common_cache_key('user:faves:'.$fave->user_id.';last'));
+ }
+ }
+ }
+ $fave->free();
+ unset($fave);
+ }
+ }
+
+ # XXX: too many args; we need to move to named params or even a separate
+ # class for notice streams
+
+ static function getStream($qry, $cachekey, $offset=0, $limit=20, $since_id=0, $before_id=0, $order=NULL, $since=NULL) {
+
+ if (common_config('memcached', 'enabled')) {
+
+ # Skip the cache if this is a since, since_id or before_id qry
+ if ($since_id > 0 || $before_id > 0 || $since) {
+ return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since);
+ } else {
+ return Notice::getCachedStream($qry, $cachekey, $offset, $limit, $order);
+ }
+ }
+
+ return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since);
+ }
+
+ static function getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since) {
+
+ $needAnd = FALSE;
+ $needWhere = TRUE;
+
+ if (preg_match('/\bWHERE\b/i', $qry)) {
+ $needWhere = FALSE;
+ $needAnd = TRUE;
+ }
+
+ if ($since_id > 0) {
+
+ if ($needWhere) {
+ $qry .= ' WHERE ';
+ $needWhere = FALSE;
+ } else {
+ $qry .= ' AND ';
+ }
+
+ $qry .= ' notice.id > ' . $since_id;
+ }
+
+ if ($before_id > 0) {
+
+ if ($needWhere) {
+ $qry .= ' WHERE ';
+ $needWhere = FALSE;
+ } else {
+ $qry .= ' AND ';
+ }
+
+ $qry .= ' notice.id < ' . $before_id;
+ }
+
+ if ($since) {
+
+ if ($needWhere) {
+ $qry .= ' WHERE ';
+ $needWhere = FALSE;
+ } else {
+ $qry .= ' AND ';
+ }
+
+ $qry .= ' notice.created > \'' . date('Y-m-d H:i:s', $since) . '\'';
+ }
+
+ # Allow ORDER override
+
+ if ($order) {
+ $qry .= $order;
+ } else {
+ $qry .= ' ORDER BY notice.created DESC, notice.id DESC ';
+ }
+
+ if (common_config('db','type') == 'pgsql') {
+ $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
+ } else {
+ $qry .= ' LIMIT ' . $offset . ', ' . $limit;
+ }
+
+ $notice = new Notice();
+
+ $notice->query($qry);
+
+ return $notice;
+ }
+
+ # XXX: this is pretty long and should probably be broken up into
+ # some helper functions
+
+ static function getCachedStream($qry, $cachekey, $offset, $limit, $order) {
+
+ # If outside our cache window, just go to the DB
+
+ if ($offset + $limit > NOTICE_CACHE_WINDOW) {
+ return Notice::getStreamDirect($qry, $offset, $limit, NULL, NULL, $order, NULL);
+ }
+
+ # Get the cache; if we can't, just go to the DB
+
+ $cache = common_memcache();
+
+ if (!$cache) {
+ return Notice::getStreamDirect($qry, $offset, $limit, NULL, NULL, $order, NULL);
+ }
+
+ # Get the notices out of the cache
+
+ $notices = $cache->get(common_cache_key($cachekey));
+
+ # On a cache hit, return a DB-object-like wrapper
+
+ if ($notices !== FALSE) {
+ $wrapper = new NoticeWrapper(array_slice($notices, $offset, $limit));
+ return $wrapper;
+ }
+
+ # If the cache was invalidated because of new data being
+ # added, we can try and just get the new stuff. We keep an additional
+ # copy of the data at the key + ';last'
+
+ # No cache hit. Try to get the *last* cached version
+
+ $last_notices = $cache->get(common_cache_key($cachekey) . ';last');
+
+ if ($last_notices) {
+
+ # Reverse-chron order, so last ID is last.
+
+ $last_id = $last_notices[0]->id;
+
+ # XXX: this assumes monotonically increasing IDs; a fair
+ # bet with our DB.
+
+ $new_notice = Notice::getStreamDirect($qry, 0, NOTICE_CACHE_WINDOW,
+ $last_id, NULL, $order, NULL);
+
+ if ($new_notice) {
+ $new_notices = array();
+ while ($new_notice->fetch()) {
+ $new_notices[] = clone($new_notice);
+ }
+ $new_notice->free();
+ $notices = array_slice(array_merge($new_notices, $last_notices),
+ 0, NOTICE_CACHE_WINDOW);
+
+ # Store the array in the cache for next time
+
+ $result = $cache->set(common_cache_key($cachekey), $notices);
+ $result = $cache->set(common_cache_key($cachekey) . ';last', $notices);
+
+ # return a wrapper of the array for use now
+
+ return new NoticeWrapper(array_slice($notices, $offset, $limit));
+ }
+ }
+
+ # Otherwise, get the full cache window out of the DB
+
+ $notice = Notice::getStreamDirect($qry, 0, NOTICE_CACHE_WINDOW, NULL, NULL, $order, NULL);
+
+ # If there are no hits, just return the value
+
+ if (!$notice) {
+ return $notice;
+ }
+
+ # Pack results into an array
+
+ $notices = array();
+
+ while ($notice->fetch()) {
+ $notices[] = clone($notice);
+ }
+
+ $notice->free();
+
+ # Store the array in the cache for next time
+
+ $result = $cache->set(common_cache_key($cachekey), $notices);
+ $result = $cache->set(common_cache_key($cachekey) . ';last', $notices);
+
+ # return a wrapper of the array for use now
+
+ $wrapper = new NoticeWrapper(array_slice($notices, $offset, $limit));
+
+ return $wrapper;
+ }
+
+ function publicStream($offset=0, $limit=20, $since_id=0, $before_id=0, $since=NULL) {
+
+ $parts = array();
+
+ $qry = 'SELECT * FROM notice ';
+
+ if (common_config('public', 'localonly')) {
+ $parts[] = 'is_local = 1';
+ } else {
+ # -1 == blacklisted
+ $parts[] = 'is_local != -1';
+ }
+
+ if ($parts) {
+ $qry .= ' WHERE ' . implode(' AND ', $parts);
+ }
+
+ return Notice::getStream($qry,
+ 'public',
+ $offset, $limit, $since_id, $before_id, NULL, $since);
+ }
+
+ function addToInboxes() {
+ $enabled = common_config('inboxes', 'enabled');
+
+ if ($enabled === true || $enabled === 'transitional') {
+ $inbox = new Notice_inbox();
+ $qry = 'INSERT INTO notice_inbox (user_id, notice_id, created) ' .
+ 'SELECT user.id, ' . $this->id . ', "' . $this->created . '" ' .
+ 'FROM user JOIN subscription ON user.id = subscription.subscriber ' .
+ 'WHERE subscription.subscribed = ' . $this->profile_id . ' ' .
+ 'AND NOT EXISTS (SELECT user_id, notice_id ' .
+ 'FROM notice_inbox ' .
+ 'WHERE user_id = user.id ' .
+ 'AND notice_id = ' . $this->id . ' )';
+ if ($enabled === 'transitional') {
+ $qry .= ' AND user.inboxed = 1';
+ }
+ $inbox->query($qry);
+ }
+ return;
+ }
+
+ # Delete from inboxes if we're deleted.
+
+ function blowInboxes() {
+
+ $enabled = common_config('inboxes', 'enabled');
+
+ if ($enabled === true || $enabled === 'transitional') {
+ $inbox = new Notice_inbox();
+ $inbox->notice_id = $this->id;
+ $inbox->delete();
+ }
+
+ return;
+ }
+
+}
+
diff --git a/_darcs/pristine/classes/NoticeWrapper.php b/_darcs/pristine/classes/NoticeWrapper.php
new file mode 100644
index 000000000..f8c0aa381
--- /dev/null
+++ b/_darcs/pristine/classes/NoticeWrapper.php
@@ -0,0 +1,59 @@
+<?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.'/classes/Notice.php');
+
+class NoticeWrapper extends Notice {
+
+ public $id; // int(4) primary_key not_null
+ public $profile_id; // int(4) not_null
+ public $uri; // varchar(255) unique_key
+ public $content; // varchar(140)
+ public $rendered; // text()
+ public $url; // varchar(255)
+ public $created; // datetime() not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+ public $reply_to; // int(4)
+ public $is_local; // tinyint(1)
+ public $source; // varchar(32)
+
+ var $notices = NULL;
+ var $i = -1;
+
+ function __construct($arr) {
+ $this->notices = $arr;
+ }
+
+ function fetch() {
+ static $fields = array('id', 'profile_id', 'uri', 'content', 'rendered',
+ 'url', 'created', 'modified', 'reply_to', 'is_local', 'source');
+ $this->i++;
+ if ($this->i >= count($this->notices)) {
+ return false;
+ } else {
+ $n = $this->notices[$this->i];
+ foreach ($fields as $f) {
+ $this->$f = $n->$f;
+ }
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/_darcs/pristine/classes/Notice_inbox.php b/_darcs/pristine/classes/Notice_inbox.php
new file mode 100644
index 000000000..cc482bd19
--- /dev/null
+++ b/_darcs/pristine/classes/Notice_inbox.php
@@ -0,0 +1,40 @@
+<?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.'/classes/Memcached_DataObject.php';
+
+class Notice_inbox extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'notice_inbox'; // table name
+ public $user_id; // int(4) primary_key not_null
+ public $notice_id; // int(4) primary_key not_null
+ public $created; // datetime() not_null
+ public $source; // tinyint(1) default_1
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Notice_inbox',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+}
diff --git a/_darcs/pristine/classes/Notice_source.php b/_darcs/pristine/classes/Notice_source.php
new file mode 100644
index 000000000..e0a41b927
--- /dev/null
+++ b/_darcs/pristine/classes/Notice_source.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Table Definition for notice_source
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Notice_source extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'notice_source'; // table name
+ public $code; // varchar(32) primary_key not_null
+ public $name; // varchar(255) not_null
+ public $url; // varchar(255) not_null
+ public $created; // datetime() not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Notice_source',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+}
diff --git a/_darcs/pristine/classes/Notice_tag.php b/_darcs/pristine/classes/Notice_tag.php
new file mode 100644
index 000000000..5b75ff13f
--- /dev/null
+++ b/_darcs/pristine/classes/Notice_tag.php
@@ -0,0 +1,55 @@
+<?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/>.
+ */
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Notice_tag extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'notice_tag'; // table name
+ public $tag; // varchar(64) primary_key not_null
+ public $notice_id; // int(4) primary_key not_null
+ public $created; // datetime() not_null
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Notice_tag',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ static function getStream($tag, $offset=0, $limit=20) {
+ $qry =
+ 'SELECT notice.* ' .
+ 'FROM notice JOIN notice_tag ON notice.id = notice_tag.notice_id ' .
+ 'WHERE notice_tag.tag = "%s" ';
+
+ return Notice::getStream(sprintf($qry, $tag),
+ 'notice_tag:notice_stream:' . common_keyize($tag),
+ $offset, $limit);
+ }
+
+ function blowCache() {
+ $cache = common_memcache();
+ if ($cache) {
+ $cache->delete(common_cache_key('notice_tag:notice_stream:' . $this->tag));
+ }
+ }
+}
diff --git a/_darcs/pristine/classes/Profile.php b/_darcs/pristine/classes/Profile.php
new file mode 100644
index 000000000..b57d7e38d
--- /dev/null
+++ b/_darcs/pristine/classes/Profile.php
@@ -0,0 +1,159 @@
+<?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); }
+
+/**
+ * Table Definition for profile
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Profile extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'profile'; // table name
+ public $id; // int(4) primary_key not_null
+ public $nickname; // varchar(64) multiple_key not_null
+ public $fullname; // varchar(255) multiple_key
+ public $profileurl; // varchar(255)
+ public $homepage; // varchar(255) multiple_key
+ public $bio; // varchar(140) multiple_key
+ public $location; // varchar(255) multiple_key
+ public $created; // datetime() not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Profile',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ function getAvatar($width, $height=NULL) {
+ if (is_null($height)) {
+ $height = $width;
+ }
+ return Avatar::pkeyGet(array('profile_id' => $this->id,
+ 'width' => $width,
+ 'height' => $height));
+ }
+
+ function getOriginalAvatar() {
+ $avatar = DB_DataObject::factory('avatar');
+ $avatar->profile_id = $this->id;
+ $avatar->original = true;
+ if ($avatar->find(true)) {
+ return $avatar;
+ } else {
+ return NULL;
+ }
+ }
+
+ function setOriginal($source) {
+
+ $info = @getimagesize($source);
+
+ if (!$info) {
+ return NULL;
+ }
+
+ $filename = common_avatar_filename($this->id,
+ image_type_to_extension($info[2]),
+ NULL, common_timestamp());
+ $filepath = common_avatar_path($filename);
+
+ copy($source, $filepath);
+
+ $avatar = new Avatar();
+
+ $avatar->profile_id = $this->id;
+ $avatar->width = $info[0];
+ $avatar->height = $info[1];
+ $avatar->mediatype = image_type_to_mime_type($info[2]);
+ $avatar->filename = $filename;
+ $avatar->original = true;
+ $avatar->url = common_avatar_url($filename);
+ $avatar->created = DB_DataObject_Cast::dateTime(); # current time
+
+ # XXX: start a transaction here
+
+ if (!$this->delete_avatars()) {
+ @unlink($filepath);
+ return NULL;
+ }
+
+ if (!$avatar->insert()) {
+ @unlink($filepath);
+ return NULL;
+ }
+
+ foreach (array(AVATAR_PROFILE_SIZE, AVATAR_STREAM_SIZE, AVATAR_MINI_SIZE) as $size) {
+ # We don't do a scaled one if original is our scaled size
+ if (!($avatar->width == $size && $avatar->height == $size)) {
+ $s = $avatar->scale($size);
+ if (!$s) {
+ return NULL;
+ }
+ }
+ }
+
+ return $avatar;
+ }
+
+ function delete_avatars() {
+ $avatar = new Avatar();
+ $avatar->profile_id = $this->id;
+ $avatar->find();
+ while ($avatar->fetch()) {
+ $avatar->delete();
+ }
+ return true;
+ }
+
+ function getBestName() {
+ return ($this->fullname) ? $this->fullname : $this->nickname;
+ }
+
+ # Get latest notice on or before date; default now
+ function getCurrentNotice($dt=NULL) {
+ $notice = new Notice();
+ $notice->profile_id = $this->id;
+ if ($dt) {
+ $notice->whereAdd('created < "' . $dt . '"');
+ }
+ $notice->orderBy('created DESC, notice.id DESC');
+ $notice->limit(1);
+ if ($notice->find(true)) {
+ return $notice;
+ }
+ return NULL;
+ }
+
+ function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) {
+ $qry =
+ 'SELECT * ' .
+ 'FROM notice ' .
+ 'WHERE profile_id = %d ';
+
+ return Notice::getStream(sprintf($qry, $this->id),
+ 'profile:notices:'.$this->id,
+ $offset, $limit, $since_id, $before_id);
+ }
+}
diff --git a/_darcs/pristine/classes/Profile_block.php b/_darcs/pristine/classes/Profile_block.php
new file mode 100644
index 000000000..6ea26a3bc
--- /dev/null
+++ b/_darcs/pristine/classes/Profile_block.php
@@ -0,0 +1,49 @@
+<?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); }
+
+/**
+ * Table Definition for profile_block
+ */
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Profile_block extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'profile_block'; // table name
+ public $blocker; // int(4) primary_key not_null
+ public $blocked; // int(4) primary_key not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Profile_block',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ function get($blocker, $blocked) {
+ return Memcached_DataObject::pkeyGet('Profile_block',
+ array('blocker' => $blocker,
+ 'blocked' => $blocked));
+ }
+}
diff --git a/_darcs/pristine/classes/Profile_tag.php b/_darcs/pristine/classes/Profile_tag.php
new file mode 100644
index 000000000..dde19aea2
--- /dev/null
+++ b/_darcs/pristine/classes/Profile_tag.php
@@ -0,0 +1,101 @@
+<?php
+/**
+ * Table Definition for profile_tag
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Profile_tag extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'profile_tag'; // table name
+ public $tagger; // int(4) primary_key not_null
+ public $tagged; // int(4) primary_key not_null
+ public $tag; // varchar(64) primary_key not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Profile_tag',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ static function getTags($tagger, $tagged) {
+
+ $tags = array();
+
+ # XXX: store this in memcached
+
+ $profile_tag = new Profile_tag();
+ $profile_tag->tagger = $tagger;
+ $profile_tag->tagged = $tagged;
+
+ $profile_tag->find();
+
+ while ($profile_tag->fetch()) {
+ $tags[] = $profile_tag->tag;
+ }
+
+ $profile_tag->free();
+
+ return $tags;
+ }
+
+ static function setTags($tagger, $tagged, $newtags) {
+
+ $oldtags = Profile_tag::getTags($tagger, $tagged);
+
+ # Delete stuff that's old that not in new
+
+ $to_delete = array_diff($oldtags, $newtags);
+
+ # Insert stuff that's in new and not in old
+
+ $to_insert = array_diff($newtags, $oldtags);
+
+ $profile_tag = new Profile_tag();
+
+ $profile_tag->tagger = $tagger;
+ $profile_tag->tagged = $tagged;
+
+ $profile_tag->query('BEGIN');
+
+ foreach ($to_delete as $deltag) {
+ $profile_tag->tag = $deltag;
+ $result = $profile_tag->delete();
+ if (!$result) {
+ common_log_db_error($profile_tag, 'DELETE', __FILE__);
+ return false;
+ }
+ }
+
+ foreach ($to_insert as $instag) {
+ $profile_tag->tag = $instag;
+ $result = $profile_tag->insert();
+ if (!$result) {
+ common_log_db_error($profile_tag, 'INSERT', __FILE__);
+ return false;
+ }
+ }
+
+ $profile_tag->query('COMMIT');
+
+ return true;
+ }
+
+ # Return profiles with a given tag
+ static function getTagged($tagger, $tag) {
+ $profile = new Profile();
+ $profile->query('SELECT profile.* ' .
+ 'FROM profile JOIN profile_tag ' .
+ 'ON profile.id = profile_tag.tagged ' .
+ 'WHERE profile_tag.tagger = ' . $tagger . ' ' .
+ 'AND profile_tag.tag = "' . $tag . '" ');
+ $tagged = array();
+ while ($profile->fetch()) {
+ $tagged[] = clone($profile);
+ }
+ return $tagged;
+ }
+}
diff --git a/_darcs/pristine/classes/Queue_item.php b/_darcs/pristine/classes/Queue_item.php
new file mode 100644
index 000000000..8ba3281de
--- /dev/null
+++ b/_darcs/pristine/classes/Queue_item.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Table Definition for queue_item
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Queue_item extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'queue_item'; // table name
+ public $notice_id; // int(4) primary_key not_null
+ public $transport; // varchar(8) primary_key not_null
+ public $created; // datetime() not_null
+ public $claimed; // datetime()
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Queue_item',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ function sequenceKey() { return array(false, false); }
+
+ static function top($transport) {
+
+ $qi = new Queue_item();
+ $qi->transport = $transport;
+ $qi->orderBy('created');
+ $qi->whereAdd('claimed is NULL');
+
+ $qi->limit(1);
+
+ $cnt = $qi->find(TRUE);
+
+ if ($cnt) {
+ # XXX: potential race condition
+ # can we force it to only update if claimed is still NULL
+ # (or old)?
+ common_log(LOG_INFO, 'claiming queue item = ' . $qi->notice_id . ' for transport ' . $transport);
+ $orig = clone($qi);
+ $qi->claimed = common_sql_now();
+ $result = $qi->update($orig);
+ if ($result) {
+ common_log(LOG_INFO, 'claim succeeded.');
+ return $qi;
+ } else {
+ common_log(LOG_INFO, 'claim failed.');
+ }
+ }
+ $qi = NULL;
+ return NULL;
+ }
+}
diff --git a/_darcs/pristine/classes/Remember_me.php b/_darcs/pristine/classes/Remember_me.php
new file mode 100644
index 000000000..5bbd6cf17
--- /dev/null
+++ b/_darcs/pristine/classes/Remember_me.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Table Definition for remember_me
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Remember_me extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'remember_me'; // table name
+ public $code; // varchar(32) primary_key not_null
+ public $user_id; // int(4) not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Remember_me',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ function sequenceKey() { return array(false, false); }
+}
diff --git a/_darcs/pristine/classes/Remote_profile.php b/_darcs/pristine/classes/Remote_profile.php
new file mode 100644
index 000000000..c961dcca2
--- /dev/null
+++ b/_darcs/pristine/classes/Remote_profile.php
@@ -0,0 +1,45 @@
+<?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); }
+
+/**
+ * Table Definition for remote_profile
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Remote_profile extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'remote_profile'; // table name
+ public $id; // int(4) primary_key not_null
+ public $uri; // varchar(255) unique_key
+ public $postnoticeurl; // varchar(255)
+ public $updateprofileurl; // 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('Remote_profile',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+}
diff --git a/_darcs/pristine/classes/Reply.php b/_darcs/pristine/classes/Reply.php
new file mode 100644
index 000000000..d71ce3afb
--- /dev/null
+++ b/_darcs/pristine/classes/Reply.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Table Definition for reply
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Reply extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'reply'; // table name
+ public $notice_id; // int(4) primary_key not_null
+ public $profile_id; // int(4) primary_key not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+ public $replied_id; // int(4)
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Reply',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+}
diff --git a/_darcs/pristine/classes/Sms_carrier.php b/_darcs/pristine/classes/Sms_carrier.php
new file mode 100644
index 000000000..6ecb51346
--- /dev/null
+++ b/_darcs/pristine/classes/Sms_carrier.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Table Definition for sms_carrier
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Sms_carrier extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'sms_carrier'; // table name
+ public $id; // int(4) primary_key not_null
+ public $name; // varchar(64) unique_key
+ public $email_pattern; // varchar(255) not_null
+ public $created; // datetime() not_null
+ public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Sms_carrier',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ function toEmailAddress($sms) {
+ return sprintf($this->email_pattern, $sms);
+ }
+}
diff --git a/_darcs/pristine/classes/Subscription.php b/_darcs/pristine/classes/Subscription.php
new file mode 100644
index 000000000..cc174fcce
--- /dev/null
+++ b/_darcs/pristine/classes/Subscription.php
@@ -0,0 +1,51 @@
+<?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); }
+
+/**
+ * Table Definition for subscription
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Subscription extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'subscription'; // table name
+ public $subscriber; // int(4) primary_key not_null
+ public $subscribed; // int(4) primary_key not_null
+ public $jabber; // tinyint(1) default_1
+ public $sms; // tinyint(1) default_1
+ public $token; // varchar(255)
+ public $secret; // 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('Subscription',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ function &pkeyGet($kv) {
+ return Memcached_DataObject::pkeyGet('Subscription', $kv);
+ }
+}
diff --git a/_darcs/pristine/classes/Token.php b/_darcs/pristine/classes/Token.php
new file mode 100644
index 000000000..d180ecebe
--- /dev/null
+++ b/_darcs/pristine/classes/Token.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * Table Definition for token
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Token extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'token'; // table name
+ public $consumer_key; // varchar(255) primary_key not_null
+ public $tok; // char(32) primary_key not_null
+ public $secret; // char(32) not_null
+ public $type; // tinyint(1) not_null
+ public $state; // tinyint(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('Token',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+}
diff --git a/_darcs/pristine/classes/User.php b/_darcs/pristine/classes/User.php
new file mode 100644
index 000000000..32d5bedde
--- /dev/null
+++ b/_darcs/pristine/classes/User.php
@@ -0,0 +1,473 @@
+<?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); }
+
+/**
+ * Table Definition for user
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+require_once 'Validate.php';
+
+class User extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'user'; // table name
+ public $id; // int(4) primary_key not_null
+ public $nickname; // varchar(64) unique_key
+ public $password; // varchar(255)
+ public $email; // varchar(255) unique_key
+ public $incomingemail; // varchar(255) unique_key
+ public $emailnotifysub; // tinyint(1) default_1
+ public $emailnotifyfav; // tinyint(1) default_1
+ public $emailnotifynudge; // tinyint(1) default_1
+ public $emailnotifymsg; // tinyint(1) default_1
+ public $emailmicroid; // tinyint(1) default_1
+ public $language; // varchar(50)
+ public $timezone; // varchar(50)
+ public $emailpost; // tinyint(1) default_1
+ public $jabber; // varchar(255) unique_key
+ public $jabbernotify; // tinyint(1)
+ public $jabberreplies; // tinyint(1)
+ public $jabbermicroid; // tinyint(1) default_1
+ public $updatefrompresence; // tinyint(1)
+ public $sms; // varchar(64) unique_key
+ public $carrier; // int(4)
+ public $smsnotify; // tinyint(1)
+ public $smsreplies; // tinyint(1)
+ public $smsemail; // varchar(255)
+ public $uri; // varchar(255) unique_key
+ public $autosubscribe; // tinyint(1)
+ public $urlshorteningservice; // varchar(50) default_ur1.ca
+ public $inboxed; // tinyint(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); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+
+ function getProfile() {
+ return Profile::staticGet('id', $this->id);
+ }
+
+ function isSubscribed($other) {
+ assert(!is_null($other));
+ # XXX: cache results of this query
+ $sub = Subscription::pkeyGet(array('subscriber' => $this->id,
+ 'subscribed' => $other->id));
+ return (is_null($sub)) ? false : true;
+ }
+
+ # 'update' won't write key columns, so we have to do it ourselves.
+
+ function updateKeys(&$orig) {
+ $parts = array();
+ foreach (array('nickname', 'email', 'jabber', 'incomingemail', 'sms', 'carrier', 'smsemail', 'language', 'timezone') as $k) {
+ if (strcmp($this->$k, $orig->$k) != 0) {
+ $parts[] = $k . ' = ' . $this->_quote($this->$k);
+ }
+ }
+ if (count($parts) == 0) {
+ # No changes
+ return true;
+ }
+ $toupdate = implode(', ', $parts);
+
+ $table = $this->tableName();
+ if(common_config('db','quote_identifiers')) {
+ $table = '"' . $table . '"';
+ }
+ $qry = 'UPDATE ' . $table . ' SET ' . $toupdate .
+ ' WHERE id = ' . $this->id;
+ $orig->decache();
+ $result = $this->query($qry);
+ if ($result) {
+ $this->encache();
+ }
+ return $result;
+ }
+
+ function allowed_nickname($nickname) {
+ # XXX: should already be validated for size, content, etc.
+ static $blacklist = array('rss', 'xrds', 'doc', 'main',
+ 'settings', 'notice', 'user',
+ 'search', 'avatar', 'tag', 'tags',
+ 'api', 'message');
+ $merged = array_merge($blacklist, common_config('nickname', 'blacklist'));
+ return !in_array($nickname, $merged);
+ }
+
+ function getCurrentNotice($dt=NULL) {
+ $profile = $this->getProfile();
+ if (!$profile) {
+ return NULL;
+ }
+ return $profile->getCurrentNotice($dt);
+ }
+
+ function getCarrier() {
+ return Sms_carrier::staticGet('id', $this->carrier);
+ }
+
+ function subscribeTo($other) {
+ $sub = new Subscription();
+ $sub->subscriber = $this->id;
+ $sub->subscribed = $other->id;
+
+ $sub->created = common_sql_now(); # current time
+
+ if (!$sub->insert()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ function hasBlocked($other) {
+
+ $block = Profile_block::get($this->id, $other->id);
+
+ if (is_null($block)) {
+ $result = false;
+ } else {
+ $result = true;
+ $block->free();
+ }
+
+ return $result;
+ }
+
+ static function register($fields) {
+
+ # MAGICALLY put fields into current scope
+
+ extract($fields);
+
+ $profile = new Profile();
+
+ $profile->query('BEGIN');
+
+ $profile->nickname = $nickname;
+ $profile->profileurl = common_profile_url($nickname);
+
+ if ($fullname) {
+ $profile->fullname = $fullname;
+ }
+ if ($homepage) {
+ $profile->homepage = $homepage;
+ }
+ if ($bio) {
+ $profile->bio = $bio;
+ }
+ if ($location) {
+ $profile->location = $location;
+ }
+
+ $profile->created = common_sql_now();
+
+ $id = $profile->insert();
+
+ if (!$id) {
+ common_log_db_error($profile, 'INSERT', __FILE__);
+ return FALSE;
+ }
+
+ $user = new User();
+
+ $user->id = $id;
+ $user->nickname = $nickname;
+
+ if ($password) { # may not have a password for OpenID users
+ $user->password = common_munge_password($password, $id);
+ }
+
+ # Users who respond to invite email have proven their ownership of that address
+
+ if ($code) {
+ $invite = Invitation::staticGet($code);
+ if ($invite && $invite->address && $invite->address_type == 'email' && $invite->address == $email) {
+ $user->email = $invite->address;
+ }
+ }
+
+ $inboxes = common_config('inboxes', 'enabled');
+
+ if ($inboxes === true || $inboxes == 'transitional') {
+ $user->inboxed = 1;
+ }
+
+ $user->created = common_sql_now();
+ $user->uri = common_user_uri($user);
+
+ $result = $user->insert();
+
+ if (!$result) {
+ common_log_db_error($user, 'INSERT', __FILE__);
+ return FALSE;
+ }
+
+ # Everyone is subscribed to themself
+
+ $subscription = new Subscription();
+ $subscription->subscriber = $user->id;
+ $subscription->subscribed = $user->id;
+ $subscription->created = $user->created;
+
+ $result = $subscription->insert();
+
+ if (!$result) {
+ common_log_db_error($subscription, 'INSERT', __FILE__);
+ return FALSE;
+ }
+
+ if ($email && !$user->email) {
+
+ $confirm = new Confirm_address();
+ $confirm->code = common_confirmation_code(128);
+ $confirm->user_id = $user->id;
+ $confirm->address = $email;
+ $confirm->address_type = 'email';
+
+ $result = $confirm->insert();
+ if (!$result) {
+ common_log_db_error($confirm, 'INSERT', __FILE__);
+ return FALSE;
+ }
+ }
+
+ if ($code && $user->email) {
+ $user->emailChanged();
+ }
+
+ $profile->query('COMMIT');
+
+ if ($email && !$user->email) {
+ mail_confirm_address($user, $confirm->code, $profile->nickname, $email);
+ }
+
+ return $user;
+ }
+
+ # Things we do when the email changes
+
+ function emailChanged() {
+
+ $invites = new Invitation();
+ $invites->address = $this->email;
+ $invites->address_type = 'email';
+
+ if ($invites->find()) {
+ while ($invites->fetch()) {
+ $other = User::staticGet($invites->user_id);
+ subs_subscribe_to($other, $this);
+ }
+ }
+ }
+
+ function hasFave($notice) {
+ $cache = common_memcache();
+
+ # XXX: Kind of a hack.
+ if ($cache) {
+ # This is the stream of favorite notices, in rev chron
+ # order. This forces it into cache.
+ $faves = $this->favoriteNotices(0, NOTICE_CACHE_WINDOW);
+ $cnt = 0;
+ while ($faves->fetch()) {
+ if ($faves->id < $notice->id) {
+ # If we passed it, it's not a fave
+ return false;
+ } else if ($faves->id == $notice->id) {
+ # If it matches a cached notice, then it's a fave
+ return true;
+ }
+ $cnt++;
+ }
+ # If we're not past the end of the cache window,
+ # then the cache has all available faves, so this one
+ # is not a fave.
+ if ($cnt < NOTICE_CACHE_WINDOW) {
+ return false;
+ }
+ # Otherwise, cache doesn't have all faves;
+ # fall through to the default
+ }
+ $fave = Fave::pkeyGet(array('user_id' => $this->id,
+ 'notice_id' => $notice->id));
+ return ((is_null($fave)) ? false : true);
+ }
+ function mutuallySubscribed($other) {
+ return $this->isSubscribed($other) &&
+ $other->isSubscribed($this);
+ }
+
+ function mutuallySubscribedUsers() {
+
+ # 3-way join; probably should get cached
+ $qry = 'SELECT user.* ' .
+ 'FROM subscription sub1 JOIN user ON sub1.subscribed = user.id ' .
+ 'JOIN subscription sub2 ON user.id = sub2.subscriber ' .
+ 'WHERE sub1.subscriber = %d and sub2.subscribed = %d ' .
+ 'ORDER BY user.nickname';
+ $user = new User();
+ $user->query(sprintf($qry, $this->id, $this->id));
+
+ return $user;
+ }
+
+ function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=NULL) {
+ $qry =
+ 'SELECT notice.* ' .
+ 'FROM notice JOIN reply ON notice.id = reply.notice_id ' .
+ 'WHERE reply.profile_id = %d ';
+ return Notice::getStream(sprintf($qry, $this->id),
+ 'user:replies:'.$this->id,
+ $offset, $limit, $since_id, $before_id, NULL, $since);
+ }
+
+ function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=NULL) {
+ $profile = $this->getProfile();
+ if (!$profile) {
+ return NULL;
+ } else {
+ return $profile->getNotices($offset, $limit, $since_id, $before_id);
+ }
+ }
+
+ function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE) {
+ $qry =
+ 'SELECT notice.* ' .
+ 'FROM notice JOIN fave ON notice.id = fave.notice_id ' .
+ 'WHERE fave.user_id = %d ';
+ return Notice::getStream(sprintf($qry, $this->id),
+ 'user:faves:'.$this->id,
+ $offset, $limit);
+ }
+
+ function noticesWithFriends($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 ';
+ $order = NULL;
+ } else if ($enabled === true ||
+ ($enabled == 'transitional' && $this->inboxed == 1)) {
+
+ $qry =
+ 'SELECT notice.* ' .
+ 'FROM notice JOIN notice_inbox ON notice.id = notice_inbox.notice_id ' .
+ 'WHERE notice_inbox.user_id = %d ';
+ # NOTE: we override ORDER
+ $order = 'ORDER BY notice_inbox.created DESC, notice_inbox.notice_id DESC ';
+ }
+ return Notice::getStream(sprintf($qry, $this->id),
+ 'user:notices_with_friends:' . $this->id,
+ $offset, $limit, $since_id, $before_id,
+ $order, $since);
+ }
+
+ function blowFavesCache() {
+ $cache = common_memcache();
+ if ($cache) {
+ # Faves don't happen chronologically, so we need to blow
+ # ;last cache, too
+ $cache->delete(common_cache_key('user:faves:'.$this->id));
+ $cache->delete(common_cache_key('user:faves:'.$this->id).';last');
+ }
+ }
+
+ function getSelfTags() {
+ return Profile_tag::getTags($this->id, $this->id);
+ }
+
+ function setSelfTags($newtags) {
+ return Profile_tag::setTags($this->id, $this->id, $newtags);
+ }
+
+ function block($other) {
+
+ # Add a new block record
+
+ $block = new Profile_block();
+
+ # Begin a transaction
+
+ $block->query('BEGIN');
+
+ $block->blocker = $this->id;
+ $block->blocked = $other->id;
+
+ $result = $block->insert();
+
+ if (!$result) {
+ common_log_db_error($block, 'INSERT', __FILE__);
+ return false;
+ }
+
+ # Cancel their subscription, if it exists
+
+ $sub = Subscription::pkeyGet(array('subscriber' => $other->id,
+ 'subscribed' => $this->id));
+
+ if ($sub) {
+ $result = $sub->delete();
+ if (!$result) {
+ common_log_db_error($sub, 'DELETE', __FILE__);
+ return false;
+ }
+ }
+
+ $block->query('COMMIT');
+
+ return true;
+ }
+
+ function unblock($other) {
+
+ # Get the block record
+
+ $block = Profile_block::get($this->id, $other->id);
+
+ if (!$block) {
+ return false;
+ }
+
+ $result = $block->delete();
+
+ if (!$result) {
+ common_log_db_error($block, 'DELETE', __FILE__);
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/_darcs/pristine/classes/User_openid.php b/_darcs/pristine/classes/User_openid.php
new file mode 100644
index 000000000..ad68f7402
--- /dev/null
+++ b/_darcs/pristine/classes/User_openid.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Table Definition for user_openid
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class User_openid extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'user_openid'; // table name
+ public $canonical; // varchar(255) primary_key not_null
+ public $display; // varchar(255) unique_key not_null
+ public $user_id; // int(4) not_null
+ 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_openid',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+}
diff --git a/_darcs/pristine/classes/laconica.ini b/_darcs/pristine/classes/laconica.ini
new file mode 100644
index 000000000..db76b2dee
--- /dev/null
+++ b/_darcs/pristine/classes/laconica.ini
@@ -0,0 +1,344 @@
+
+[avatar]
+profile_id = 129
+original = 17
+width = 129
+height = 129
+mediatype = 130
+filename = 2
+url = 2
+created = 142
+modified = 384
+
+[avatar__keys]
+profile_id = K
+width = K
+height = K
+url = U
+
+[confirm_address]
+code = 130
+user_id = 129
+address = 130
+address_extra = 130
+address_type = 130
+claimed = 14
+sent = 14
+modified = 384
+
+[confirm_address__keys]
+code = K
+
+[consumer]
+consumer_key = 130
+seed = 130
+created = 142
+modified = 384
+
+[consumer__keys]
+consumer_key = K
+
+[fave]
+notice_id = 129
+user_id = 129
+modified = 384
+
+[fave__keys]
+notice_id = K
+user_id = K
+
+[foreign_link]
+user_id = 129
+foreign_id = 129
+service = 129
+credentials = 2
+noticesync = 145
+friendsync = 145
+profilesync = 145
+created = 142
+modified = 384
+
+[foreign_link__keys]
+user_id = K
+foreign_id = K
+service = K
+
+[foreign_service]
+id = 129
+name = 130
+description = 2
+created = 142
+modified = 384
+
+[foreign_service__keys]
+id = K
+name = U
+
+[foreign_subscription]
+service = 129
+subscriber = 129
+subscribed = 129
+created = 142
+
+[foreign_subscription__keys]
+service = K
+subscriber = K
+subscribed = K
+
+[foreign_user]
+id = 129
+service = 129
+uri = 130
+nickname = 2
+created = 142
+modified = 384
+
+[foreign_user__keys]
+id = K
+service = K
+uri = U
+
+[invitation]
+code = 130
+user_id = 129
+address = 130
+address_type = 130
+created = 142
+
+[invitation__keys]
+code = K
+
+[message]
+id = 129
+uri = 2
+from_profile = 129
+to_profile = 129
+content = 2
+rendered = 34
+url = 2
+created = 142
+modified = 384
+source = 2
+
+[message__keys]
+id = N
+
+[nonce]
+consumer_key = 130
+tok = 130
+nonce = 130
+ts = 142
+created = 142
+modified = 384
+
+[nonce__keys]
+consumer_key = K
+tok = K
+nonce = K
+
+[notice]
+id = 129
+profile_id = 129
+uri = 2
+content = 2
+rendered = 34
+url = 2
+created = 142
+modified = 384
+reply_to = 1
+is_local = 17
+source = 2
+
+[notice__keys]
+id = N
+
+[notice_inbox]
+user_id = 129
+notice_id = 129
+created = 142
+source = 17
+
+[notice_inbox__keys]
+user_id = K
+notice_id = K
+
+[notice_source]
+code = 130
+name = 130
+url = 130
+created = 142
+modified = 384
+
+[notice_source__keys]
+code = K
+
+[notice_tag]
+tag = 130
+notice_id = 129
+created = 142
+
+[notice_tag__keys]
+tag = K
+notice_id = K
+
+[profile]
+id = 129
+nickname = 130
+fullname = 2
+profileurl = 2
+homepage = 2
+bio = 2
+location = 2
+created = 142
+modified = 384
+
+[profile__keys]
+id = N
+
+[profile_block]
+blocker = 129
+blocked = 129
+modified = 384
+
+[profile_block__keys]
+blocker = K
+blocked = K
+
+[profile_tag]
+tagger = 129
+tagged = 129
+tag = 130
+modified = 384
+
+[profile_tag__keys]
+tagger = K
+tagged = K
+tag = K
+
+[queue_item]
+notice_id = 129
+transport = 130
+created = 142
+claimed = 14
+
+[queue_item__keys]
+notice_id = K
+transport = K
+
+[remember_me]
+code = 130
+user_id = 129
+modified = 384
+
+[remember_me__keys]
+code = K
+
+[remote_profile]
+id = 129
+uri = 2
+postnoticeurl = 2
+updateprofileurl = 2
+created = 142
+modified = 384
+
+[remote_profile__keys]
+id = K
+uri = U
+
+[reply]
+notice_id = 129
+profile_id = 129
+modified = 384
+replied_id = 1
+
+[reply__keys]
+notice_id = K
+profile_id = K
+
+[sms_carrier]
+id = 129
+name = 2
+email_pattern = 130
+created = 142
+modified = 384
+
+[sms_carrier__keys]
+id = N
+
+[subscription]
+subscriber = 129
+subscribed = 129
+jabber = 17
+sms = 17
+token = 2
+secret = 2
+created = 142
+modified = 384
+
+[subscription__keys]
+subscriber = K
+subscribed = K
+
+[token]
+consumer_key = 130
+tok = 130
+secret = 130
+type = 145
+state = 17
+created = 142
+modified = 384
+
+[token__keys]
+consumer_key = K
+tok = K
+
+[user]
+id = 129
+nickname = 2
+password = 2
+email = 2
+incomingemail = 2
+emailnotifysub = 17
+emailnotifyfav = 17
+emailnotifynudge = 17
+emailnotifymsg = 17
+emailmicroid = 17
+language = 2
+timezone = 2
+emailpost = 17
+jabber = 2
+jabbernotify = 17
+jabberreplies = 17
+jabbermicroid = 17
+updatefrompresence = 17
+sms = 2
+carrier = 1
+smsnotify = 17
+smsreplies = 17
+smsemail = 2
+uri = 2
+autosubscribe = 17
+urlshorteningservice = 2
+inboxed = 17
+created = 142
+modified = 384
+
+[user__keys]
+id = K
+nickname = U
+email = U
+incomingemail = U
+jabber = U
+sms = U
+uri = U
+
+[user_openid]
+canonical = 130
+display = 130
+user_id = 129
+created = 142
+modified = 384
+
+[user_openid__keys]
+canonical = K
+display = U
diff --git a/_darcs/pristine/classes/laconica.links.ini b/_darcs/pristine/classes/laconica.links.ini
new file mode 100644
index 000000000..173b18726
--- /dev/null
+++ b/_darcs/pristine/classes/laconica.links.ini
@@ -0,0 +1,43 @@
+[avatar]
+profile_id = profile:id
+
+[user]
+id = profile:id
+carrier = sms_carrier:id
+
+[remote_profile]
+id = profile:id
+
+[notice]
+profile_id = profile:id
+reply_to = notice:id
+
+[reply]
+notice_id = notice:id
+profile_id = profile:id
+
+[token]
+consumer_key = consumer:consumer_key
+
+[nonce]
+consumer_key,token = token:consumer_key,token
+
+[user_openid]
+user_id = user:id
+
+[confirm_address]
+user_id = user:id
+
+[remember_me]
+user_id = user:id
+
+[queue_item]
+notice_id = notice:id
+
+[subscription]
+subscriber = profile:id
+subscribed = profile:id
+
+[fave]
+notice_id = notice:id
+user_id = user:id