diff --git a/EVENTS.txt b/EVENTS.txt index cad93a7121..2496416173 100644 --- a/EVENTS.txt +++ b/EVENTS.txt @@ -258,10 +258,28 @@ EndShowExportData: just after showing the
with export data (feeds) - $action: action object being shown StartShowNoticeItem: just before showing the notice item -- $action: action object being shown +- $item: The NoticeListItem object being shown EndShowNoticeItem: just after showing the notice item -- $action: action object being shown +- $item: the NoticeListItem object being shown + +StartShowNoticeInfo: just before showing notice info +- $item: The NoticeListItem object being shown + +EndShowNoticeInfo: just after showing notice info +- $item: The NoticeListItem object being shown + +StartShowNoticeOptions: just before showing notice options like fave, repeat, etc. +- $item: the NoticeListItem object being shown + +EndShowNoticeOptions: just after showing notice options like fave, repeat, etc. +- $item: the NoticeListItem object being shown + +StartShowFaveForm: just before showing the fave form +- $item: the NoticeListItem object being shown + +EndShowFaveForm: just after showing the fave form +- $item: the NoticeListItem object being shown StartShowPageNotice: just before showing the page notice (instructions or error) - $action: action object being shown @@ -756,6 +774,22 @@ EndDisfavorNotice: After saving a notice as a favorite - $profile: profile of the person faving (can be remote!) - $notice: notice being faved +StartFavorNoticeForm: starting the data in the form for favoring a notice +- $FavorForm: the favor form being shown +- $notice: notice being favored + +EndFavorNoticeForm: Ending the data in the form for favoring a notice +- $FavorForm: the favor form being shown +- $notice: notice being favored + +StartDisFavorNoticeForm: starting the data in the form for disfavoring a notice +- $DisfavorForm: the disfavor form being shown +- $notice: notice being difavored + +EndDisFavorNoticeForm: Ending the data in the form for disfavoring a notice +- $DisfavorForm: the disfavor form being shown +- $notice: notice being disfavored + StartFindMentions: start finding mentions in a block of text - $sender: sender profile - $text: plain text version of the notice diff --git a/actions/shownotice.php b/actions/shownotice.php index 86df5f9f30..c8e9cfe66a 100644 --- a/actions/shownotice.php +++ b/actions/shownotice.php @@ -314,10 +314,14 @@ class SingleNoticeItem extends NoticeListItem function show() { $this->showStart(); - $this->showNotice(); - $this->showNoticeAttachments(); - $this->showNoticeInfo(); - $this->showNoticeOptions(); + if (Event::handle('StartShowNoticeItem', array($this))) { + $this->showNotice(); + $this->showNoticeAttachments(); + $this->showNoticeInfo(); + $this->showNoticeOptions(); + Event::handle('EndShowNoticeItem', array($this)); + } + $this->showEnd(); } diff --git a/classes/Profile.php b/classes/Profile.php index 3a381fcc89..3844077e62 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -473,6 +473,41 @@ class Profile extends Memcached_DataObject return $cnt; } + function hasFave($notice) + { + $cache = common_memcache(); + + // XXX: Kind of a hack. + + if (!empty($cache)) { + // This is the stream of favorite notices, in rev chron + // order. This forces it into cache. + + $ids = Fave::stream($this->id, 0, NOTICE_CACHE_WINDOW); + + // If it's in the list, then it's a fave + + if (in_array($notice->id, $ids)) { + return true; + } + + // 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 (count($ids) < 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 faveCount() { $c = common_memcache(); @@ -516,6 +551,20 @@ class Profile extends Memcached_DataObject return $cnt; } + 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('fave:ids_by_user:'.$this->id)); + $cache->delete(common_cache_key('fave:ids_by_user:'.$this->id.';last')); + $cache->delete(common_cache_key('fave:ids_by_user_own:'.$this->id)); + $cache->delete(common_cache_key('fave:ids_by_user_own:'.$this->id.';last')); + } + $this->blowFaveCount(); + } + function blowSubscriberCount() { $c = common_memcache(); diff --git a/classes/User.php b/classes/User.php index b85192b29c..e784fd9e9a 100644 --- a/classes/User.php +++ b/classes/User.php @@ -412,37 +412,8 @@ class User extends Memcached_DataObject 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. - - $ids = Fave::stream($this->id, 0, NOTICE_CACHE_WINDOW); - - // If it's in the list, then it's a fave - - if (in_array($notice->id, $ids)) { - return true; - } - - // 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 (count($ids) < 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); + $profile = $this->getProfile(); + return $profile->hasFave($notice); } function mutuallySubscribed($other) @@ -511,17 +482,8 @@ class User extends Memcached_DataObject 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('fave:ids_by_user:'.$this->id)); - $cache->delete(common_cache_key('fave:ids_by_user:'.$this->id.';last')); - $cache->delete(common_cache_key('fave:ids_by_user_own:'.$this->id)); - $cache->delete(common_cache_key('fave:ids_by_user_own:'.$this->id.';last')); - } $profile = $this->getProfile(); - $profile->blowFaveCount(); + $profile->blowFavesCache(); } function getSelfTags() diff --git a/lib/disfavorform.php b/lib/disfavorform.php index 5b135b38ad..6023766d7b 100644 --- a/lib/disfavorform.php +++ b/lib/disfavorform.php @@ -123,9 +123,13 @@ class DisfavorForm extends Form function formData() { - $this->out->hidden('notice-n'.$this->notice->id, - $this->notice->id, - 'notice'); + if (Event::handle('StartDisFavorNoticeForm', array($this, $this->notice))) { + $this->out->hidden('notice-n'.$this->notice->id, + $this->notice->id, + 'notice'); + Event::handle('EndDisFavorNoticeForm', array($this, $this->notice)); + } + } /** diff --git a/lib/favorform.php b/lib/favorform.php index 625df7c8b5..4e2891ffd5 100644 --- a/lib/favorform.php +++ b/lib/favorform.php @@ -123,9 +123,12 @@ class FavorForm extends Form function formData() { - $this->out->hidden('notice-n'.$this->notice->id, - $this->notice->id, - 'notice'); + if (Event::handle('StartFavorNoticeForm', array($this, $this->notice))) { + $this->out->hidden('notice-n'.$this->notice->id, + $this->notice->id, + 'notice'); + Event::handle('EndFavorNoticeForm', array($this, $this->notice)); + } } /** diff --git a/lib/noticelist.php b/lib/noticelist.php index 529d6a3f90..df1533980a 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -226,24 +226,31 @@ class NoticeListItem extends Widget function showNoticeInfo() { $this->out->elementStart('div', 'entry-content'); - $this->showNoticeLink(); - $this->showNoticeSource(); - $this->showNoticeLocation(); - $this->showContext(); - $this->showRepeat(); + if (Event::handle('StartShowNoticeInfo', array($this))) { + $this->showNoticeLink(); + $this->showNoticeSource(); + $this->showNoticeLocation(); + $this->showContext(); + $this->showRepeat(); + Event::handle('EndShowNoticeInfo', array($this)); + } + $this->out->elementEnd('div'); } function showNoticeOptions() { - $user = common_current_user(); - if ($user) { - $this->out->elementStart('div', 'notice-options'); - $this->showFaveForm(); - $this->showReplyLink(); - $this->showRepeatForm(); - $this->showDeleteLink(); - $this->out->elementEnd('div'); + if (Event::handle('StartShowNoticeOptions', array($this))) { + $user = common_current_user(); + if ($user) { + $this->out->elementStart('div', 'notice-options'); + $this->showFaveForm(); + $this->showReplyLink(); + $this->showRepeatForm(); + $this->showDeleteLink(); + $this->out->elementEnd('div'); + } + Event::handle('EndShowNoticeOptions', array($this)); } } @@ -270,15 +277,18 @@ class NoticeListItem extends Widget function showFaveForm() { - $user = common_current_user(); - if ($user) { - if ($user->hasFave($this->notice)) { - $disfavor = new DisfavorForm($this->out, $this->notice); - $disfavor->show(); - } else { - $favor = new FavorForm($this->out, $this->notice); - $favor->show(); + if (Event::handle('StartShowFaveForm', array($this))) { + $user = common_current_user(); + if ($user) { + if ($user->hasFave($this->notice)) { + $disfavor = new DisfavorForm($this->out, $this->notice); + $disfavor->show(); + } else { + $favor = new FavorForm($this->out, $this->notice); + $favor->show(); + } } + Event::handle('EndShowFaveForm', array($this)); } } diff --git a/plugins/AnonymousFave/AnonymousFavePlugin.php b/plugins/AnonymousFave/AnonymousFavePlugin.php new file mode 100644 index 0000000000..72093e7f7e --- /dev/null +++ b/plugins/AnonymousFave/AnonymousFavePlugin.php @@ -0,0 +1,287 @@ +. + * + * @category Plugin + * @package StatusNet + * @author Zach Copley + * @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')) { + // This check helps protect against security problems; + // your code file can't be executed directly from the web. + exit(1); +} + +define('ANONYMOUS_FAVE_PLUGIN_VERSION', '0.1'); + +/** + * Anonymous Fave plugin to allow anonymous (not logged in) users + * to favorite notices + * + * @category Plugin + * @package StatusNet + * @author Zach Copley + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ +class AnonymousFavePlugin extends Plugin { + + function onArgsInitialize() { + // We always want a session because we're tracking anon users + common_ensure_session(); + } + + /** + * Hook for ensuring our tables are created + * + * Ensures the fave_tally table is there and has the right columns + * + * @return boolean hook return + */ + + function onCheckSchema() + { + $schema = Schema::get(); + + // For storing total number of times a notice has been faved + + $schema->ensureTable('fave_tally', + array( + new ColumnDef('notice_id', 'integer', null, false, 'PRI'), + new ColumnDef('count', 'integer', null, false), + new ColumnDef( + 'modified', + 'timestamp', + null, + false, + null, + 'CURRENT_TIMESTAMP', + 'on update CURRENT_TIMESTAMP' + ) + ) + ); + + return true; + } + + function onEndShowHTML($action) + { + if (!common_logged_in()) { + // Set a place to return to when submitting forms + common_set_returnto($action->selfUrl()); + } + } + + function onEndShowScripts($action) + { + // Setup ajax calls for favoriting. Usually this is only done when + // a user is logged in. + $action->inlineScript('SN.U.NoticeFavor();'); + } + + function onAutoload($cls) + { + $dir = dirname(__FILE__); + + switch ($cls) { + case 'Fave_tally': + include_once $dir . '/' . $cls . '.php'; + return false; + case 'AnonFavorAction': + include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php'; + return false; + case 'AnonDisFavorAction': + include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php'; + return false; + case 'AnonFavorForm': + include_once $dir . '/anonfavorform.php'; + return false; + case 'AnonDisFavorForm': + include_once $dir . '/anondisfavorform.php'; + return false; + default: + return true; + } + } + + function onStartInitializeRouter($m) { + + $m->connect('main/anonfavor', array('action' => 'AnonFavor')); + $m->connect('main/anondisfavor', array('action' => 'AnonDisFavor')); + + return true; + } + + function onStartShowNoticeOptions($item) { + + if (!common_logged_in()) { + $item->out->elementStart('div', 'notice-options'); + $item->showFaveForm(); + $item->out->elementEnd('div'); + } + + return true; + } + + function onStartShowFaveForm($item) { + + if (!common_logged_in()) { + + $profile = AnonymousFavePlugin::getAnonProfile(); + if (!empty($profile)) { + if ($profile->hasFave($item->notice)) { + $disfavor = new AnonDisFavorForm($item->out, $item->notice); + $disfavor->show(); + } else { + $favor = new AnonFavorForm($item->out, $item->notice); + $favor->show(); + } + } + } + + return true; + } + + function onEndFavorNoticeForm($form, $notice) + { + $this->showTally($form->out, $notice); + } + + function onEndDisFavorNoticeForm($form, $notice) + { + $this->showTally($form->out, $notice); + } + + function showTally($out, $notice) + { + $tally = Fave_tally::ensureTally($notice->id); + + if (!empty($tally)) { + $out->elementStart( + 'div', + array( + 'id' => 'notice-' . $notice->id . '-tally', + 'class' => 'notice-tally' + ) + ); + $out->raw(sprintf(_m("favored %d times"), $tally->count)); + $out->elementEnd('div'); + } + } + + function onEndFavorNotice($profile, $notice) + { + $tally = Fave_tally::increment($notice->id); + } + + function onEndDisfavorNotice($profile, $notice) + { + $tally = Fave_tally::decrement($notice->id); + } + + static function createAnonProfile() { + + // Get the anon user's IP, and turn it into a nickname + list($proxy, $ip) = common_client_ip(); + + // IP + time + random number should help to avoid collisions + $baseNickname = $ip . '-' . time() . '-' . common_good_rand(5); + + $profile = new Profile(); + $profile->nickname = $baseNickname; + $id = $profile->insert(); + + if (!$id) { + throw new ServerException(_m("Couldn't create anonymous user session.")); + } + + // Stick the Profile ID into the nickname + $orig = clone($profile); + + $profile->nickname = 'anon-' . $id . '-' . $baseNickname; + $result = $profile->update($orig); + + if (!$result) { + throw new ServerException(_m("Couldn't create anonymous user session.")); + } + + common_log( + LOG_INFO, + "AnonymousFavePlugin - created profile for anonymous user from IP: " + . $ip + . ', nickname = ' + . $profile->nickname + ); + + return $profile; + } + + static function getAnonProfile() { + + $token = $_SESSION['anon_token']; + $anon = base64_decode($token); + + $profile = null; + + if (!empty($anon) && substr($anon, 0, 5) == 'anon-') { + $parts = explode('-', $anon); + $id = $parts[1]; + // Do Profile lookup by ID instead of nickname for safety/performance + $profile = Profile::staticGet('id', $id); + } else { + $profile = AnonymousFavePlugin::createAnonProfile(); + // Obfuscate so it's hard to figure out the Profile ID + $_SESSION['anon_token'] = base64_encode($profile->nickname); + } + + return $profile; + } + + /** + * Provide plugin version information. + * + * This data is used when showing the version page. + * + * @param array &$versions array of version data arrays; see EVENTS.txt + * + * @return boolean hook value + */ + function onPluginVersion(&$versions) + { + $url = 'http://status.net/wiki/Plugin:AnonymousFave'; + + $versions[] = array('name' => 'AnonymousFave', + 'version' => ANONYMOUS_FAVE_PLUGIN_VERSION, + 'author' => 'Zach Copley', + 'homepage' => $url, + 'rawdescription' => + _m('Allow anonymous users to favorite notices.')); + + return true; + } + +} diff --git a/plugins/AnonymousFave/Fave_tally.php b/plugins/AnonymousFave/Fave_tally.php new file mode 100644 index 0000000000..b350d5a0a5 --- /dev/null +++ b/plugins/AnonymousFave/Fave_tally.php @@ -0,0 +1,244 @@ + + * @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 . + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/classes/Memcached_DataObject.php'; + +/** + * Data class for favorites tally + * + * A class representing a total number of times a notice has been favored + * + * @category Action + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + */ + +class Fave_tally extends Memcached_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'fave_tally'; // table name + public $notice_id; // int(4) primary_key not_null + public $count; // int(4) not_null + public $modified; // datetime not_null default_0000-00-00%2000%3A00%3A00 + + /* Static get */ + function staticGet($k, $v = NULL) { return Memcached_DataObject::staticGet('Fave_tally', $k, $v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE + + /** + * return table definition for DB_DataObject + * + * @return array array of column definitions + */ + + function table() + { + return array( + 'notice_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'count' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'modified' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL + ); + } + + /** + * return key definitions for DB_DataObject + * + * DB_DataObject needs to know about keys that the table has, since it + * won't appear in StatusNet's own keys list. In most cases, this will + * simply reference your keyTypes() function. + * + * @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('notice_id' => 'K'); + } + + /** + * Magic formula for non-autoincrementing integer primary keys + * + * If a table has a single integer column as its primary key, DB_DataObject + * assumes that the column is auto-incrementing and makes a sequence table + * to do this incrementation. Since we don't need this for our class, we + * overload this method and return the magic formula that DB_DataObject needs. + * + * @return array magic three-false array that stops auto-incrementing. + */ + + + function sequenceKey() + { + return array(false, false, false); + } + + /** + * Get a single object with multiple keys + * + * @param array $kv Map of key-value pairs + * + * @return User_flag_profile found object or null + */ + + function pkeyGet($kv) + { + return Memcached_DataObject::pkeyGet('Fave_tally', $kv); + } + + /** + * Increment a notice's tally + * + * @param integer $noticeID ID of notice we're tallying + * + * @return Fave_tally $tally the tally data object + */ + + static function increment($noticeID) + { + $tally = Fave_tally::ensureTally($noticeID); + + $orig = clone($tally); + $tally->count++; + $result = $tally->update($orig); + + if (!$result) { + $msg = sprintf( + _m("Couldn't update favorite tally for notice ID %d."), + $noticeID + ); + throw new ServerException($msg); + } + + return $tally; + } + + /** + * Decrement a notice's tally + * + * @param integer $noticeID ID of notice we're tallying + * + * @return Fave_tally $tally the tally data object + */ + + static function decrement($noticeID) + { + $tally = Fave_tally::ensureTally($noticeID); + + if ($tally->count > 0) { + $orig = clone($tally); + $tally->count--; + $result = $tally->update($orig); + + if (!$result) { + $msg = sprintf( + _m("Couldn't update favorite tally for notice ID %d."), + $noticeID + ); + throw new ServerException($msg); + } + } + + return $tally; + } + + /** + * Ensure a tally exists for a given notice. If we can't find + * one create one with the total number of existing faves + * + * @param integer $noticeID + * + * @return Fave_tally the tally data object + */ + + static function ensureTally($noticeID) + { + $tally = Fave_tally::staticGet('notice_id', $noticeID); + + if (!$tally) { + $tally = new Fave_tally(); + $tally->notice_id = $noticeID; + $tally->count = Fave_tally::countExistingFaves($noticeID); + $result = $tally->insert(); + if (!$result) { + $msg = sprintf( + _m("Couldn't create favorite tally for notice ID %d."), + $noticeID + ); + throw new ServerException($msg); + } + } + + return $tally; + } + + /** + * Count the number of faves a notice already has. Used to initalize + * a tally for a notice. + * + * @param integer $noticeID ID of the notice to count faves for + * + * @return integer $total total number of time the notice has been favored + */ + + static function countExistingFaves($noticeID) + { + $fave = new Fave(); + $fave->notice_id = $noticeID; + $total = $fave->count(); + return $total; + } +} diff --git a/plugins/AnonymousFave/anondisfavor.php b/plugins/AnonymousFave/anondisfavor.php new file mode 100644 index 0000000000..f39d5a7780 --- /dev/null +++ b/plugins/AnonymousFave/anondisfavor.php @@ -0,0 +1,124 @@ + + * @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 . + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Anonymous disfavor class + * + * @category Action + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + */ +class AnonDisfavorAction extends RedirectingAction +{ + /** + * Class handler. + * + * @param array $args query arguments + * + * @return void + */ + function handle($args) + { + parent::handle($args); + + $profile = AnonymousFavePlugin::getAnonProfile(); + + if (empty($profile) || $_SERVER['REQUEST_METHOD'] != 'POST') { + $this->clientError( + _m('Could not disfavor notice! Please make sure your browser has cookies enabled.') + ); + return; + } + + $id = $this->trimmed('notice'); + $notice = Notice::staticGet($id); + $token = $this->trimmed('token-' . $notice->id); + + if (!$token || $token != common_session_token()) { + $this->clientError(_m('There was a problem with your session token. Try again, please.')); + return; + } + + $fave = new Fave(); + $fave->user_id = $profile->id; + $fave->notice_id = $notice->id; + + if (!$fave->find(true)) { + $this->clientError(_m('This notice is not a favorite!')); + return; + } + + $result = $fave->delete(); + + if (!$result) { + common_log_db_error($fave, 'DELETE', __FILE__); + $this->serverError(_m('Could not delete favorite.')); + return; + } + + $profile->blowFavesCache(); + + if ($this->boolean('ajax')) { + $this->startHTML('text/xml;charset=utf-8'); + $this->elementStart('head'); + $this->element('title', null, _m('Add to favorites')); + $this->elementEnd('head'); + $this->elementStart('body'); + $favor = new AnonFavorForm($this, $notice); + $favor->show(); + $this->elementEnd('body'); + $this->elementEnd('html'); + } else { + $this->returnToPrevious(); + } + } + + /** + * If returnto not set, return to the public stream. + * + * @return string URL + */ + function defaultReturnTo() + { + $returnto = common_get_returnto(); + if (empty($returnto)) { + return common_local_url('public'); + } else { + return $returnto; + } + } +} + diff --git a/plugins/AnonymousFave/anondisfavorform.php b/plugins/AnonymousFave/anondisfavorform.php new file mode 100644 index 0000000000..c347ed7b43 --- /dev/null +++ b/plugins/AnonymousFave/anondisfavorform.php @@ -0,0 +1,74 @@ +. + * + * @category Form + * @package StatusNet + * @author Zach Copley + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/form.php'; + +/** + * Form for disfavoring a notice anonymously + * + * @category Form + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @see DisFavorForm + */ + +class AnonDisfavorForm extends DisFavorForm +{ + + /** + * Constructor + * + * @param HTMLOutputter $out output channel + * @param Notice $notice notice to disfavor + */ + + function __construct($out=null, $notice=null) + { + parent::__construct($out, $notice); + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + return common_local_url('AnonDisFavor'); + } + +} diff --git a/plugins/AnonymousFave/anonfavor.php b/plugins/AnonymousFave/anonfavor.php new file mode 100644 index 0000000000..58570ced9a --- /dev/null +++ b/plugins/AnonymousFave/anonfavor.php @@ -0,0 +1,118 @@ + + * @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 . + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Anonymous favor class + * + * @category Action + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + */ +class AnonFavorAction extends RedirectingAction +{ + /** + * Class handler. + * + * @param array $args query arguments + * + * @return void + */ + function handle($args) + { + parent::handle($args); + + $profile = AnonymousFavePlugin::getAnonProfile(); + + if (empty($profile) || $_SERVER['REQUEST_METHOD'] != 'POST') { + $this->clientError( + _m('Could not favor notice! Please make sure your browser has cookies enabled.') + ); + return; + } + + $id = $this->trimmed('notice'); + $notice = Notice::staticGet($id); + $token = $this->trimmed('token-' . $notice->id); + + if (empty($token) || $token != common_session_token()) { + $this->clientError(_m('There was a problem with your session token. Try again, please.')); + return; + } + + + if ($profile->hasFave($notice)) { + $this->clientError(_m('This notice is already a favorite!')); + return; + } + $fave = Fave::addNew($profile, $notice); + + if (!$fave) { + $this->serverError(_m('Could not create favorite.')); + return; + } + + $profile->blowFavesCache(); + + if ($this->boolean('ajax')) { + $this->startHTML('text/xml;charset=utf-8'); + $this->elementStart('head'); + $this->element('title', null, _m('Disfavor favorite')); + $this->elementEnd('head'); + $this->elementStart('body'); + $disfavor = new AnonDisFavorForm($this, $notice); + $disfavor->show(); + $this->elementEnd('body'); + $this->elementEnd('html'); + } else { + $this->returnToPrevious(); + } + } + + /** + * If returnto not set, return to the public stream. + * + * @return string URL + */ + function defaultReturnTo() + { + $returnto = common_get_returnto(); + if (empty($returnto)) { + return common_local_url('public'); + } else { + return $returnto; + } + } +} diff --git a/plugins/AnonymousFave/anonfavorform.php b/plugins/AnonymousFave/anonfavorform.php new file mode 100644 index 0000000000..d73c2831d0 --- /dev/null +++ b/plugins/AnonymousFave/anonfavorform.php @@ -0,0 +1,74 @@ +. + * + * @category Form + * @package StatusNet + * @author Zach Copley + * @copyright 20010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/form.php'; + +/** + * Form for favoring a notice anonymously + * + * @category Form + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @see AnonDisfavorForm + */ + +class AnonFavorForm extends FavorForm +{ + + /** + * Constructor + * + * @param HTMLOutputter $out output channel + * @param Notice $notice notice to favor + */ + + function __construct($out=null, $notice=null) + { + parent::__construct($out, $notice); + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + return common_local_url('AnonFavor'); + } + +} diff --git a/plugins/AnonymousFave/scripts/initialize_fave_tallys.php b/plugins/AnonymousFave/scripts/initialize_fave_tallys.php new file mode 100755 index 0000000000..f7ea6d1ef7 --- /dev/null +++ b/plugins/AnonymousFave/scripts/initialize_fave_tallys.php @@ -0,0 +1,38 @@ +#!/usr/bin/env php +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..')); + +$helptext = <<find(); + +while ($notice->fetch()) { + Fave_tally::ensureTally($notice->id); +} +