From db519d3ffbb54526ed0791f314b39325422d3f15 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Tue, 7 Dec 2010 16:23:12 -0500 Subject: whitespace problems in lib/router --- lib/router.php | 98 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 49 insertions(+), 49 deletions(-) (limited to 'lib/router.php') diff --git a/lib/router.php b/lib/router.php index eff88bb46..ca895c8bb 100644 --- a/lib/router.php +++ b/lib/router.php @@ -55,14 +55,14 @@ class StatusNet_URL_Mapper extends Net_URL_Mapper $result = null; if (Event::handle('StartConnectPath', array(&$path, &$defaults, &$rules, &$result))) { $result = parent::connect($path, $defaults, $rules); - if (array_key_exists('action', $defaults)) { - $action = $defaults['action']; - } elseif (array_key_exists('action', $rules)) { - $action = $rules['action']; - } else { - $action = null; - } - $this->_mapAction($action, $result); + if (array_key_exists('action', $defaults)) { + $action = $defaults['action']; + } elseif (array_key_exists('action', $rules)) { + $action = $rules['action']; + } else { + $action = null; + } + $this->_mapAction($action, $result); Event::handle('EndConnectPath', array($path, $defaults, $rules, $result)); } return $result; @@ -70,31 +70,31 @@ class StatusNet_URL_Mapper extends Net_URL_Mapper protected function _mapAction($action, $path) { - if (!array_key_exists($action, $this->_actionToPath)) { - $this->_actionToPath[$action] = array(); - } - $this->_actionToPath[$action][] = $path; - return; + if (!array_key_exists($action, $this->_actionToPath)) { + $this->_actionToPath[$action] = array(); + } + $this->_actionToPath[$action][] = $path; + return; } public function generate($values = array(), $qstring = array(), $anchor = '') { - if (!array_key_exists('action', $values)) { - return parent::generate($values, $qstring, $anchor); - } + if (!array_key_exists('action', $values)) { + return parent::generate($values, $qstring, $anchor); + } - $action = $values['action']; + $action = $values['action']; - if (!array_key_exists($action, $this->_actionToPath)) { - return parent::generate($values, $qstring, $anchor); - } + if (!array_key_exists($action, $this->_actionToPath)) { + return parent::generate($values, $qstring, $anchor); + } - $oldPaths = $this->paths; - $this->paths = $this->_actionToPath[$action]; - $result = parent::generate($values, $qstring, $anchor); - $this->paths = $oldPaths; + $oldPaths = $this->paths; + $this->paths = $this->_actionToPath[$action]; + $result = parent::generate($values, $qstring, $anchor); + $this->paths = $oldPaths; - return $result; + return $result; } } @@ -127,19 +127,19 @@ class Router function __construct() { if (empty($this->m)) { - if (!common_config('router', 'cache')) { + if (!common_config('router', 'cache')) { $this->m = $this->initialize(); - } else { - $k = self::cacheKey(); - $c = Cache::instance(); - $m = $c->get($k); - if (!empty($m)) { - $this->m = $m; - } else { - $this->m = $this->initialize(); - $c->set($k, $this->m); - } - } + } else { + $k = self::cacheKey(); + $c = Cache::instance(); + $m = $c->get($k); + if (!empty($m)) { + $this->m = $m; + } else { + $this->m = $this->initialize(); + $c->set($k, $this->m); + } + } } } @@ -199,7 +199,7 @@ class Router 'deleteuser', 'geocode', 'version', - ); + ); foreach ($main as $a) { $m->connect('main/'.$a, array('action' => $a)); @@ -222,8 +222,8 @@ class Router array('action' => 'publicxrds')); $m->connect('.well-known/host-meta', array('action' => 'hostmeta')); - $m->connect('main/xrd', - array('action' => 'userxrd')); + $m->connect('main/xrd', + array('action' => 'userxrd')); // these take a code @@ -248,19 +248,19 @@ class Router } $m->connect('settings/oauthapps/show/:id', - array('action' => 'showapplication'), - array('id' => '[0-9]+') + array('action' => 'showapplication'), + array('id' => '[0-9]+') ); $m->connect('settings/oauthapps/new', - array('action' => 'newapplication') + array('action' => 'newapplication') ); $m->connect('settings/oauthapps/edit/:id', - array('action' => 'editapplication'), - array('id' => '[0-9]+') + array('action' => 'editapplication'), + array('id' => '[0-9]+') ); $m->connect('settings/oauthapps/delete/:id', - array('action' => 'deleteapplication'), - array('id' => '[0-9]+') + array('action' => 'deleteapplication'), + array('id' => '[0-9]+') ); // search @@ -951,14 +951,14 @@ class Router $qpos = strpos($url, '?'); if ($qpos !== false) { $url = substr($url, 0, $qpos+1) . - str_replace('?', '&', substr($url, $qpos+1)); + str_replace('?', '&', substr($url, $qpos+1)); // @fixme this is a hacky workaround for http_build_query in the // lower-level code and bad configs that set the default separator // to & instead of &. Encoded &s in parameters will not be // affected. $url = substr($url, 0, $qpos+1) . - str_replace('&', '&', substr($url, $qpos+1)); + str_replace('&', '&', substr($url, $qpos+1)); } -- cgit v1.2.3-54-g00ecf From 11a0bde4595d2779b2587398c4b69bfcee116880 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 9 Dec 2010 13:11:02 -0500 Subject: AtomPub for single subscription --- actions/atompubshowsubscription.php | 224 ++++++++++++++++++++++++++++++++++++ classes/Subscription.php | 9 ++ lib/router.php | 21 ++-- 3 files changed, 247 insertions(+), 7 deletions(-) create mode 100644 actions/atompubshowsubscription.php (limited to 'lib/router.php') diff --git a/actions/atompubshowsubscription.php b/actions/atompubshowsubscription.php new file mode 100644 index 000000000..a30b21096 --- /dev/null +++ b/actions/atompubshowsubscription.php @@ -0,0 +1,224 @@ +. + * + * @category AtomPub + * @package StatusNet + * @author Evan Prodromou + * @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); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; + +/** + * Show a single subscription + * + * @category AtomPub + * @package StatusNet + * @author Evan Prodromou + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class AtompubshowsubscriptionAction extends ApiAuthAction +{ + private $_subscriber = null; + private $_subscribed = null; + private $_subscription = null; + + /** + * For initializing members of the class. + * + * @param array $argarray misc. arguments + * + * @return boolean true + */ + + function prepare($argarray) + { + parent::prepare($argarray); + $subscriberId = $this->trimmed('subscriber'); + + $this->_subscriber = Profile::staticGet('id', $subscriberId); + + if (empty($this->_subscriber)) { + throw new ClientException(sprintf(_('No such profile id: %d'), + $subscriberId), 404); + } + + $subscribedId = $this->trimmed('subscribed'); + + $this->_subscribed = Profile::staticGet('id', $subscribedId); + + if (empty($this->_subscribed)) { + throw new ClientException(sprintf(_('No such profile id: %d'), + $subscribedId), 404); + } + + $this->_subscription = + Subscription::pkeyGet(array('subscriber' => $subscriberId, + 'subscribed' => $subscribedId)); + + if (empty($this->_subscription)) { + $msg = sprintf(_('Profile %d not subscribed to profile %d'), + $subscriberId, $subscribedId); + throw new ClientException($msg, 404); + } + + return true; + } + + /** + * Handler method + * + * @param array $argarray is ignored since it's now passed in in prepare() + * + * @return void + */ + + function handle($argarray=null) + { + switch ($_SERVER['REQUEST_METHOD']) { + case 'GET': + $this->showSubscription(); + break; + case 'DELETE': + $this->deleteSubscription(); + break; + default: + $this->clientError(_('HTTP method not supported.'), 405); + return; + } + + return; + } + + /** + * Show the subscription in ActivityStreams Atom format. + * + * @return void + */ + + function showSubscription() + { + $activity = $this->_subscription->asActivity(); + + header('Content-Type: application/atom+xml; charset=utf-8'); + + $this->startXML(); + $this->raw($activity->asString(true, true, true)); + $this->endXML(); + + return; + } + + /** + * Delete the subscription + * + * @return void + */ + + function deleteSubscription() + { + if (empty($this->auth_user) || + $this->auth_user->id != $this->_subscriber->id) { + throw new ClientException(_("Can't delete someone else's". + " subscription"), 403); + } + + Subscription::cancel($this->_subscriber, + $this->_subscribed); + + return; + } + + /** + * Is this action read only? + * + * @param array $args other arguments + * + * @return boolean true + */ + + function isReadOnly($args) + { + if ($_SERVER['REQUEST_METHOD'] == 'DELETE') { + return false; + } else { + return true; + } + } + + /** + * Return last modified, if applicable. + * + * MAY override + * + * @return string last modified http header + */ + + function lastModified() + { + return max(strtotime($this->_subscriber->modified), + strtotime($this->_subscribed->modified), + strtotime($this->_subscription->modified)); + } + + /** + * Etag for this object + * + * @return string etag http header + */ + + function etag() + { + $mtime = strtotime($this->_subscription->modified); + + return 'W/"' . implode(':', array('AtomPubShowSubscription', + $this->_subscriber->id, + $this->_subscribed->id, + $mtime)) . '"'; + } + + /** + * Does this require authentication? + * + * @return boolean true if delete, else false + */ + + function requiresAuth() + { + if ($_SERVER['REQUEST_METHOD'] == 'DELETE') { + return true; + } else { + return false; + } + } +} diff --git a/classes/Subscription.php b/classes/Subscription.php index e9ad2a5a2..d41349412 100644 --- a/classes/Subscription.php +++ b/classes/Subscription.php @@ -245,6 +245,8 @@ class Subscription extends Memcached_DataObject $act->verb = ActivityVerb::FOLLOW; + // XXX: rationalize this with the URL + $act->id = TagURI::mint('follow:%d:%d:%s', $subscriber->id, $subscribed->id, @@ -262,6 +264,13 @@ class Subscription extends Memcached_DataObject $act->actor = ActivityObject::fromProfile($subscriber); $act->objects[] = ActivityObject::fromProfile($subscribed); + $url = common_local_url('AtomPubShowSubscription', + array('subscriber' => $subscriber->id, + 'subscribed' => $subscribed->id)); + + $act->selfLink = $url; + $act->editLink = $url; + return $act; } } diff --git a/lib/router.php b/lib/router.php index ca895c8bb..4e718f132 100644 --- a/lib/router.php +++ b/lib/router.php @@ -761,13 +761,6 @@ class Router $m->connect('api/oauth/authorize', array('action' => 'ApiOauthAuthorize')); - $m->connect('api/statusnet/app/service/:id.xml', - array('action' => 'ApiAtomService', - 'id' => Nickname::DISPLAY_FMT)); - - $m->connect('api/statusnet/app/service.xml', - array('action' => 'ApiAtomService')); - // Admin $m->connect('admin/site', array('action' => 'siteadminpanel')); @@ -909,6 +902,20 @@ class Router array('nickname' => Nickname::DISPLAY_FMT)); } + // AtomPub API + + $m->connect('api/statusnet/app/service/:id.xml', + array('action' => 'ApiAtomService', + 'id' => Nickname::DISPLAY_FMT)); + + $m->connect('api/statusnet/app/service.xml', + array('action' => 'ApiAtomService')); + + $m->connect('api/statusnet/app/subscriptions/:subscriber/:subscribed.atom', + array('action' => 'AtomPubShowSubscription'), + array('subscriber' => '[0-9]+', + 'subscribed' => '[0-9]+')); + // user stuff Event::handle('RouterInitialized', array($m)); -- cgit v1.2.3-54-g00ecf From c619a257fe04a5ad4cc670c14cc1eab2042b71d8 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 9 Dec 2010 16:05:07 -0500 Subject: Add subscription feed for AtomPub --- actions/atompubsubscriptionfeed.php | 262 ++++++++++++++++++++++++++++++++++++ actions/subscriptions.php | 17 +++ lib/router.php | 4 + 3 files changed, 283 insertions(+) create mode 100644 actions/atompubsubscriptionfeed.php (limited to 'lib/router.php') diff --git a/actions/atompubsubscriptionfeed.php b/actions/atompubsubscriptionfeed.php new file mode 100644 index 000000000..5eddc4cdd --- /dev/null +++ b/actions/atompubsubscriptionfeed.php @@ -0,0 +1,262 @@ +. + * + * @category Cache + * @package StatusNet + * @author Evan Prodromou + * @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); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; + +/** + * Subscription feed class for AtomPub + * + * Generates a list of the user's subscriptions + * + * @category AtomPub + * @package StatusNet + * @author Evan Prodromou + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class AtompubsubscriptionfeedAction extends ApiAuthAction +{ + private $_profile = null; + private $_subscribed = null; + + /** + * For initializing members of the class. + * + * @param array $argarray misc. arguments + * + * @return boolean true + */ + + function prepare($argarray) + { + parent::prepare($argarray); + + $subscriber = $this->trimmed('subscriber'); + + $this->_profile = Profile::staticGet('id', $subscriber); + + if (empty($this->_profile)) { + throw new ClientException(sprintf(_('No such profile id: %d'), + $subscriber), 404); + } + + // page and count from ApiAction + // Note: this is a list of profiles, not subscriptions + + $this->_subscribed = + $this->_profile->getSubscriptions(($this->page-1) * $this->count, + $this->count + 1); + + return true; + } + + /** + * Handler method + * + * @param array $argarray is ignored since it's now passed in in prepare() + * + * @return void + */ + + function handle($argarray=null) + { + parent::handle($argarray); + switch ($_SERVER['REQUEST_METHOD']) { + case 'HEAD': + case 'GET': + $this->showFeed(); + break; + case 'POST': + $this->addSubscription(); + break; + default: + $this->clientError(_('HTTP method not supported.'), 405); + return; + } + + return; + } + + /** + * Show the feed of subscriptions + * + * @return void + */ + + function showFeed() + { + header('Content-Type: application/atom+xml; charset=utf-8'); + + $url = common_local_url('AtomPubSubscriptionFeed', + array('subscriber' => $this->_profile->id)); + + $feed = new Atom10Feed(true); + + $feed->addNamespace('activity', + 'http://activitystrea.ms/spec/1.0/'); + + $feed->addNamespace('poco', + 'http://portablecontacts.net/spec/1.0'); + + $feed->addNamespace('media', + 'http://purl.org/syndication/atommedia'); + + $feed->id = $url; + + $feed->setUpdated('now'); + + $feed->addAuthor($this->_profile->getBestName(), + $this->_profile->getURI()); + + $feed->setTitle(sprintf(_("%s subscriptions"), + $this->_profile->getBestName())); + + $feed->setSubtitle(sprintf(_("People %s has subscribed to on %s"), + $this->_profile->getBestName()), + common_config('site', 'name')); + + $feed->addLink(common_local_url('subscriptions', + array('nickname' => + $this->_profile->nickname))); + + $feed->addLink($url, + array('rel' => 'self', + 'type' => 'application/atom+xml')); + + // If there's more... + + if ($this->page > 1) { + $feed->addLink($url, + array('rel' => 'first', + 'type' => 'application/atom+xml')); + + $feed->addLink(common_local_url('AtomPubSubscriptionFeed', + array('subscriber' => + $this->_profile->id, + 'page' => + $this->page - 1)), + array('rel' => 'prev', + 'type' => 'application/atom+xml')); + } + + if ($this->_subscribed->N > $this->count) { + + $feed->addLink(common_local_url('AtomPubSubscriptionFeed', + array('subscriber' => + $this->_profile->id, + 'page' => + $this->page + 1)), + array('rel' => 'next', + 'type' => 'application/atom+xml')); + } + + $i = 0; + + // XXX: This is kind of inefficient + + while ($this->_subscribed->fetch()) { + + // We get one more than needed; skip that one + + $i++; + + if ($i > $this->count) { + break; + } + + $sub = Subscription::pkeyGet(array('subscriber' => + $this->_profile->id, + 'subscribed' => + $this->_subscribed->id)); + $act = $sub->asActivity(); + $feed->addEntryRaw($act->asString(false, false, false)); + } + + $this->raw($feed->getString()); + } + + /** + * Return true if read only. + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + + function isReadOnly($args) + { + return $_SERVER['REQUEST_METHOD'] != 'POST'; + } + + /** + * Return last modified, if applicable. + * + * @return string last modified http header + */ + + function lastModified() + { + return null; + } + + /** + * Return etag, if applicable. + * + * @return string etag http header + */ + + function etag() + { + return null; + } + + /** + * Does this require authentication? + * + * @return boolean true if delete, else false + */ + + function requiresAuth() + { + if ($_SERVER['REQUEST_METHOD'] == 'DELETE') { + return true; + } else { + return false; + } + } +} diff --git a/actions/subscriptions.php b/actions/subscriptions.php index ba2f67f2d..a814a4f35 100644 --- a/actions/subscriptions.php +++ b/actions/subscriptions.php @@ -163,6 +163,22 @@ class SubscriptionsAction extends GalleryAction $cloud2 = new SubscriptionsPeopleSelfTagCloudSection($this); $cloud2->show(); } + + /** + * Link to feeds of subscriptions + * + * @return array of Feed objects + */ + + function getFeeds() + { + return array(new Feed(Feed::ATOM, + common_local_url('AtomPubSubscriptionFeed', + array('subscriber' => $this->profile->id)), + sprintf(_('Subscription feed for %s (Atom)'), + $this->profile->nickname))); + + } } // XXX SubscriptionsList and SubscriptionList are dangerously close @@ -247,4 +263,5 @@ class SubscriptionsListItem extends SubscriptionListItem $this->out->elementEnd('form'); return; } + } diff --git a/lib/router.php b/lib/router.php index 4e718f132..e6b4c0fbc 100644 --- a/lib/router.php +++ b/lib/router.php @@ -916,6 +916,10 @@ class Router array('subscriber' => '[0-9]+', 'subscribed' => '[0-9]+')); + $m->connect('api/statusnet/app/subscriptions/:subscriber.atom', + array('action' => 'AtomPubSubscriptionFeed'), + array('subscriber' => '[0-9]+')); + // user stuff Event::handle('RouterInitialized', array($m)); -- cgit v1.2.3-54-g00ecf From d55f606cf8c7c73397258fdfd492b195b987b707 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 9 Dec 2010 16:43:35 -0800 Subject: Tweaking nickname format regexes: added one that explicitly allows numbers, to be used in router setup. --- lib/nickname.php | 22 ++++++++++++++++++++-- lib/router.php | 44 ++++++++++++++++++++++---------------------- 2 files changed, 42 insertions(+), 24 deletions(-) (limited to 'lib/router.php') diff --git a/lib/nickname.php b/lib/nickname.php index a0c9378cd..562f1e205 100644 --- a/lib/nickname.php +++ b/lib/nickname.php @@ -20,7 +20,25 @@ class Nickname { /** - * Regex fragment for pulling an arbitrarily-formated nickname. + * Regex fragment for pulling a formated nickname *OR* ID number. + * Suitable for router def of 'id' parameters on API actions. + * + * Not guaranteed to be valid after normalization; run the string through + * Nickname::normalize() to get the canonical form, or Nickname::isValid() + * if you just need to check if it's properly formatted. + * + * This, DISPLAY_FMT, and CANONICAL_FMT replace the old NICKNAME_FMT, + * but be aware that these should not be enclosed in []s. + * + * @fixme would prefer to define in reference to the other constants + */ + const INPUT_FMT = '(?:[0-9]+|[0-9a-zA-Z_]{1,64})'; + + /** + * Regex fragment for acceptable user-formatted variant of a nickname. + * This includes some chars such as underscore which will be removed + * from the normalized canonical form, but still must fit within + * field length limits. * * Not guaranteed to be valid after normalization; run the string through * Nickname::normalize() to get the canonical form, or Nickname::isValid() @@ -29,7 +47,7 @@ class Nickname * This and CANONICAL_FMT replace the old NICKNAME_FMT, but be aware * that these should not be enclosed in []s. */ - const DISPLAY_FMT = '[0-9a-zA-Z_]+'; + const DISPLAY_FMT = '[0-9a-zA-Z_]{1,64}'; /** * Regex fragment for checking a canonical nickname. diff --git a/lib/router.php b/lib/router.php index ca895c8bb..d747493de 100644 --- a/lib/router.php +++ b/lib/router.php @@ -403,7 +403,7 @@ class Router $m->connect('api/statuses/friends_timeline/:id.:format', array('action' => 'ApiTimelineFriends', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json|rss|atom)')); $m->connect('api/statuses/home_timeline.:format', @@ -412,7 +412,7 @@ class Router $m->connect('api/statuses/home_timeline/:id.:format', array('action' => 'ApiTimelineHome', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json|rss|atom)')); $m->connect('api/statuses/user_timeline.:format', @@ -421,7 +421,7 @@ class Router $m->connect('api/statuses/user_timeline/:id.:format', array('action' => 'ApiTimelineUser', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json|rss|atom)')); $m->connect('api/statuses/mentions.:format', @@ -430,7 +430,7 @@ class Router $m->connect('api/statuses/mentions/:id.:format', array('action' => 'ApiTimelineMentions', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json|rss|atom)')); $m->connect('api/statuses/replies.:format', @@ -439,7 +439,7 @@ class Router $m->connect('api/statuses/replies/:id.:format', array('action' => 'ApiTimelineMentions', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json|rss|atom)')); $m->connect('api/statuses/retweeted_by_me.:format', @@ -460,7 +460,7 @@ class Router $m->connect('api/statuses/friends/:id.:format', array('action' => 'ApiUserFriends', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json)')); $m->connect('api/statuses/followers.:format', @@ -469,7 +469,7 @@ class Router $m->connect('api/statuses/followers/:id.:format', array('action' => 'ApiUserFollowers', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json)')); $m->connect('api/statuses/show.:format', @@ -512,7 +512,7 @@ class Router $m->connect('api/users/show/:id.:format', array('action' => 'ApiUserShow', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json)')); // direct messages @@ -550,12 +550,12 @@ class Router $m->connect('api/friendships/create/:id.:format', array('action' => 'ApiFriendshipsCreate', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json)')); $m->connect('api/friendships/destroy/:id.:format', array('action' => 'ApiFriendshipsDestroy', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json)')); // Social graph @@ -612,17 +612,17 @@ class Router $m->connect('api/favorites/:id.:format', array('action' => 'ApiTimelineFavorites', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json|rss|atom)')); $m->connect('api/favorites/create/:id.:format', array('action' => 'ApiFavoriteCreate', - 'id' => Nickname::DISPLAY_FMT, + 'id' => '[0-9]+', 'format' => '(xml|json)')); $m->connect('api/favorites/destroy/:id.:format', array('action' => 'ApiFavoriteDestroy', - 'id' => Nickname::DISPLAY_FMT, + 'id' => '[0-9]+', 'format' => '(xml|json)')); // blocks @@ -632,7 +632,7 @@ class Router $m->connect('api/blocks/create/:id.:format', array('action' => 'ApiBlockCreate', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json)')); $m->connect('api/blocks/destroy.:format', @@ -641,7 +641,7 @@ class Router $m->connect('api/blocks/destroy/:id.:format', array('action' => 'ApiBlockDestroy', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json)')); // help @@ -677,7 +677,7 @@ class Router $m->connect('api/statusnet/groups/timeline/:id.:format', array('action' => 'ApiTimelineGroup', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json|rss|atom)')); $m->connect('api/statusnet/groups/show.:format', @@ -686,12 +686,12 @@ class Router $m->connect('api/statusnet/groups/show/:id.:format', array('action' => 'ApiGroupShow', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json)')); $m->connect('api/statusnet/groups/join.:format', array('action' => 'ApiGroupJoin', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json)')); $m->connect('api/statusnet/groups/join/:id.:format', @@ -700,7 +700,7 @@ class Router $m->connect('api/statusnet/groups/leave.:format', array('action' => 'ApiGroupLeave', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json)')); $m->connect('api/statusnet/groups/leave/:id.:format', @@ -717,7 +717,7 @@ class Router $m->connect('api/statusnet/groups/list/:id.:format', array('action' => 'ApiGroupList', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json|rss|atom)')); $m->connect('api/statusnet/groups/list_all.:format', @@ -730,7 +730,7 @@ class Router $m->connect('api/statusnet/groups/membership/:id.:format', array('action' => 'ApiGroupMembership', - 'id' => Nickname::DISPLAY_FMT, + 'id' => Nickname::INPUT_FMT, 'format' => '(xml|json)')); $m->connect('api/statusnet/groups/create.:format', @@ -763,7 +763,7 @@ class Router $m->connect('api/statusnet/app/service/:id.xml', array('action' => 'ApiAtomService', - 'id' => Nickname::DISPLAY_FMT)); + 'id' => Nickname::INPUT_FMT)); $m->connect('api/statusnet/app/service.xml', array('action' => 'ApiAtomService')); -- cgit v1.2.3-54-g00ecf From 37c447be4637dc639f586846d35e14a4bfce069d Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 10 Dec 2010 18:50:50 -0500 Subject: Show a single favorite for AtomPub --- actions/atompubshowfavorite.php | 228 ++++++++++++++++++++++++++++++++++++++++ classes/Fave.php | 10 ++ lib/router.php | 9 ++ 3 files changed, 247 insertions(+) create mode 100644 actions/atompubshowfavorite.php (limited to 'lib/router.php') diff --git a/actions/atompubshowfavorite.php b/actions/atompubshowfavorite.php new file mode 100644 index 000000000..5fe680bb7 --- /dev/null +++ b/actions/atompubshowfavorite.php @@ -0,0 +1,228 @@ +. + * + * @category AtomPub + * @package StatusNet + * @author Evan Prodromou + * @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); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; + +/** + * Show a single favorite in Atom Activity Streams format. + * + * Can also be used to delete a favorite. + * + * @category Action + * @package StatusNet + * @author Evan Prodromou + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class AtompubshowfavoriteAction extends ApiAuthAction +{ + private $_profile = null; + private $_notice = null; + private $_fave = null; + + /** + * For initializing members of the class. + * + * @param array $argarray misc. arguments + * + * @return boolean true + */ + + function prepare($argarray) + { + parent::prepare($argarray); + + $profileId = $this->trimmed('profile'); + $noticeId = $this->trimmed('notice'); + + $this->_profile = Profile::staticGet('id', $profileId); + + if (empty($this->_profile)) { + throw new ClientException(_('No such profile.'), 404); + } + + $this->_notice = Notice::staticGet('id', $noticeId); + + if (empty($this->_notice)) { + throw new ClientException(_('No such notice.'), 404); + } + + $this->_fave = Fave::pkeyGet(array('user_id' => $profileId, + 'notice_id' => $noticeId)); + + if (empty($this->_fave)) { + throw new ClientException(_('No such favorite.'), 404); + } + + return true; + } + + /** + * Handler method + * + * @param array $argarray is ignored since it's now passed in in prepare() + * + * @return void + */ + + function handle($argarray=null) + { + parent::handle($argarray); + + switch ($_SERVER['REQUEST_METHOD']) { + case GET: + case HEAD: + $this->showFave(); + break; + case DELETE: + $this->deleteFave(); + break; + default: + throw new ClientException(_('HTTP method not supported.'), + 405); + } + return true; + } + + /** + * Show a single favorite, in ActivityStreams format + * + * @return void + */ + + function showFave() + { + $activity = $this->_fave->asActivity(); + + header('Content-Type: application/atom+xml; charset=utf-8'); + + $this->startXML(); + $this->raw($activity->asString(true, true, true)); + $this->endXML(); + + return; + } + + /** + * Delete the favorite + * + * @return void + */ + + function deleteFave() + { + if (empty($this->auth_user) || + $this->auth_user->id != $this->_profile->id) { + throw new ClientException(_("Can't delete someone else's". + " favorite"), 403); + } + + $this->_fave->delete(); + + return; + } + + /** + * Return true if read only. + * + * MAY override + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + + function isReadOnly($args) + { + if ($_SERVER['REQUEST_METHOD'] == 'GET' || + $_SERVER['REQUEST_METHOD'] == 'HEAD') { + return true; + } else { + return false; + } + } + + /** + * Return last modified, if applicable. + * + * MAY override + * + * @return string last modified http header + */ + + function lastModified() + { + return max(strtotime($this->_profile->modified), + strtotime($this->_notice->modified), + strtotime($this->_fave->modified)); + } + + /** + * Return etag, if applicable. + * + * MAY override + * + * @return string etag http header + */ + + function etag() + { + $mtime = strtotime($this->_fave->modified); + + return 'W/"' . implode(':', array('AtomPubShowFavorite', + $this->_profile->id, + $this->_notice->id, + $mtime)) . '"'; + } + + /** + * Does this require authentication? + * + * @return boolean true if delete, else false + */ + + function requiresAuth() + { + if ($_SERVER['REQUEST_METHOD'] == 'GET' || + $_SERVER['REQUEST_METHOD'] == 'HEAD') { + return false; + } else { + return true; + } + } +} diff --git a/classes/Fave.php b/classes/Fave.php index 9922ae45c..030e67b56 100644 --- a/classes/Fave.php +++ b/classes/Fave.php @@ -138,6 +138,9 @@ class Fave extends Memcached_DataObject $act = new Activity(); $act->verb = ActivityVerb::FAVORITE; + + // FIXME: rationalize this with URL below + $act->id = TagURI::mint('favor:%d:%d:%s', $profile->id, $notice->id, @@ -155,6 +158,13 @@ class Fave extends Memcached_DataObject $act->actor = ActivityObject::fromProfile($profile); $act->objects[] = ActivityObject::fromNotice($notice); + $url = common_local_url('AtomPubShowFavorite', + array('profile' => $this->user_id, + 'notice' => $this->notice_id)); + + $act->selfLink = $url; + $act->editLink = $url; + return $act; } } diff --git a/lib/router.php b/lib/router.php index b20336164..0ced86f34 100644 --- a/lib/router.php +++ b/lib/router.php @@ -920,6 +920,15 @@ class Router array('action' => 'AtomPubSubscriptionFeed'), array('subscriber' => '[0-9]+')); + $m->connect('api/statusnet/app/favorites/:profile/:notice.atom', + array('action' => 'AtomPubShowFavorite'), + array('profile' => '[0-9]+', + 'notice' => '[0-9]+')); + + $m->connect('api/statusnet/app/favorites/:profile.atom', + array('action' => 'AtomPubFavoriteFeed'), + array('profile' => '[0-9]+')); + // user stuff Event::handle('RouterInitialized', array($m)); -- cgit v1.2.3-54-g00ecf From 4b7a0d366ccc2da4c178ff0cff0f207732301a0a Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 13 Dec 2010 12:40:01 -0500 Subject: add atompub membership actions to router --- lib/router.php | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'lib/router.php') diff --git a/lib/router.php b/lib/router.php index 0ced86f34..e05fa096d 100644 --- a/lib/router.php +++ b/lib/router.php @@ -929,6 +929,15 @@ class Router array('action' => 'AtomPubFavoriteFeed'), array('profile' => '[0-9]+')); + $m->connect('api/statusnet/app/memberships/:profile/:group.atom', + array('action' => 'AtomPubShowMembership'), + array('profile' => '[0-9]+', + 'group' => '[0-9]+')); + + $m->connect('api/statusnet/app/membership/:profile.atom', + array('action' => 'AtomPubMembershipFeed'), + array('profile' => '[0-9]+')); + // user stuff Event::handle('RouterInitialized', array($m)); -- cgit v1.2.3-54-g00ecf From 4be9c0a0e98b2135347baa9aea257d819719c7bf Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 13 Dec 2010 13:50:57 -0500 Subject: fix URL for memberships feed --- lib/router.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/router.php') diff --git a/lib/router.php b/lib/router.php index e05fa096d..c42cca5f6 100644 --- a/lib/router.php +++ b/lib/router.php @@ -934,7 +934,7 @@ class Router array('profile' => '[0-9]+', 'group' => '[0-9]+')); - $m->connect('api/statusnet/app/membership/:profile.atom', + $m->connect('api/statusnet/app/memberships/:profile.atom', array('action' => 'AtomPubMembershipFeed'), array('profile' => '[0-9]+')); -- cgit v1.2.3-54-g00ecf From 2ed1e9b126baa3d09cb41b1c4ea4016ae4f89936 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 14 Dec 2010 14:43:50 -0800 Subject: AtomPub discovery fix: gets MarsEdit's auto API detection working. Router entry for AtomPubService was slightly off, generating an incorrect link in the RSD data. --- lib/router.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/router.php') diff --git a/lib/router.php b/lib/router.php index c42cca5f6..24cda72b6 100644 --- a/lib/router.php +++ b/lib/router.php @@ -905,8 +905,8 @@ class Router // AtomPub API $m->connect('api/statusnet/app/service/:id.xml', - array('action' => 'ApiAtomService', - 'id' => Nickname::DISPLAY_FMT)); + array('action' => 'ApiAtomService'), + array('id' => Nickname::DISPLAY_FMT)); $m->connect('api/statusnet/app/service.xml', array('action' => 'ApiAtomService')); -- cgit v1.2.3-54-g00ecf