diff options
author | Zach Copley <zach@status.net> | 2010-11-16 02:32:46 +0000 |
---|---|---|
committer | Zach Copley <zach@status.net> | 2010-11-16 02:32:46 +0000 |
commit | bd566b6f855a19efb1d74eeaa39ab4e621903b12 (patch) | |
tree | befc30688421cc1fc99b1aef7e64d45f08614cd2 /plugins | |
parent | ca4c0a160122d20f95877102f9712aee45c7afb8 (diff) | |
parent | eb0495d10719f2be86f8f97e82aaa1b8bf923c7d (diff) |
Merge branch '0.9.x' into facebook-upgrade
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/EmailSummary/EmailSummaryPlugin.php | 202 | ||||
-rw-r--r-- | plugins/EmailSummary/Email_summary_status.php | 167 | ||||
-rw-r--r-- | plugins/EmailSummary/sendemailsummary.php | 47 | ||||
-rw-r--r-- | plugins/EmailSummary/siteemailsummaryhandler.php | 96 | ||||
-rw-r--r-- | plugins/EmailSummary/useremailsummaryhandler.php | 226 | ||||
-rw-r--r-- | plugins/Mapstraction/MapstractionPlugin.php | 3 | ||||
-rw-r--r-- | plugins/ModPlus/ModPlusPlugin.php | 116 | ||||
-rw-r--r-- | plugins/ModPlus/modplus.css | 23 | ||||
-rw-r--r-- | plugins/ModPlus/modplus.js | 23 | ||||
-rw-r--r-- | plugins/ModPlus/remoteprofileaction.php | 106 | ||||
-rw-r--r-- | plugins/TwitterBridge/Notice_to_status.php | 7 | ||||
-rw-r--r-- | plugins/TwitterBridge/twitter.php | 32 | ||||
-rw-r--r-- | plugins/TwitterBridge/twitterimport.php | 17 |
13 files changed, 1063 insertions, 2 deletions
diff --git a/plugins/EmailSummary/EmailSummaryPlugin.php b/plugins/EmailSummary/EmailSummaryPlugin.php new file mode 100644 index 000000000..58c40e43c --- /dev/null +++ b/plugins/EmailSummary/EmailSummaryPlugin.php @@ -0,0 +1,202 @@ +<?php +/** + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2010, StatusNet, Inc. + * + * Sends an email summary of the inbox to users in the network + * + * PHP version 5 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Sample + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Plugin for sending email summaries to users + * + * @category Email + * @package StatusNet + * @author Brion Vibber <brionv@status.net> + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class EmailSummaryPlugin extends Plugin +{ + /** + * Database schema setup + * + * @return boolean hook value + */ + + function onCheckSchema() + { + $schema = Schema::get(); + + // For storing user-submitted flags on profiles + + $schema->ensureTable('email_summary_status', + array(new ColumnDef('user_id', 'integer', null, + false, 'PRI'), + new ColumnDef('send_summary', 'tinyint', null, + false, null, 1), + new ColumnDef('last_summary_id', 'integer', null, + true), + new ColumnDef('created', 'datetime', null, + false), + new ColumnDef('modified', 'datetime', null, + false), + ) + ); + return true; + } + + /** + * Load related modules when needed + * + * @param string $cls Name of the class to be loaded + * + * @return boolean hook value; true means continue processing, false means stop. + * + */ + + function onAutoload($cls) + { + $dir = dirname(__FILE__); + + switch ($cls) + { + case 'SiteEmailSummaryHandler': + case 'UserEmailSummaryHandler': + include_once $dir . '/'.strtolower($cls).'.php'; + return false; + case 'Email_summary_status': + include_once $dir . '/'.$cls.'.php'; + return false; + default: + return true; + } + } + + /** + * Version info for this plugin + * + * @param array &$versions array of version data + * + * @return boolean hook value; true means continue processing, false means stop. + * + */ + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'EmailSummary', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:EmailSummary', + 'rawdescription' => + _m('Send an email summary of the inbox to users.')); + return true; + } + + /** + * Register our queue handlers + * + * @param QueueManager $qm Current queue manager + * + * @return boolean hook value + */ + + function onEndInitializeQueueManager($qm) + { + $qm->connect('sitesum', 'SiteEmailSummaryHandler'); + $qm->connect('usersum', 'UserEmailSummaryHandler'); + return true; + } + + /** + * Add a checkbox to turn off email summaries + * + * @param Action $action Action being executed (emailsettings) + * + * @return boolean hook value + */ + + function onEndEmailFormData($action) + { + $user = common_current_user(); + + $action->elementStart('li'); + $action->checkbox('emailsummary', + // TRANS: Checkbox label in e-mail preferences form. + _('Send me a periodic summary of updates from my network.'), + Email_summary_status::getSendSummary($user->id)); + $action->elementEnd('li'); + return true; + } + + /** + * Add a checkbox to turn off email summaries + * + * @param Action $action Action being executed (emailsettings) + * + * @return boolean hook value + */ + + function onEndEmailSaveForm($action) + { + $sendSummary = $action->boolean('emailsummary'); + + $user = common_current_user(); + + if (!empty($user)) { + + $ess = Email_summary_status::staticGet('user_id', $user->id); + + if (empty($ess)) { + + $ess = new Email_summary_status(); + + $ess->user_id = $user->id; + $ess->send_summary = $sendSummary; + $ess->created = common_sql_now(); + $ess->modified = common_sql_now(); + + $ess->insert(); + + } else { + + $orig = clone($ess); + + $ess->send_summary = $sendSummary; + $ess->modified = common_sql_now(); + + $ess->update($orig); + } + } + + return true; + } +} diff --git a/plugins/EmailSummary/Email_summary_status.php b/plugins/EmailSummary/Email_summary_status.php new file mode 100644 index 000000000..5b5b231e3 --- /dev/null +++ b/plugins/EmailSummary/Email_summary_status.php @@ -0,0 +1,167 @@ +<?php +/** + * Data class for email summary status + * + * PHP version 5 + * + * @category Data + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2010, StatusNet, Inc. + * + * This program 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('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/classes/Memcached_DataObject.php'; + +/** + * Data class for email summaries + * + * Email summary information for users + * + * @category Action + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * @see DB_DataObject + */ + +class Email_summary_status extends Memcached_DataObject +{ + public $__table = 'email_summary_status'; // table name + public $user_id; // int(4) primary_key not_null + public $send_summary; // tinyint not_null + public $last_summary_id; // int(4) null + public $created; // datetime not_null + public $modified; // datetime not_null + + /** + * Get an instance by key + * + * @param string $k Key to use to lookup (usually 'user_id' for this class) + * @param mixed $v Value to lookup + * + * @return Email_summary_status object found, or null for no hits + * + */ + function staticGet($k, $v=null) + { + return Memcached_DataObject::staticGet('email_summary_status', $k, $v); + } + + /** + * return table definition for DB_DataObject + * + * DB_DataObject needs to know something about the table to manipulate + * instances. This method provides all the DB_DataObject needs to know. + * + * @return array array of column definitions + */ + + function table() + { + return array('user_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'send_summary' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'last_summary_id' => DB_DATAOBJECT_INT, + 'created' => DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL, + 'modified' => DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL); + } + + /** + * return key definitions for DB_DataObject + * + * @return array list of key field names + */ + + function keys() + { + return array_keys($this->keyTypes()); + } + + /** + * return key definitions for Memcached_DataObject + * + * Our caching system uses the same key definitions, but uses a different + * method to get them. This key information is used to store and clear + * cached data, so be sure to list any key that will be used for static + * lookups. + * + * @return array associative array of key definitions, field name to type: + * 'K' for primary key: for compound keys, add an entry for each component; + * 'U' for unique keys: compound keys are not well supported here. + */ + function keyTypes() + { + return array('user_id' => 'K'); + } + + /** + * Magic formula for non-autoincrementing integer primary keys + * + * @return array magic three-false array that stops auto-incrementing. + */ + + function sequenceKey() + { + return array(false, false, false); + } + + /** + * Helper function + * + * @param integer $user_id ID of the user to get a count for + * + * @return int flag for whether to send this user a summary email + */ + + static function getSendSummary($user_id) + { + $ess = Email_summary_status::staticGet('user_id', $user_id); + + if (!empty($ess)) { + return $ess->send_summary; + } else { + return 1; + } + } + + /** + * Get email summary status for a user + * + * @param integer $user_id ID of the user to get a count for + * + * @return Email_summary_status instance for this user, with count already incremented. + */ + + static function getLastSummaryID($user_id) + { + $ess = Email_summary_status::staticGet('user_id', $user_id); + + if (!empty($ess)) { + return $ess->last_summary_id; + } else { + return null; + } + } +} diff --git a/plugins/EmailSummary/sendemailsummary.php b/plugins/EmailSummary/sendemailsummary.php new file mode 100644 index 000000000..37bfdcfbd --- /dev/null +++ b/plugins/EmailSummary/sendemailsummary.php @@ -0,0 +1,47 @@ +#!/usr/bin/env php +<?php +/* + * StatusNet - a distributed open-source microblogging tool + * Copyright (C) 2010, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..')); + +$shortoptions = 'i:n:a'; +$longoptions = array('id=', 'nickname=', 'all'); + +$helptext = <<<END_OF_SENDEMAILSUMMARY_HELP +sendemailsummary.php [options] +Send an email summary of the inbox to users + + -i --id ID of user to send summary to + -n --nickname nickname of the user to send summary to + -a --all send summary to all users + +END_OF_SENDEMAILSUMMARY_HELP; + +require_once INSTALLDIR.'/scripts/commandline.inc'; + +$qm = QueueManager::get(); + +// enqueue summary for user or all users + +try { + $user = getUser(); + $qm->enqueue($user->id, 'usersum'); +} catch (NoUserArgumentException $nuae) { + $qm->enqueue(null, 'sitesum'); +} diff --git a/plugins/EmailSummary/siteemailsummaryhandler.php b/plugins/EmailSummary/siteemailsummaryhandler.php new file mode 100644 index 000000000..595c3267a --- /dev/null +++ b/plugins/EmailSummary/siteemailsummaryhandler.php @@ -0,0 +1,96 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * + * Handler for queue items of type 'sitesum', sends email summaries + * to all users on the site. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Sample + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * + * Handler for queue items of type 'sitesum', sends email summaries + * to all users on the site. + * + * @category Email + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class SiteEmailSummaryHandler extends QueueHandler +{ + + /** + * Return transport keyword which identifies items this queue handler + * services; must be defined for all subclasses. + * + * Must be 8 characters or less to fit in the queue_item database. + * ex "email", "jabber", "sms", "irc", ... + * + * @return string + */ + + function transport() + { + return 'sitesum'; + } + + /** + * Handle the site + * + * @param mixed $object + * @return boolean true on success, false on failure + */ + + function handle($object) + { + $qm = QueueManager::get(); + + try { + // Enqueue a summary for all users + + $user = new User(); + $user->find(); + + while ($user->fetch()) { + try { + $qm->enqueue($user->id, 'usersum'); + } catch (Exception $e) { + common_log(LOG_WARNING, $e->getMessage()); + continue; + } + } + } catch (Exception $e) { + common_log(LOG_WARNING, $e->getMessage()); + } + + return true; + } +} + diff --git a/plugins/EmailSummary/useremailsummaryhandler.php b/plugins/EmailSummary/useremailsummaryhandler.php new file mode 100644 index 000000000..b1ebd0c42 --- /dev/null +++ b/plugins/EmailSummary/useremailsummaryhandler.php @@ -0,0 +1,226 @@ +<?php +/** + * StatusNet - the distributed open-source microblogging tool + * + * Handler for queue items of type 'usersum', sends an email summaries + * to a particular user. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Sample + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Handler for queue items of type 'usersum', sends an email summaries + * to a particular user. + * + * @category Email + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class UserEmailSummaryHandler extends QueueHandler +{ + // Maximum number of notices to include by default. This is probably too much. + + const MAX_NOTICES = 200; + + /** + * Return transport keyword which identifies items this queue handler + * services; must be defined for all subclasses. + * + * Must be 8 characters or less to fit in the queue_item database. + * ex "email", "jabber", "sms", "irc", ... + * + * @return string + */ + + function transport() + { + return 'sitesum'; + } + + /** + * Send a summary email to the user + * + * @param mixed $object + * @return boolean true on success, false on failure + */ + + function handle($user_id) + { + // Skip if they've asked not to get summaries + + $ess = Email_summary_status::staticGet('user_id', $user_id); + + if (!empty($ess) && !$ess->send_summary) { + common_log(LOG_INFO, sprintf('Not sending email summary for user %s by request.', $user_id)); + return true; + } + + $since_id = null; + + if (!empty($ess)) { + $since_id = $ess->last_summary_id; + } + + $user = User::staticGet('id', $user_id); + + if (empty($user)) { + common_log(LOG_INFO, sprintf('Not sending email summary for user %s; no such user.', $user_id)); + return true; + } + + if (empty($user->email)) { + common_log(LOG_INFO, sprintf('Not sending email summary for user %s; no email address.', $user_id)); + return true; + } + + $profile = $user->getProfile(); + + if (empty($profile)) { + common_log(LOG_WARNING, sprintf('Not sending email summary for user %s; no profile.', $user_id)); + return true; + } + + $notice = $user->ownFriendsTimeline(0, self::MAX_NOTICES, $since_id); + + if (empty($notice) || $notice->N == 0) { + common_log(LOG_WARNING, sprintf('Not sending email summary for user %s; no notices.', $user_id)); + return true; + } + + // XXX: This is risky fingerpoken in der objektvars, but I didn't feel like + // figuring out a better way. -ESP + + $new_top = null; + + if ($notice instanceof ArrayWrapper) { + $new_top = $notice->_items[0]->id; + } + + $out = new XMLStringer(); + + $out->raw(sprintf(_('<p>Recent updates from %1s for %2s:</p>'), + common_config('site', 'name'), + $profile->getBestName())); + + + $out->elementStart('table', array('width' => '541px', 'style' => 'border: none')); + + while ($notice->fetch()) { + + $profile = Profile::staticGet('id', $notice->profile_id); + + if (empty($profile)) { + continue; + } + + $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); + + $out->elementStart('tr'); + $out->elementStart('td', array('width' => AVATAR_STREAM_SIZE, + 'height' => AVATAR_STREAM_SIZE, + 'align' => 'left', + 'valign' => 'top')); + $out->element('img', array('src' => ($avatar) ? + $avatar->displayUrl() : + Avatar::defaultImage($avatar_size), + 'class' => 'avatar photo', + 'width' => AVATAR_STREAM_SIZE, + 'height' => AVATAR_STREAM_SIZE, + 'alt' => $profile->getBestName())); + $out->elementEnd('td'); + $out->elementStart('td', array('align' => 'left', + 'valign' => 'top')); + $out->element('a', array('href' => $profile->profileurl), + $profile->nickname); + $out->text(' '); + $out->raw($notice->rendered); + $out->element('br'); // yeah, you know it. I just wrote a <br> in the middle of my table layout. + $noticeurl = $notice->bestUrl(); + // above should always return an URL + assert(!empty($noticeurl)); + $out->elementStart('a', array('rel' => 'bookmark', + 'class' => 'timestamp', + 'href' => $noticeurl)); + $dt = common_date_iso8601($notice->created); + $out->element('abbr', array('class' => 'published', + 'title' => $dt), + common_date_string($notice->created)); + $out->elementEnd('a'); + if ($notice->hasConversation()) { + $conv = Conversation::staticGet('id', $notice->conversation); + $convurl = $conv->uri; + if (!empty($convurl)) { + $out->text(' '); + $out->element('a', + array('href' => $convurl.'#notice-'.$notice->id, + 'class' => 'response'), + _('in context')); + } + } + $out->elementEnd('td'); + $out->elementEnd('tr'); + } + + $out->elementEnd('table'); + + $out->raw(sprintf(_('<p><a href="%1s">change your email settings for %2s</a></p>'), + common_local_url('emailsettings'), + common_config('site', 'name'))); + + $body = $out->getString(); + + // FIXME: do something for people who don't like HTML email + + mail_to_user($user, _('Updates from your network'), $body, + array('Content-Type' => 'text/html; charset=UTF-8')); + + if (empty($ess)) { + + $ess = new Email_summary_status(); + + $ess->user_id = $user_id; + $ess->created = common_sql_now(); + $ess->last_summary_id = $new_top; + $ess->modified = common_sql_now(); + + $ess->insert(); + + } else { + + $orig = clone($ess); + + $ess->last_summary_id = $new_top; + $ess->modified = common_sql_now(); + + $ess->update($orig); + } + + return true; + } +} diff --git a/plugins/Mapstraction/MapstractionPlugin.php b/plugins/Mapstraction/MapstractionPlugin.php index c4ba6464e..d5261d8bc 100644 --- a/plugins/Mapstraction/MapstractionPlugin.php +++ b/plugins/Mapstraction/MapstractionPlugin.php @@ -156,7 +156,8 @@ class MapstractionPlugin extends Plugin ' var user = null; '. (($actionName == 'showstream') ? ' user = scrapeUser(); ' : '') . ' var notices = scrapeNotices(user); ' . - ' showMapstraction($("#map_canvas"), notices); '. + ' var canvas = $("#map_canvas")[0]; ' . + ' if (typeof(canvas) != "undefined") { showMapstraction(canvas, notices); } '. '});'); } diff --git a/plugins/ModPlus/ModPlusPlugin.php b/plugins/ModPlus/ModPlusPlugin.php new file mode 100644 index 000000000..3e7a8c745 --- /dev/null +++ b/plugins/ModPlus/ModPlusPlugin.php @@ -0,0 +1,116 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2010, StatusNet, Inc. + * + * This program 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('STATUSNET')) { + exit(1); +} + +/** + * Some UI extras for now... + * + * @package ModPlusPlugin + * @maintainer Brion Vibber <brion@status.net> + */ +class ModPlusPlugin extends Plugin +{ + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'ModPlus', + 'version' => STATUSNET_VERSION, + 'author' => 'Brion Vibber', + 'homepage' => 'http://status.net/wiki/Plugin:ModPlus', + 'rawdescription' => + _m('UI extensions for profile moderation actions.')); + + return true; + } + + /** + * Load JS at runtime if we're logged in. + * + * @param Action $action + * @return boolean hook result + */ + function onEndShowScripts($action) + { + $user = common_current_user(); + if ($user) { + $action->script('plugins/ModPlus/modplus.js'); + } + return true; + } + + function onEndShowStatusNetStyles($action) { + $action->cssLink('plugins/ModPlus/modplus.css'); + return true; + } + + /** + * Autoloader + * + * Loads our classes if they're requested. + * + * @param string $cls Class requested + * + * @return boolean hook return + */ + function onAutoload($cls) + { + switch ($cls) + { + case 'RemoteprofileAction': + case 'RemoteProfileAction': + require_once dirname(__FILE__) . '/remoteprofileaction.php'; + return false; + default: + return true; + } + } + + /** + * Add OpenID-related paths to the router table + * + * Hook for RouterInitialized event. + * + * @param Net_URL_Mapper $m URL mapper + * + * @return boolean hook return + */ + function onStartInitializeRouter($m) + { + $m->connect('user/remote/:id', + array('action' => 'remoteprofile'), + array('id' => '[\d]+')); + + return true; + } + + function onStartShowNoticeItem($item) + { + $profile = $item->profile; + $isRemote = !(User::staticGet('id', $profile->id)); + if ($isRemote) { + $target = common_local_url('remoteprofile', array('id' => $profile->id)); + $label = _m('Remote profile options...'); + $item->out->elementStart('div', 'remote-profile-options'); + $item->out->element('a', array('href' => $target), $label); + $item->out->elementEnd('div'); + } + } +} diff --git a/plugins/ModPlus/modplus.css b/plugins/ModPlus/modplus.css new file mode 100644 index 000000000..8d2fc8fba --- /dev/null +++ b/plugins/ModPlus/modplus.css @@ -0,0 +1,23 @@ +.remote-profile-options { + position: absolute; + z-index: 999; + + background: url(../../theme/base/images/icons/twotone/green/admin.gif) no-repeat 8px 8px white; + border: solid 1px #c0c0c0; + + margin-top: 56px; + + padding: 6px 16px; + padding-left: 32px; + + -moz-border-radius: 8px; + -webkit-border-radius: 8px; + -msie-border-radius: 8px; + border-radius: 8px; + + box-shadow:3px 3px 7px rgba(194, 194, 194, 0.3); + -moz-box-shadow:3px 3px 7px rgba(194, 194, 194, 0.3); + -webkit-box-shadow:3px 3px 7px rgba(194, 194, 194, 0.3); + + display: none; +} diff --git a/plugins/ModPlus/modplus.js b/plugins/ModPlus/modplus.js new file mode 100644 index 000000000..2e90de4f1 --- /dev/null +++ b/plugins/ModPlus/modplus.js @@ -0,0 +1,23 @@ +/** + * modplus.js + * (c) 2010 StatusNet, Inc + */ + +$(function() { + function ModPlus_setup(notice) { + if ($(notice).find('.remote-profile-options').size()) { + var $options = $(notice).find('.remote-profile-options'); + $options.prepend($()) + $(notice).find('.author').mouseenter(function(event) { + $(notice).find('.remote-profile-options').fadeIn(); + }); + $(notice).mouseleave(function(event) { + $(notice).find('.remote-profile-options').fadeOut(); + }); + } + } + + $('.notice').each(function() { + ModPlus_setup(this); + }); +}); diff --git a/plugins/ModPlus/remoteprofileaction.php b/plugins/ModPlus/remoteprofileaction.php new file mode 100644 index 000000000..caa5e6fbf --- /dev/null +++ b/plugins/ModPlus/remoteprofileaction.php @@ -0,0 +1,106 @@ +<?php +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +class RemoteProfileAction extends ShowstreamAction +{ + function prepare($args) + { + OwnerDesignAction::prepare($args); // skip the ProfileAction code and replace it... + + $id = $this->arg('id'); + $this->user = false; + $this->profile = Profile::staticGet('id', $id); + + if (!$this->profile) { + $this->serverError(_('User has no profile.')); + return false; + } + + $user = User::staticGet('id', $this->profile->id); + if ($user) { + // This is a local user -- send to their regular profile. + $url = common_local_url('showstream', array('nickname' => $user->nickname)); + common_redirect($url); + return false; + } + + $this->tag = $this->trimmed('tag'); + $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; + common_set_returnto($this->selfUrl()); + return true; + } + + function handle($args) + { + // skip yadis thingy + $this->showPage(); + } + + function title() + { + // maybe fixed in 0.9.x + if (!empty($this->profile->fullname)) { + $base = $this->profile->fullname . ' (' . $this->profile->nickname . ') '; + } else { + $base = $this->profile->nickname; + } + $host = parse_url($this->profile->profileurl, PHP_URL_HOST); + return sprintf(_m('%s on %s'), $base, $host); + } + + /** + * Instead of showing notices, link to the original offsite profile. + */ + function showNotices() + { + $url = $this->profile->profileurl; + $host = parse_url($url, PHP_URL_HOST); + $markdown = sprintf( + _m('This remote profile is registered on another site; see [%s\'s original profile page on %s](%s).'), + $this->profile->nickname, + $host, + $url); + $html = common_markup_to_html($markdown); + $this->raw($html); + + if ($this->profile->hasRole(Profile_role::SILENCED)) { + $markdown = _m('Site moderators have silenced this profile, which prevents delivery of new messages to any users on this site.'); + $this->raw(common_markup_to_html($markdown)); + } + } + + function getFeeds() + { + // none + } + + /** + * Don't do various extra stuff, and also trim some things to avoid crawlers. + */ + function extraHead() + { + $this->element('meta', array('name' => 'robots', + 'content' => 'noindex,nofollow')); + } + + function showLocalNav() + { + $nav = new PublicGroupNav($this); + $nav->show(); + } + + function showSections() + { + ProfileAction::showSections(); + // skip tag cloud + } + + function showStatistics() + { + // skip + } + +}
\ No newline at end of file diff --git a/plugins/TwitterBridge/Notice_to_status.php b/plugins/TwitterBridge/Notice_to_status.php index 2e32ba963..3b8f816cf 100644 --- a/plugins/TwitterBridge/Notice_to_status.php +++ b/plugins/TwitterBridge/Notice_to_status.php @@ -144,6 +144,7 @@ class Notice_to_status extends Memcached_DataObject /** * Save a mapping between a notice and a status + * Warning: status_id values may not fit in 32-bit integers. * * @param integer $notice_id ID of the notice in StatusNet * @param integer $status_id ID of the status in Twitter @@ -153,12 +154,18 @@ class Notice_to_status extends Memcached_DataObject static function saveNew($notice_id, $status_id) { + if (empty($notice_id)) { + throw new Exception("Invalid notice_id $notice_id"); + } $n2s = Notice_to_status::staticGet('notice_id', $notice_id); if (!empty($n2s)) { return $n2s; } + if (empty($status_id)) { + throw new Exception("Invalid status_id $status_id"); + } $n2s = Notice_to_status::staticGet('status_id', $status_id); if (!empty($n2s)) { diff --git a/plugins/TwitterBridge/twitter.php b/plugins/TwitterBridge/twitter.php index cd1ad70b9..b34488069 100644 --- a/plugins/TwitterBridge/twitter.php +++ b/plugins/TwitterBridge/twitter.php @@ -128,6 +128,16 @@ function is_twitter_notice($id) return (!empty($n2s)); } +/** + * Check if we need to broadcast a notice over the Twitter bridge, and + * do so if necessary. Will determine whether to do a straight post or + * a repeat/retweet + * + * This function is meant to be called directly from TwitterQueueHandler. + * + * @param Notice $notice + * @return boolean true if complete or successful, false if we should retry + */ function broadcast_twitter($notice) { $flink = Foreign_link::getByUserID($notice->profile_id, @@ -137,8 +147,13 @@ function broadcast_twitter($notice) if (!empty($flink) && TwitterOAuthClient::isPackedToken($flink->credentials)) { if (!empty($notice->repeat_of) && is_twitter_notice($notice->repeat_of)) { $retweet = retweet_notice($flink, Notice::staticGet('id', $notice->repeat_of)); - if (!empty($retweet)) { + if (is_object($retweet)) { Notice_to_status::saveNew($notice->id, $retweet->id); + return true; + } else { + // Our error processing will have decided if we need to requeue + // this or can discard safely. + return $retweet; } } else if (is_twitter_bound($notice, $flink)) { return broadcast_oauth($notice, $flink); @@ -148,6 +163,21 @@ function broadcast_twitter($notice) return true; } +/** + * Send a retweet to Twitter for a notice that has been previously bridged + * in or out. + * + * Warning: the return value is not guaranteed to be an object; some error + * conditions will return a 'true' which should be passed on to a calling + * queue handler. + * + * No local information about the resulting retweet is saved: it's up to + * caller to save new mappings etc if appropriate. + * + * @param Foreign_link $flink + * @param Notice $notice + * @return mixed object with resulting Twitter status data on success, or true/false/null on error conditions. + */ function retweet_notice($flink, $notice) { $token = TwitterOAuthClient::unpackToken($flink->credentials); diff --git a/plugins/TwitterBridge/twitterimport.php b/plugins/TwitterBridge/twitterimport.php index 07a9cf95f..498e9b1fc 100644 --- a/plugins/TwitterBridge/twitterimport.php +++ b/plugins/TwitterBridge/twitterimport.php @@ -189,6 +189,7 @@ class TwitterImport Notice_to_status::saveNew($notice->id, $status->id); $this->saveStatusMentions($notice, $status); + $this->saveStatusAttachments($notice, $status); $notice->blowOnInsert(); @@ -648,4 +649,20 @@ class TwitterImport } } } + + /** + * Record URL links from the notice. Needed to get thumbnail records + * for referenced photo and video posts, etc. + * + * @param Notice $notice + * @param object $status + */ + function saveStatusAttachments($notice, $status) + { + if (!empty($status->entities) && !empty($status->entities->urls)) { + foreach ($status->entities->urls as $url) { + File::processNew($url->url, $notice->id); + } + } + } }
\ No newline at end of file |