From d3d499879c4d01bde46033a3a98f9190ea18cb63 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 16 Jun 2010 14:29:24 -0700 Subject: - More useful group info from api/statusnet/group/show - Add statusnet:group_info tag to group Atom feeds --- lib/apiaction.php | 40 ++++++++++++++++++++++++++-------------- lib/atomgroupnoticefeed.php | 19 +++++++++++++++++++ 2 files changed, 45 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/apiaction.php b/lib/apiaction.php index 320aa0316..7cc473d51 100644 --- a/lib/apiaction.php +++ b/lib/apiaction.php @@ -346,20 +346,32 @@ class ApiAction extends Action function twitterGroupArray($group) { - $twitter_group=array(); - $twitter_group['id']=$group->id; - $twitter_group['url']=$group->permalink(); - $twitter_group['nickname']=$group->nickname; - $twitter_group['fullname']=$group->fullname; - $twitter_group['original_logo']=$group->original_logo; - $twitter_group['homepage_logo']=$group->homepage_logo; - $twitter_group['stream_logo']=$group->stream_logo; - $twitter_group['mini_logo']=$group->mini_logo; - $twitter_group['homepage']=$group->homepage; - $twitter_group['description']=$group->description; - $twitter_group['location']=$group->location; - $twitter_group['created']=$this->dateTwitter($group->created); - $twitter_group['modified']=$this->dateTwitter($group->modified); + $twitter_group = array(); + + $twitter_group['id'] = $group->id; + $twitter_group['url'] = $group->permalink(); + $twitter_group['nickname'] = $group->nickname; + $twitter_group['fullname'] = $group->fullname; + + if (isset($this->auth_user)) { + $twitter_group['member'] = $this->auth_user->isMember($group); + $twitter_group['blocked'] = Group_block::isBlocked( + $group, + $this->auth_user->getProfile() + ); + } + + $twitter_group['member_count'] = $group->getMemberCount(); + $twitter_group['original_logo'] = $group->original_logo; + $twitter_group['homepage_logo'] = $group->homepage_logo; + $twitter_group['stream_logo'] = $group->stream_logo; + $twitter_group['mini_logo'] = $group->mini_logo; + $twitter_group['homepage'] = $group->homepage; + $twitter_group['description'] = $group->description; + $twitter_group['location'] = $group->location; + $twitter_group['created'] = $this->dateTwitter($group->created); + $twitter_group['modified'] = $this->dateTwitter($group->modified); + return $twitter_group; } diff --git a/lib/atomgroupnoticefeed.php b/lib/atomgroupnoticefeed.php index 7934a4f9e..761df587c 100644 --- a/lib/atomgroupnoticefeed.php +++ b/lib/atomgroupnoticefeed.php @@ -93,4 +93,23 @@ class AtomGroupNoticeFeed extends AtomNoticeFeed return $this->group; } + function initFeed() + { + parent::initFeed(); + + $attrs = array(); + + if (!empty($this->cur)) { + $attrs['member'] = $this->cur->isMember($this->group) + ? 'true' : 'false'; + $attrs['blocked'] = Group_block::isBlocked( + $this->group, + $this->cur->getProfile() + ) ? 'true' : 'false'; + } + + $attrs['member_count'] = $this->group->getMemberCount(); + + $this->element('statusnet:group_info', $attrs, null); + } } -- cgit v1.2.3-54-g00ecf From a6ce4eef0df225dd863baf45d5615cbe22dcdea6 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 16 Jun 2010 18:27:51 -0700 Subject: Fix problem with AvatarLink in which it was sometimes leaving the width attribute empty --- lib/avatarlink.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/avatarlink.php b/lib/avatarlink.php index e67799e2e..7d4256d6e 100644 --- a/lib/avatarlink.php +++ b/lib/avatarlink.php @@ -76,8 +76,8 @@ class AvatarLink $alink = new AvatarLink(); $alink->url = $filename; $alink->height = $size; + $alink->width = $size; if (!empty($filename)) { - $alink->width = $size; $alink->type = self::mediatype($filename); } else { $alink->url = User_group::defaultLogo($size); -- cgit v1.2.3-54-g00ecf From 87125a1395ed4bf8660e2ba47645835b9d1f4acf Mon Sep 17 00:00:00 2001 From: Siebrand Mazeland Date: Mon, 21 Jun 2010 20:15:26 +0200 Subject: Improve error message per discussion on http://translatewiki.net/wiki/Thread:Support/Unclear_message. Spotted by Peter17 and changed per suggestion of McDutchie with approval of Brion. --- lib/themeuploader.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/themeuploader.php b/lib/themeuploader.php index 18ef8c4d1..370965db0 100644 --- a/lib/themeuploader.php +++ b/lib/themeuploader.php @@ -55,10 +55,10 @@ class ThemeUploader public static function fromUpload($name) { if (!isset($_FILES[$name]['error'])) { - throw new ServerException(_("Theme upload missing or failed.")); + throw new ServerException(_("The theme file is missing or the upload failed.")); } if ($_FILES[$name]['error'] != UPLOAD_ERR_OK) { - throw new ServerException(_("Theme upload missing or failed.")); + throw new ServerException(_("The theme file is missing or the upload failed.")); } return new ThemeUploader($_FILES[$name]['tmp_name']); } -- cgit v1.2.3-54-g00ecf From 1eec7f779fc85b530907ea31deceadb2a30d7614 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 22 Jun 2010 16:28:06 -0700 Subject: - Add profile_info tag to Atom author - Normalize xmlns:statusnet links in the API --- classes/Notice.php | 4 ++-- classes/Profile.php | 10 +++++++++- lib/apiaction.php | 2 ++ lib/atomnoticefeed.php | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/classes/Notice.php b/classes/Notice.php index f8eda5777..c752e35a7 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -1190,7 +1190,7 @@ class Notice extends Memcached_DataObject 'xmlns:media' => 'http://purl.org/syndication/atommedia', 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0', 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0', - 'xmlns:statusnet' => 'http://status.net/ont/'); + 'xmlns:statusnet' => 'http://status.net/schema/api/1/'); } else { $attrs = array(); } @@ -1225,7 +1225,7 @@ class Notice extends Memcached_DataObject $xs->element('title', null, common_xml_safe_str($this->content)); if ($author) { - $xs->raw($profile->asAtomAuthor()); + $xs->raw($profile->asAtomAuthor($cur)); $xs->raw($profile->asActivityActor()); } diff --git a/classes/Profile.php b/classes/Profile.php index 54f557ea7..a303469e9 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -849,15 +849,23 @@ class Profile extends Memcached_DataObject * * Assumes that Atom has been previously set up as the base namespace. * + * @param Profile $cur the current authenticated user + * * @return string */ - function asAtomAuthor() + function asAtomAuthor($cur = null) { $xs = new XMLStringer(true); $xs->elementStart('author'); $xs->element('name', null, $this->nickname); $xs->element('uri', null, $this->getUri()); + if ($cur != null) { + $attrs = Array(); + $attrs['following'] = $cur->isSubscribed($this) ? 'true' : 'false'; + $attrs['blocking'] = $cur->hasBlocked($this) ? 'true' : 'false'; + $xs->element('statusnet:profile_info', $attrs, null); + } $xs->elementEnd('author'); return $xs->getString(); diff --git a/lib/apiaction.php b/lib/apiaction.php index 7cc473d51..226481778 100644 --- a/lib/apiaction.php +++ b/lib/apiaction.php @@ -208,11 +208,13 @@ class ApiAction extends Action // Is the requesting user following this user? $twitter_user['following'] = false; + $twitter_user['statusnet:blocking'] = false; $twitter_user['notifications'] = false; if (isset($this->auth_user)) { $twitter_user['following'] = $this->auth_user->isSubscribed($profile); + $twitter_user['statusnet:blocking'] = $this->auth_user->hasBlocked($profile); // Notifications on? $sub = Subscription::pkeyGet(array('subscriber' => diff --git a/lib/atomnoticefeed.php b/lib/atomnoticefeed.php index ef44de4b6..6ed803ce4 100644 --- a/lib/atomnoticefeed.php +++ b/lib/atomnoticefeed.php @@ -95,7 +95,7 @@ class AtomNoticeFeed extends Atom10Feed $this->addNamespace( 'statusnet', - 'http://status.net/ont/' + 'http://status.net/schema/api/1/' ); } -- cgit v1.2.3-54-g00ecf From 9eb5a976b03fae6bd1e1fce6abfe4a6c7964d1ae Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 24 Jun 2010 18:11:50 -0700 Subject: Have API methods for search subclass ApiPrivateAuthAction --- actions/apisearchatom.php | 408 ++++++++++++++++++++++++++++++++++++++++++ actions/apisearchjson.php | 154 ++++++++++++++++ actions/apitrends.php | 90 ++++++++++ actions/twitapisearchatom.php | 402 ----------------------------------------- actions/twitapisearchjson.php | 151 ---------------- actions/twitapitrends.php | 88 --------- lib/router.php | 6 +- 7 files changed, 655 insertions(+), 644 deletions(-) create mode 100644 actions/apisearchatom.php create mode 100644 actions/apisearchjson.php create mode 100644 actions/apitrends.php delete mode 100644 actions/twitapisearchatom.php delete mode 100644 actions/twitapisearchjson.php delete mode 100644 actions/twitapitrends.php (limited to 'lib') diff --git a/actions/apisearchatom.php b/actions/apisearchatom.php new file mode 100644 index 000000000..60bb8b040 --- /dev/null +++ b/actions/apisearchatom.php @@ -0,0 +1,408 @@ +. + * + * @category Search + * @package StatusNet + * @author Zach Copley + * @copyright 2008-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') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/apiprivateauth.php'; + +/** + * Action for outputting search results in Twitter compatible Atom + * format. + * + * TODO: abstract Atom stuff into a ruseable base class like + * RSS10Action. + * + * @category Search + * @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 ApiPrivateAuthAction + */ + +class ApiSearchAtomAction extends ApiPrivateAuthAction +{ + + var $cnt; + var $query; + var $lang; + var $rpp; + var $page; + var $since_id; + var $geocode; + + /** + * Constructor + * + * Just wraps the Action constructor. + * + * @param string $output URI to output to, default = stdout + * @param boolean $indent Whether to indent output, default true + * + * @see Action::__construct + */ + + function __construct($output='php://output', $indent=null) + { + parent::__construct($output, $indent); + } + + /** + * Do we need to write to the database? + * + * @return boolean true + */ + + function isReadonly() + { + return true; + } + + /** + * Read arguments and initialize members + * + * @param array $args Arguments from $_REQUEST + * + * @return boolean success + * + */ + + function prepare($args) + { + common_debug("in apisearchatom prepare()"); + + parent::prepare($args); + + + $this->query = $this->trimmed('q'); + $this->lang = $this->trimmed('lang'); + $this->rpp = $this->trimmed('rpp'); + + if (!$this->rpp) { + $this->rpp = 15; + } + + if ($this->rpp > 100) { + $this->rpp = 100; + } + + $this->page = $this->trimmed('page'); + + if (!$this->page) { + $this->page = 1; + } + + // TODO: Suppport since_id -- we need to tweak the backend + // Search classes to support it. + + $this->since_id = $this->trimmed('since_id'); + $this->geocode = $this->trimmed('geocode'); + + // TODO: Also, language and geocode + + return true; + } + + /** + * Handle a request + * + * @param array $args Arguments from $_REQUEST + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + common_debug("In apisearchatom handle()"); + $this->showAtom(); + } + + /** + * Get the notices to output as results. This also sets some class + * attrs so we can use them to calculate pagination, and output + * since_id and max_id. + * + * @return array an array of Notice objects sorted in reverse chron + */ + + function getNotices() + { + // TODO: Support search operators like from: and to:, boolean, etc. + + $notices = array(); + $notice = new Notice(); + + // lcase it for comparison + $q = strtolower($this->query); + + $search_engine = $notice->getSearchEngine('notice'); + $search_engine->set_sort_mode('chron'); + $search_engine->limit(($this->page - 1) * $this->rpp, + $this->rpp + 1, true); + if (false === $search_engine->query($q)) { + $this->cnt = 0; + } else { + $this->cnt = $notice->find(); + } + + $cnt = 0; + $this->max_id = 0; + + if ($this->cnt > 0) { + while ($notice->fetch()) { + + ++$cnt; + + if (!$this->max_id) { + $this->max_id = $notice->id; + } + + if ($cnt > $this->rpp) { + break; + } + + $notices[] = clone($notice); + } + } + + return $notices; + } + + /** + * Output search results as an Atom feed + * + * @return void + */ + + function showAtom() + { + $notices = $this->getNotices(); + + $this->initAtom(); + $this->showFeed(); + + foreach ($notices as $n) { + + $profile = $n->getProfile(); + + // Don't show notices from deleted users + + if (!empty($profile)) { + $this->showEntry($n); + } + } + + $this->endAtom(); + } + + /** + * Show feed specific Atom elements + * + * @return void + */ + + function showFeed() + { + // TODO: A9 OpenSearch stuff like search.twitter.com? + + $server = common_config('site', 'server'); + $sitename = common_config('site', 'name'); + + // XXX: Use xmlns:statusnet instead? + + $this->elementStart('feed', + array('xmlns' => 'http://www.w3.org/2005/Atom', + + // XXX: xmlns:twitter causes Atom validation to fail + // It's used for the source attr on notices + + 'xmlns:twitter' => 'http://api.twitter.com/', + 'xml:lang' => 'en-US')); // XXX Other locales ? + + $taguribase = TagURI::base(); + $this->element('id', null, "tag:$taguribase:search/$server"); + + $site_uri = common_path(false); + + $search_uri = $site_uri . 'api/search.atom?q=' . urlencode($this->query); + + if ($this->rpp != 15) { + $search_uri .= '&rpp=' . $this->rpp; + } + + // FIXME: this alternate link is not quite right because our + // web-based notice search doesn't support a rpp (responses per + // page) param yet + + $this->element('link', array('type' => 'text/html', + 'rel' => 'alternate', + 'href' => $site_uri . 'search/notice?q=' . + urlencode($this->query))); + + // self link + + $self_uri = $search_uri; + $self_uri .= ($this->page > 1) ? '&page=' . $this->page : ''; + + $this->element('link', array('type' => 'application/atom+xml', + 'rel' => 'self', + 'href' => $self_uri)); + + $this->element('title', null, "$this->query - $sitename Search"); + $this->element('updated', null, common_date_iso8601('now')); + + // XXX: The below "rel" links are not valid Atom, but it's what + // Twitter does... + + // refresh link + + $refresh_uri = $search_uri . "&since_id=" . $this->max_id; + + $this->element('link', array('type' => 'application/atom+xml', + 'rel' => 'refresh', + 'href' => $refresh_uri)); + + // pagination links + + if ($this->cnt > $this->rpp) { + + $next_uri = $search_uri . "&max_id=" . $this->max_id . + '&page=' . ($this->page + 1); + + $this->element('link', array('type' => 'application/atom+xml', + 'rel' => 'next', + 'href' => $next_uri)); + } + + if ($this->page > 1) { + + $previous_uri = $search_uri . "&max_id=" . $this->max_id . + '&page=' . ($this->page - 1); + + $this->element('link', array('type' => 'application/atom+xml', + 'rel' => 'previous', + 'href' => $previous_uri)); + } + + } + + /** + * Build an Atom entry similar to search.twitter.com's based on + * a given notice + * + * @param Notice $notice the notice to use + * + * @return void + */ + + function showEntry($notice) + { + $server = common_config('site', 'server'); + $profile = $notice->getProfile(); + $nurl = common_local_url('shownotice', array('notice' => $notice->id)); + + $this->elementStart('entry'); + + $taguribase = TagURI::base(); + + $this->element('id', null, "tag:$taguribase:$notice->id"); + $this->element('published', null, common_date_w3dtf($notice->created)); + $this->element('link', array('type' => 'text/html', + 'rel' => 'alternate', + 'href' => $nurl)); + $this->element('title', null, common_xml_safe_str(trim($notice->content))); + $this->element('content', array('type' => 'html'), $notice->rendered); + $this->element('updated', null, common_date_w3dtf($notice->created)); + $this->element('link', array('type' => 'image/png', + // XXX: Twitter uses rel="image" (not valid) + 'rel' => 'related', + 'href' => $profile->avatarUrl())); + + // @todo: Here is where we'd put in a link to an atom feed for threads + + $source = null; + + $ns = $notice->getSource(); + if ($ns) { + if (!empty($ns->name) && !empty($ns->url)) { + $source = '' + . htmlspecialchars($ns->name) + . ''; + } else { + $source = $ns->code; + } + } + + $this->element("twitter:source", null, $source); + + $this->elementStart('author'); + + $name = $profile->nickname; + + if ($profile->fullname) { + $name .= ' (' . $profile->fullname . ')'; + } + + $this->element('name', null, $name); + $this->element('uri', null, common_profile_uri($profile)); + $this->elementEnd('author'); + + $this->elementEnd('entry'); + } + + /** + * Initialize the Atom output, send headers + * + * @return void + */ + + function initAtom() + { + header('Content-Type: application/atom+xml; charset=utf-8'); + $this->startXml(); + } + + /** + * End the Atom feed + * + * @return void + */ + + function endAtom() + { + $this->elementEnd('feed'); + } + +} diff --git a/actions/apisearchjson.php b/actions/apisearchjson.php new file mode 100644 index 000000000..e44634684 --- /dev/null +++ b/actions/apisearchjson.php @@ -0,0 +1,154 @@ +. + * + * @category Search + * @package StatusNet + * @author Zach Copley + * @copyright 2008-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') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/apiprivateauth.php'; +require_once INSTALLDIR.'/lib/jsonsearchresultslist.php'; + +/** + * Action handler for Twitter-compatible API search + * + * @category Search + * @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 ApiAction + */ + +class ApiSearchJSONAction extends ApiPrivateAuthAction +{ + var $query; + var $lang; + var $rpp; + var $page; + var $since_id; + var $limit; + var $geocode; + + /** + * Initialization. + * + * @param array $args Web and URL arguments + * + * @return boolean true if nothing goes wrong + */ + + function prepare($args) + { + common_debug("apisearchjson prepare()"); + + parent::prepare($args); + + $this->query = $this->trimmed('q'); + $this->lang = $this->trimmed('lang'); + $this->rpp = $this->trimmed('rpp'); + + if (!$this->rpp) { + $this->rpp = 15; + } + + if ($this->rpp > 100) { + $this->rpp = 100; + } + + $this->page = $this->trimmed('page'); + + if (!$this->page) { + $this->page = 1; + } + + $this->since_id = $this->trimmed('since_id'); + $this->geocode = $this->trimmed('geocode'); + + return true; + } + + /** + * Handle a request + * + * @param array $args Arguments from $_REQUEST + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + $this->showResults(); + } + + /** + * Show search results + * + * @return void + */ + + function showResults() + { + + // TODO: Support search operators like from: and to:, boolean, etc. + + $notice = new Notice(); + + // lcase it for comparison + $q = strtolower($this->query); + + $search_engine = $notice->getSearchEngine('notice'); + $search_engine->set_sort_mode('chron'); + $search_engine->limit(($this->page - 1) * $this->rpp, $this->rpp + 1, true); + if (false === $search_engine->query($q)) { + $cnt = 0; + } else { + $cnt = $notice->find(); + } + + // TODO: since_id, lang, geocode + + $results = new JSONSearchResultsList($notice, $q, $this->rpp, $this->page); + + $this->initDocument('json'); + $results->show(); + $this->endDocument('json'); + } + + /** + * Do we need to write to the database? + * + * @return boolean true + */ + + function isReadOnly($args) + { + return true; + } +} diff --git a/actions/apitrends.php b/actions/apitrends.php new file mode 100644 index 000000000..5b74636c6 --- /dev/null +++ b/actions/apitrends.php @@ -0,0 +1,90 @@ +. + * + * @category Search + * @package StatusNet + * @author Zach Copley + * @copyright 2008-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') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/apiprivateauth.php'; + +/** + * Returns the top ten queries that are currently trending + * + * @category Search + * @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 ApiAction + */ + +class ApiTrendsAction extends ApiPrivateAuthAction +{ + + var $callback; + + /** + * Initialization. + * + * @param array $args Web and URL arguments + * + * @return boolean false if user doesn't exist + */ + function prepare($args) + { + parent::prepare($args); + return true; + } + + /** + * Handle a request + * + * @param array $args Arguments from $_REQUEST + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + $this->showTrends(); + } + + /** + * Output the trends + * + * @return void + */ + function showTrends() + { + $this->serverError(_('API method under construction.'), 501); + } + +} \ No newline at end of file diff --git a/actions/twitapisearchatom.php b/actions/twitapisearchatom.php deleted file mode 100644 index 51e8a8881..000000000 --- a/actions/twitapisearchatom.php +++ /dev/null @@ -1,402 +0,0 @@ -. - * - * @category Search - * @package StatusNet - * @author Zach Copley - * @copyright 2008-2009 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') && !defined('LACONICA')) { - exit(1); -} - -/** - * Action for outputting search results in Twitter compatible Atom - * format. - * - * TODO: abstract Atom stuff into a ruseable base class like - * RSS10Action. - * - * @category Search - * @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 ApiAction - */ - -class TwitapisearchatomAction extends ApiAction -{ - - var $cnt; - var $query; - var $lang; - var $rpp; - var $page; - var $since_id; - var $geocode; - - /** - * Constructor - * - * Just wraps the Action constructor. - * - * @param string $output URI to output to, default = stdout - * @param boolean $indent Whether to indent output, default true - * - * @see Action::__construct - */ - - function __construct($output='php://output', $indent=null) - { - parent::__construct($output, $indent); - } - - /** - * Do we need to write to the database? - * - * @return boolean true - */ - - function isReadonly() - { - return true; - } - - /** - * Read arguments and initialize members - * - * @param array $args Arguments from $_REQUEST - * - * @return boolean success - * - */ - - function prepare($args) - { - parent::prepare($args); - - $this->query = $this->trimmed('q'); - $this->lang = $this->trimmed('lang'); - $this->rpp = $this->trimmed('rpp'); - - if (!$this->rpp) { - $this->rpp = 15; - } - - if ($this->rpp > 100) { - $this->rpp = 100; - } - - $this->page = $this->trimmed('page'); - - if (!$this->page) { - $this->page = 1; - } - - // TODO: Suppport since_id -- we need to tweak the backend - // Search classes to support it. - - $this->since_id = $this->trimmed('since_id'); - $this->geocode = $this->trimmed('geocode'); - - // TODO: Also, language and geocode - - return true; - } - - /** - * Handle a request - * - * @param array $args Arguments from $_REQUEST - * - * @return void - */ - - function handle($args) - { - parent::handle($args); - $this->showAtom(); - } - - /** - * Get the notices to output as results. This also sets some class - * attrs so we can use them to calculate pagination, and output - * since_id and max_id. - * - * @return array an array of Notice objects sorted in reverse chron - */ - - function getNotices() - { - // TODO: Support search operators like from: and to:, boolean, etc. - - $notices = array(); - $notice = new Notice(); - - // lcase it for comparison - $q = strtolower($this->query); - - $search_engine = $notice->getSearchEngine('notice'); - $search_engine->set_sort_mode('chron'); - $search_engine->limit(($this->page - 1) * $this->rpp, - $this->rpp + 1, true); - if (false === $search_engine->query($q)) { - $this->cnt = 0; - } else { - $this->cnt = $notice->find(); - } - - $cnt = 0; - $this->max_id = 0; - - if ($this->cnt > 0) { - while ($notice->fetch()) { - - ++$cnt; - - if (!$this->max_id) { - $this->max_id = $notice->id; - } - - if ($cnt > $this->rpp) { - break; - } - - $notices[] = clone($notice); - } - } - - return $notices; - } - - /** - * Output search results as an Atom feed - * - * @return void - */ - - function showAtom() - { - $notices = $this->getNotices(); - - $this->initAtom(); - $this->showFeed(); - - foreach ($notices as $n) { - - $profile = $n->getProfile(); - - // Don't show notices from deleted users - - if (!empty($profile)) { - $this->showEntry($n); - } - } - - $this->endAtom(); - } - - /** - * Show feed specific Atom elements - * - * @return void - */ - - function showFeed() - { - // TODO: A9 OpenSearch stuff like search.twitter.com? - - $server = common_config('site', 'server'); - $sitename = common_config('site', 'name'); - - // XXX: Use xmlns:statusnet instead? - - $this->elementStart('feed', - array('xmlns' => 'http://www.w3.org/2005/Atom', - - // XXX: xmlns:twitter causes Atom validation to fail - // It's used for the source attr on notices - - 'xmlns:twitter' => 'http://api.twitter.com/', - 'xml:lang' => 'en-US')); // XXX Other locales ? - - $taguribase = TagURI::base(); - $this->element('id', null, "tag:$taguribase:search/$server"); - - $site_uri = common_path(false); - - $search_uri = $site_uri . 'api/search.atom?q=' . urlencode($this->query); - - if ($this->rpp != 15) { - $search_uri .= '&rpp=' . $this->rpp; - } - - // FIXME: this alternate link is not quite right because our - // web-based notice search doesn't support a rpp (responses per - // page) param yet - - $this->element('link', array('type' => 'text/html', - 'rel' => 'alternate', - 'href' => $site_uri . 'search/notice?q=' . - urlencode($this->query))); - - // self link - - $self_uri = $search_uri; - $self_uri .= ($this->page > 1) ? '&page=' . $this->page : ''; - - $this->element('link', array('type' => 'application/atom+xml', - 'rel' => 'self', - 'href' => $self_uri)); - - $this->element('title', null, "$this->query - $sitename Search"); - $this->element('updated', null, common_date_iso8601('now')); - - // XXX: The below "rel" links are not valid Atom, but it's what - // Twitter does... - - // refresh link - - $refresh_uri = $search_uri . "&since_id=" . $this->max_id; - - $this->element('link', array('type' => 'application/atom+xml', - 'rel' => 'refresh', - 'href' => $refresh_uri)); - - // pagination links - - if ($this->cnt > $this->rpp) { - - $next_uri = $search_uri . "&max_id=" . $this->max_id . - '&page=' . ($this->page + 1); - - $this->element('link', array('type' => 'application/atom+xml', - 'rel' => 'next', - 'href' => $next_uri)); - } - - if ($this->page > 1) { - - $previous_uri = $search_uri . "&max_id=" . $this->max_id . - '&page=' . ($this->page - 1); - - $this->element('link', array('type' => 'application/atom+xml', - 'rel' => 'previous', - 'href' => $previous_uri)); - } - - } - - /** - * Build an Atom entry similar to search.twitter.com's based on - * a given notice - * - * @param Notice $notice the notice to use - * - * @return void - */ - - function showEntry($notice) - { - $server = common_config('site', 'server'); - $profile = $notice->getProfile(); - $nurl = common_local_url('shownotice', array('notice' => $notice->id)); - - $this->elementStart('entry'); - - $taguribase = TagURI::base(); - - $this->element('id', null, "tag:$taguribase:$notice->id"); - $this->element('published', null, common_date_w3dtf($notice->created)); - $this->element('link', array('type' => 'text/html', - 'rel' => 'alternate', - 'href' => $nurl)); - $this->element('title', null, common_xml_safe_str(trim($notice->content))); - $this->element('content', array('type' => 'html'), $notice->rendered); - $this->element('updated', null, common_date_w3dtf($notice->created)); - $this->element('link', array('type' => 'image/png', - // XXX: Twitter uses rel="image" (not valid) - 'rel' => 'related', - 'href' => $profile->avatarUrl())); - - // @todo: Here is where we'd put in a link to an atom feed for threads - - $source = null; - - $ns = $notice->getSource(); - if ($ns) { - if (!empty($ns->name) && !empty($ns->url)) { - $source = '' - . htmlspecialchars($ns->name) - . ''; - } else { - $source = $ns->code; - } - } - - $this->element("twitter:source", null, $source); - - $this->elementStart('author'); - - $name = $profile->nickname; - - if ($profile->fullname) { - $name .= ' (' . $profile->fullname . ')'; - } - - $this->element('name', null, $name); - $this->element('uri', null, common_profile_uri($profile)); - $this->elementEnd('author'); - - $this->elementEnd('entry'); - } - - /** - * Initialize the Atom output, send headers - * - * @return void - */ - - function initAtom() - { - header('Content-Type: application/atom+xml; charset=utf-8'); - $this->startXml(); - } - - /** - * End the Atom feed - * - * @return void - */ - - function endAtom() - { - $this->elementEnd('feed'); - } - -} diff --git a/actions/twitapisearchjson.php b/actions/twitapisearchjson.php deleted file mode 100644 index b5c006aa7..000000000 --- a/actions/twitapisearchjson.php +++ /dev/null @@ -1,151 +0,0 @@ -. - * - * @category Search - * @package StatusNet - * @author Zach Copley - * @copyright 2008-2009 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') && !defined('LACONICA')) { - exit(1); -} - -require_once INSTALLDIR.'/lib/jsonsearchresultslist.php'; - -/** - * Action handler for Twitter-compatible API search - * - * @category Search - * @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 ApiAction - */ - -class TwitapisearchjsonAction extends ApiAction -{ - var $query; - var $lang; - var $rpp; - var $page; - var $since_id; - var $limit; - var $geocode; - - /** - * Initialization. - * - * @param array $args Web and URL arguments - * - * @return boolean true if nothing goes wrong - */ - - function prepare($args) - { - parent::prepare($args); - - $this->query = $this->trimmed('q'); - $this->lang = $this->trimmed('lang'); - $this->rpp = $this->trimmed('rpp'); - - if (!$this->rpp) { - $this->rpp = 15; - } - - if ($this->rpp > 100) { - $this->rpp = 100; - } - - $this->page = $this->trimmed('page'); - - if (!$this->page) { - $this->page = 1; - } - - $this->since_id = $this->trimmed('since_id'); - $this->geocode = $this->trimmed('geocode'); - - return true; - } - - /** - * Handle a request - * - * @param array $args Arguments from $_REQUEST - * - * @return void - */ - - function handle($args) - { - parent::handle($args); - $this->showResults(); - } - - /** - * Show search results - * - * @return void - */ - - function showResults() - { - - // TODO: Support search operators like from: and to:, boolean, etc. - - $notice = new Notice(); - - // lcase it for comparison - $q = strtolower($this->query); - - $search_engine = $notice->getSearchEngine('notice'); - $search_engine->set_sort_mode('chron'); - $search_engine->limit(($this->page - 1) * $this->rpp, $this->rpp + 1, true); - if (false === $search_engine->query($q)) { - $cnt = 0; - } else { - $cnt = $notice->find(); - } - - // TODO: since_id, lang, geocode - - $results = new JSONSearchResultsList($notice, $q, $this->rpp, $this->page); - - $this->initDocument('json'); - $results->show(); - $this->endDocument('json'); - } - - /** - * Do we need to write to the database? - * - * @return boolean true - */ - - function isReadOnly($args) - { - return true; - } -} diff --git a/actions/twitapitrends.php b/actions/twitapitrends.php deleted file mode 100644 index 5a04569a2..000000000 --- a/actions/twitapitrends.php +++ /dev/null @@ -1,88 +0,0 @@ -. - * - * @category Search - * @package StatusNet - * @author Zach Copley - * @copyright 2008-2009 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') && !defined('LACONICA')) { - exit(1); -} - -/** - * Returns the top ten queries that are currently trending - * - * @category Search - * @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 ApiAction - */ - -class TwitapitrendsAction extends ApiAction -{ - - var $callback; - - /** - * Initialization. - * - * @param array $args Web and URL arguments - * - * @return boolean false if user doesn't exist - */ - function prepare($args) - { - parent::prepare($args); - return true; - } - - /** - * Handle a request - * - * @param array $args Arguments from $_REQUEST - * - * @return void - */ - - function handle($args) - { - parent::handle($args); - $this->showTrends(); - } - - /** - * Output the trends - * - * @return void - */ - function showTrends() - { - $this->serverError(_('API method under construction.'), $code = 501); - } - -} \ No newline at end of file diff --git a/lib/router.php b/lib/router.php index fec229c9b..7e1e6a2a4 100644 --- a/lib/router.php +++ b/lib/router.php @@ -667,9 +667,9 @@ class Router ); // search - $m->connect('api/search.atom', array('action' => 'twitapisearchatom')); - $m->connect('api/search.json', array('action' => 'twitapisearchjson')); - $m->connect('api/trends.json', array('action' => 'twitapitrends')); + $m->connect('api/search.atom', array('action' => 'ApiSearchAtom')); + $m->connect('api/search.json', array('action' => 'ApiSearchJSON')); + $m->connect('api/trends.json', array('action' => 'ApiTrends')); $m->connect('api/oauth/request_token', array('action' => 'apioauthrequesttoken')); -- cgit v1.2.3-54-g00ecf From f0c5e7eca3842411d4e3ee6efb3fd6ac0ad85f4a Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Sat, 26 Jun 2010 15:07:32 -0400 Subject: Fix for bug #2382: releasing claim on failed queue item works again with DB-based queues. Warning: DB-based queue doesn't currently implement discarding of items after a retry limit. Failed items will be retried until they succeed. --- classes/Queue_item.php | 13 +++++++++++++ lib/dbqueuemanager.php | 4 +--- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/classes/Queue_item.php b/classes/Queue_item.php index f83c2cef1..c7e17be6e 100644 --- a/classes/Queue_item.php +++ b/classes/Queue_item.php @@ -64,4 +64,17 @@ class Queue_item extends Memcached_DataObject $qi = null; return null; } + + /** + * Release a claimed item. + */ + function releaseCLaim() + { + // DB_DataObject doesn't let us save nulls right now + $sql = sprintf("UPDATE queue_item SET claimed=NULL WHERE id=%d", $this->id); + $this->query($sql); + + $this->claimed = null; + $this->encache(); + } } diff --git a/lib/dbqueuemanager.php b/lib/dbqueuemanager.php index 3032e4ec7..3dda9fd1a 100644 --- a/lib/dbqueuemanager.php +++ b/lib/dbqueuemanager.php @@ -135,9 +135,7 @@ class DBQueueManager extends QueueManager if (empty($qi->claimed)) { $this->_log(LOG_WARNING, "[$queue:item $qi->id] Ignoring failure for unclaimed queue item"); } else { - $orig = clone($qi); - $qi->claimed = null; - $qi->update($orig); + $qi->releaseClaim(); } $this->stats('error', $queue); -- cgit v1.2.3-54-g00ecf From 9c7b66984c46668f314f93337d28c62854b6d134 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 10 May 2010 16:18:29 -0700 Subject: Enhanced upload file type detection. If given an original filename, we'll attempt to detect type from the extension if we were unable to make a definitive match from content. Generic octet-stream, zip, and MS Office type are explicitly singled out for re-checks, which fixes OpenOffice and MS Office documents to come up with the proper types when misdetected. File extensions can also be added to the upload type whitelist; they'll be normalized to types for the actual comparison, so only known extensions will work. --- lib/mediafile.php | 54 +++++++++++++++++++++++++++-- tests/MediaFileTest.php | 73 +++++++++++++++++++++++++--------------- tests/sample-uploads/image.gif | Bin 0 -> 35 bytes tests/sample-uploads/image.jpeg | Bin 0 -> 306 bytes tests/sample-uploads/image.jpg | Bin 0 -> 306 bytes tests/sample-uploads/image.png | Bin 0 -> 159 bytes 6 files changed, 97 insertions(+), 30 deletions(-) create mode 100644 tests/sample-uploads/image.gif create mode 100644 tests/sample-uploads/image.jpeg create mode 100644 tests/sample-uploads/image.jpg create mode 100644 tests/sample-uploads/image.png (limited to 'lib') diff --git a/lib/mediafile.php b/lib/mediafile.php index 10d90d008..85d673d92 100644 --- a/lib/mediafile.php +++ b/lib/mediafile.php @@ -180,7 +180,8 @@ class MediaFile return; } - $mimetype = MediaFile::getUploadedFileType($_FILES[$param]['tmp_name']); + $mimetype = MediaFile::getUploadedFileType($_FILES[$param]['tmp_name'], + $_FILES[$param]['name']); $filename = null; @@ -241,19 +242,41 @@ class MediaFile return new MediaFile($user, $filename, $mimetype); } - static function getUploadedFileType($f) { + /** + * Attempt to identify the content type of a given file. + * + * @param mixed $f file handle resource, or filesystem path as string + * @param string $originalFilename (optional) for extension-based detection + * @return string + * + * @fixme is this an internal or public method? It's called from GetFileAction + * @fixme this seems to tie a front-end error message in, kinda confusing + * @fixme this looks like it could return a PEAR_Error in some cases, if + * type can't be identified and $config['attachments']['supported'] is true + * + * @throws ClientException if type is known, but not supported for local uploads + */ + static function getUploadedFileType($f, $originalFilename=false) { require_once 'MIME/Type.php'; + require_once 'MIME/Type/Extension.php'; + $mte = new MIME_Type_Extension(); $cmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd'); $cmd = common_config('attachments', 'filecommand'); $filetype = null; + // If we couldn't get a clear type from the file extension, + // we'll go ahead and try checking the content. Content checks + // are unambiguous for most image files, but nearly useless + // for office document formats. + if (is_string($f)) { // assuming a filename $filetype = MIME_Type::autoDetect($f); + } else { // assuming a filehandle @@ -262,7 +285,32 @@ class MediaFile $filetype = MIME_Type::autoDetect($stream['uri']); } - if (common_config('attachments', 'supported') === true || in_array($filetype, common_config('attachments', 'supported'))) { + // The content-based sources for MIME_Type::autoDetect() + // are wildly unreliable for office-type documents. If we've + // gotten an unclear reponse back or just couldn't identify it, + // we'll try detecting a type from its extension... + $unclearTypes = array('application/octet-stream', + 'application/vnd.ms-office', + 'application/zip'); + + if ($originalFilename && (!$filetype || in_array($filetype, $unclearTypes))) { + $type = $mte->getMIMEType($originalFilename); + if (is_string($type)) { + $filetype = $type; + } + } + + $supported = common_config('attachments', 'supported'); + if (is_array($supported)) { + // Normalize extensions to mime types + foreach ($supported as $i => $entry) { + if (strpos($entry, '/') === false) { + common_log(LOG_INFO, "sample.$entry"); + $supported[$i] = $mte->getMIMEType("sample.$entry"); + } + } + } + if ($supported === true || in_array($filetype, $supported)) { return $filetype; } $media = MIME_Type::getMedia($filetype); diff --git a/tests/MediaFileTest.php b/tests/MediaFileTest.php index 6fe995621..a76a4f45e 100644 --- a/tests/MediaFileTest.php +++ b/tests/MediaFileTest.php @@ -34,43 +34,62 @@ class MediaFileTest extends PHPUnit_Framework_TestCase if (!file_exists($filename)) { throw new Exception("WTF? $filename test file missing"); } - $this->assertEquals($expectedType, MediaFile::getUploadedFileType($filename)); + + $type = MediaFile::getUploadedFileType($filename, basename($filename)); + $this->assertEquals($expectedType, $type); + } + + /** + * @dataProvider fileTypeCases + * + */ + public function testUploadedFileType($filename, $expectedType) + { + if (!file_exists($filename)) { + throw new Exception("WTF? $filename test file missing"); + } + $tmp = tmpfile(); + fwrite($tmp, file_get_contents($filename)); + + $type = MediaFile::getUploadedFileType($tmp, basename($filename)); + $this->assertEquals($expectedType, $type); } static public function fileTypeCases() { $base = dirname(__FILE__); $dir = "$base/sample-uploads"; - return array( - array("$dir/office.pdf", "application/pdf"), + $files = array( + "image.png" => "image/png", + "image.gif" => "image/gif", + "image.jpg" => "image/jpeg", + "image.jpeg" => "image/jpeg", + + "office.pdf" => "application/pdf", - array("$dir/wordproc.odt", "application/vnd.oasis.opendocument.text"), - array("$dir/wordproc.ott", "application/vnd.oasis.opendocument.text-template"), - array("$dir/wordproc.doc", "application/msword"), - array("$dir/wordproc.docx", - "application/vnd.openxmlformats-officedocument.wordprocessingml.document"), - array("$dir/wordproc.rtf", "text/rtf"), + "wordproc.odt" => "application/vnd.oasis.opendocument.text", + "wordproc.ott" => "application/vnd.oasis.opendocument.text-template", + "wordproc.doc" => "application/msword", + "wordproc.docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "wordproc.rtf" => "text/rtf", - array("$dir/spreadsheet.ods", - "application/vnd.oasis.opendocument.spreadsheet"), - array("$dir/spreadsheet.ots", - "application/vnd.oasis.opendocument.spreadsheet-template"), - array("$dir/spreadsheet.xls", "application/vnd.ms-excel"), - array("$dir/spreadsheet.xlt", "application/vnd.ms-excel"), - array("$dir/spreadsheet.xlsx", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"), + "spreadsheet.ods" => "application/vnd.oasis.opendocument.spreadsheet", + "spreadsheet.ots" => "application/vnd.oasis.opendocument.spreadsheet-template", + "spreadsheet.xls" => "application/vnd.ms-excel", + "spreadsheet.xlt" => "application/vnd.ms-excel", + "spreadsheet.xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - array("$dir/presentation.odp", - "application/vnd.oasis-opendocument.presentation"), - array("$dir/presentation.otp", - "application/vnd.oasis-opendocument.presentation-template"), - array("$dir/presentation.ppt", - "application/vnd.ms-powerpoint"), - array("$dir/presentation.pot", - "application/vnd.ms-powerpoint"), - array("$dir/presentation.pptx", - "application/vnd.openxmlformats-officedocument.presentationml.presentation"), + "presentation.odp" => "application/vnd.oasis.opendocument.presentation", + "presentation.otp" => "application/vnd.oasis.opendocument.presentation-template", + "presentation.ppt" => "application/vnd.ms-powerpoint", + "presentation.pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation", ); + + $dataset = array(); + foreach ($files as $file => $type) { + $dataset[] = array("$dir/$file", $type); + } + return $dataset; } } diff --git a/tests/sample-uploads/image.gif b/tests/sample-uploads/image.gif new file mode 100644 index 000000000..b636f4b8d Binary files /dev/null and b/tests/sample-uploads/image.gif differ diff --git a/tests/sample-uploads/image.jpeg b/tests/sample-uploads/image.jpeg new file mode 100644 index 000000000..21fcb5aef Binary files /dev/null and b/tests/sample-uploads/image.jpeg differ diff --git a/tests/sample-uploads/image.jpg b/tests/sample-uploads/image.jpg new file mode 100644 index 000000000..21fcb5aef Binary files /dev/null and b/tests/sample-uploads/image.jpg differ diff --git a/tests/sample-uploads/image.png b/tests/sample-uploads/image.png new file mode 100644 index 000000000..60cbcfd17 Binary files /dev/null and b/tests/sample-uploads/image.png differ -- cgit v1.2.3-54-g00ecf From b2ad8ec571bd68a92e433e817531151ebf8aa4d0 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 28 Jun 2010 14:41:33 -0400 Subject: Fix for PHP notice when given an integer degrees in decimalDegreesToDMS(); using math instead of string manipulation to split integer portion from decimal remainder. --- lib/noticelist.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/noticelist.php b/lib/noticelist.php index 432ea78d5..e23cf3b6d 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -463,12 +463,14 @@ class NoticeListItem extends Widget $this->out->elementEnd('span'); } + /** + * @param number $dec decimal degrees + * @return array split into 'deg', 'min', and 'sec' + */ function decimalDegreesToDMS($dec) { - - $vars = explode(".",$dec); - $deg = $vars[0]; - $tempma = "0.".$vars[1]; + $deg = intval($dec); + $tempma = abs($dec) - abs($deg); $tempma = $tempma * 3600; $min = floor($tempma / 60); -- cgit v1.2.3-54-g00ecf From c9863bb864d67e5a88f5f9647ba9727d740692a2 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 28 Jun 2010 15:08:01 -0400 Subject: Update version and code name: 0.9.3 Half a World Away --- README | 4 ++-- lib/common.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/README b/README index c47d3499f..452123db4 100644 --- a/README +++ b/README @@ -2,8 +2,8 @@ README ------ -StatusNet 0.9.2 ("King of Birds") -3 May 2010 +StatusNet 0.9.3 ("Half a World Away") +29 June 2010 This is the README file for StatusNet, the Open Source microblogging platform. It includes installation instructions, descriptions of diff --git a/lib/common.php b/lib/common.php index 064f6f73a..817434b97 100644 --- a/lib/common.php +++ b/lib/common.php @@ -22,10 +22,10 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } //exit with 200 response, if this is checking fancy from the installer if (isset($_REQUEST['p']) && $_REQUEST['p'] == 'check-fancy') { exit; } -define('STATUSNET_VERSION', '0.9.2'); +define('STATUSNET_VERSION', '0.9.3'); define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility -define('STATUSNET_CODENAME', 'King of Birds'); +define('STATUSNET_CODENAME', 'Half a World Away'); define('AVATAR_PROFILE_SIZE', 96); define('AVATAR_STREAM_SIZE', 48); -- cgit v1.2.3-54-g00ecf From 923d9ef71ce49bfa55460bf42acb8a35bf3e84be Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 28 Jun 2010 16:53:05 -0700 Subject: - Fix bugs with block and friendship API methods - Friendship API methods now use a Profile instead of User for target --- actions/apiblockcreate.php | 4 ++-- actions/apiblockdestroy.php | 4 ++-- actions/apifriendshipscreate.php | 6 +++--- actions/apifriendshipsdestroy.php | 7 +++---- actions/apifriendshipsexists.php | 19 ++++++++----------- lib/apiaction.php | 28 ++++++++++++++++++++++++++++ 6 files changed, 46 insertions(+), 22 deletions(-) (limited to 'lib') diff --git a/actions/apiblockcreate.php b/actions/apiblockcreate.php index c26485f59..b355cd1c7 100644 --- a/actions/apiblockcreate.php +++ b/actions/apiblockcreate.php @@ -23,7 +23,7 @@ * @package StatusNet * @author Evan Prodromou * @author Zach Copley - * @copyright 2009 StatusNet, Inc. + * @copyright 2009-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/ */ @@ -65,7 +65,7 @@ class ApiBlockCreateAction extends ApiAuthAction parent::prepare($args); $this->user = $this->auth_user; - $this->other = $this->getTargetUser($this->arg('id')); + $this->other = $this->getTargetProfile($this->arg('id')); return true; } diff --git a/actions/apiblockdestroy.php b/actions/apiblockdestroy.php index 666f308f4..7ea201677 100644 --- a/actions/apiblockdestroy.php +++ b/actions/apiblockdestroy.php @@ -23,7 +23,7 @@ * @package StatusNet * @author Evan Prodromou * @author Zach Copley - * @copyright 2009 StatusNet, Inc. + * @copyright 2009-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/ */ @@ -64,7 +64,7 @@ class ApiBlockDestroyAction extends ApiAuthAction parent::prepare($args); $this->user = $this->auth_user; - $this->other = $this->getTargetUser($this->arg('id')); + $this->other = $this->getTargetProfile($this->arg('id')); return true; } diff --git a/actions/apifriendshipscreate.php b/actions/apifriendshipscreate.php index 1de2cc32e..a7ec5b28a 100644 --- a/actions/apifriendshipscreate.php +++ b/actions/apifriendshipscreate.php @@ -24,7 +24,7 @@ * @author Dan Moore * @author Evan Prodromou * @author Zach Copley - * @copyright 2009 StatusNet, Inc. + * @copyright 2009-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/ */ @@ -67,7 +67,7 @@ class ApiFriendshipsCreateAction extends ApiAuthAction parent::prepare($args); $this->user = $this->auth_user; - $this->other = $this->getTargetUser($id); + $this->other = $this->getTargetProfile($this->arg('id')); return true; } @@ -106,7 +106,7 @@ class ApiFriendshipsCreateAction extends ApiAuthAction if (empty($this->other)) { $this->clientError( - _('Could not follow user: User not found.'), + _('Could not follow user: profile not found.'), 403, $this->format ); diff --git a/actions/apifriendshipsdestroy.php b/actions/apifriendshipsdestroy.php index d48a57756..551d01682 100644 --- a/actions/apifriendshipsdestroy.php +++ b/actions/apifriendshipsdestroy.php @@ -24,7 +24,7 @@ * @author Dan Moore * @author Evan Prodromou * @author Zach Copley - * @copyright 2009 StatusNet, Inc. + * @copyright 2009-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/ */ @@ -67,7 +67,7 @@ class ApiFriendshipsDestroyAction extends ApiAuthAction parent::prepare($args); $this->user = $this->auth_user; - $this->other = $this->getTargetUser($id); + $this->other = $this->getTargetProfile($this->arg('id')); return true; } @@ -125,8 +125,7 @@ class ApiFriendshipsDestroyAction extends ApiAuthAction } // throws an exception on error - Subscription::cancel($this->user->getProfile(), - $this->other->getProfile()); + Subscription::cancel($this->user->getProfile(), $this->other); $this->initDocument($this->format); $this->showProfile($this->other, $this->format); diff --git a/actions/apifriendshipsexists.php b/actions/apifriendshipsexists.php index ca62b5f51..725178fd4 100644 --- a/actions/apifriendshipsexists.php +++ b/actions/apifriendshipsexists.php @@ -24,7 +24,7 @@ * @author Dan Moore * @author Evan Prodromou * @author Zach Copley - * @copyright 2009 StatusNet, Inc. + * @copyright 2009-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/ */ @@ -50,8 +50,8 @@ require_once INSTALLDIR . '/lib/apiprivateauth.php'; class ApiFriendshipsExistsAction extends ApiPrivateAuthAction { - var $user_a = null; - var $user_b = null; + var $profile_a = null; + var $profile_b = null; /** * Take arguments for running @@ -66,11 +66,8 @@ class ApiFriendshipsExistsAction extends ApiPrivateAuthAction { parent::prepare($args); - $user_a_id = $this->trimmed('user_a'); - $user_b_id = $this->trimmed('user_b'); - - $this->user_a = $this->getTargetUser($user_a_id); - $this->user_b = $this->getTargetUser($user_b_id); + $this->profile_a = $this->getTargetProfile($this->trimmed('user_a')); + $this->profile_b = $this->getTargetProfile($this->trimmed('user_b')); return true; } @@ -89,16 +86,16 @@ class ApiFriendshipsExistsAction extends ApiPrivateAuthAction { parent::handle($args); - if (empty($this->user_a) || empty($this->user_b)) { + if (empty($this->profile_a) || empty($this->profile_b)) { $this->clientError( - _('Two user ids or screen_names must be supplied.'), + _('Two valid IDs or screen_names must be supplied.'), 400, $this->format ); return; } - $result = $this->user_a->isSubscribed($this->user_b); + $result = Subscription::exists($this->profile_a, $this->profile_b); switch ($this->format) { case 'xml': diff --git a/lib/apiaction.php b/lib/apiaction.php index 89a487102..8de13a62d 100644 --- a/lib/apiaction.php +++ b/lib/apiaction.php @@ -1374,6 +1374,34 @@ class ApiAction extends Action } } + function getTargetProfile($id) + { + if (empty($id)) { + + // Twitter supports these other ways of passing the user ID + if (is_numeric($this->arg('id'))) { + return Profile::staticGet($this->arg('id')); + } else if ($this->arg('id')) { + $nickname = common_canonical_nickname($this->arg('id')); + return Profile::staticGet('nickname', $nickname); + } else if ($this->arg('user_id')) { + // This is to ensure that a non-numeric user_id still + // overrides screen_name even if it doesn't get used + if (is_numeric($this->arg('user_id'))) { + return Profile::staticGet('id', $this->arg('user_id')); + } + } else if ($this->arg('screen_name')) { + $nickname = common_canonical_nickname($this->arg('screen_name')); + return Profile::staticGet('nickname', $nickname); + } + } else if (is_numeric($id)) { + return Profile::staticGet($id); + } else { + $nickname = common_canonical_nickname($id); + return Profile::staticGet('nickname', $nickname); + } + } + function getTargetGroup($id) { if (empty($id)) { -- cgit v1.2.3-54-g00ecf