Commit 46962e61 authored by hannes's avatar hannes

easier installation, cover photos now work with 1.1.1-alpha2

parent f6d86255
......@@ -203,9 +203,53 @@ class QvitterPlugin extends Plugin {
return true;
}
/**
* Group addresses in API response
*
* @param Action $action action being executed
*
* @return boolean hook return
*/
function onNoticeSimpleStatusArray($notice, &$twitter_status)
{
$notice_groups = $notice->getGroups();
$group_addressees = false;
foreach($notice_groups as $g) {
$group_addressees .= '!'.$g->nickname.' ';
}
$group_addressees = trim($group_addressees);
if($group_addressees == '') $group_addressees = false;
$twitter_status['statusnet_in_groups'] = $group_addressees;
return true;
}
/**
* Cover photo in API response
*
* @param Action $action action being executed
*
* @return boolean hook return
*/
function onTwitterUserArray($profile, &$twitter_user)
{
$twitter_user['cover_photo'] = Profile_prefs::getConfigData($profile, 'qvitter', 'cover_photo');
return true;
}
}
/**
* Overwrites variables in URL-mapping
*
......
......@@ -2,7 +2,7 @@ Qvitter
==========================================
* Author: Hannes Mannerheim (<h@nnesmannerhe.im>)
* Last mod.: May, 2014
* Last mod.: Sept, 2014
* Version: 3
* GitHub: <https://github.com/hannesmannerheim/qvitter>
......@@ -37,14 +37,7 @@ be opt-in instead.
6. Users can go to ://{instance}/settings/qvitter and enable or disable Qvitter.
Extras
-----
1. There is a bug in GNUsocial that won't let you see groups' members lists. Replace your
actions/apigroupmembership.php file with the one supplied to fix it.
2. If you want notice headers to show which group a notice is posted in, and if you want
cover photos to work, replace your lib/apiaction.php file.
Note: Qvitter is tested with GNU Social version 1.1.1-alpha2 (7e47026085fa4f2071e694d9c3e3fe2aa5142135).
TODO
......
......@@ -98,7 +98,7 @@ class ApiUpdateCoverPhotoAction extends ApiAuthAction
$base64img_mimetype = MediaFile::getUploadedMimeType($base64img_path, $base64img_filename);
$mediafile = new MediaFile($profile, $base64img_filename, $base64img_mimetype);
$imagefile = new ImageFile($mediafile->fileRecord->id, File::path($mediafile->filename));
$imagefile->resizeTo(File::path($mediafile->filename), $this->cropW, $this->cropH, $this->cropX, $this->cropY, $this->cropW, $this->cropH);
$imagefile->resizeTo(File::path($mediafile->filename), array($this->cropW, $this->cropH, $this->cropX, $this->cropY, $this->cropW, $this->cropH));
$result['url'] = File::url($mediafile->filename);
Profile_prefs::setData($profile, 'qvitter', 'cover_photo', $result['url']);
......
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* List a group's members
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @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);
}
/**
* List 20 newest members of the group specified by name or ID.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiGroupMembershipAction extends ApiPrivateAuthAction
{
var $group = null;
var $profiles = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->group = $this->getTargetGroup($this->arg('id'));
if (empty($this->group)) {
// TRANS: Client error displayed trying to show group membership on a non-existing group.
$this->clientError(_('Group not found.'), 404);
}
$this->profiles = $this->getProfiles();
return true;
}
/**
* Handle the request
*
* Show the members of the group
*
* @return void
*/
protected function handle()
{
parent::handle();
// XXX: RSS and Atom
switch($this->format) {
case 'xml':
$this->showTwitterXmlUsers($this->profiles);
break;
case 'json':
$this->showJsonUsers($this->profiles[0]->_items);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
/**
* Fetch the members of a group
*
* @return array $profiles list of profiles
*/
function getProfiles()
{
$profiles = array();
$profile = $this->group->getMembers(
($this->page - 1) * $this->count,
$this->count,
$this->since_id,
$this->max_id
);
while ($profile->fetch()) {
$profiles[] = clone($profile);
}
return $profiles;
}
/**
* Is this action read only?
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* When was this list of profiles last modified?
*
* @return string datestamp of the lastest profile in the group
*/
function lastModified()
{
if (!empty($this->profiles) && (count($this->profiles) > 0)) {
return strtotime($this->profiles[0]->created);
}
return null;
}
/**
* An entity tag for this list of groups
*
* Returns an Etag based on the action name, language
* the group id, and timestamps of the first and last
* user who has joined the group
*
* @return string etag
*/
function etag()
{
if (!empty($this->profiles) && (count($this->profiles) > 0)) {
$last = count($this->profiles) - 1;
return '"' . implode(
':',
array($this->arg('action'),
common_user_cache_hash($this->auth_user),
common_language(),
$this->group->id,
strtotime($this->profiles[0]->created),
strtotime($this->profiles[$last]->created))
)
. '"';
}
return null;
}
}
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Base API action
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Toby Inkster <mail@tobyinkster.co.uk>
* @author Zach Copley <zach@status.net>
* @copyright 2009-2010 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
/* External API usage documentation. Please update when you change how the API works. */
/*! @mainpage StatusNet REST API
@section Introduction
Some explanatory text about the API would be nice.
@section API Methods
@subsection timelinesmethods_sec Timeline Methods
@li @ref publictimeline
@li @ref friendstimeline
@subsection statusmethods_sec Status Methods
@li @ref statusesupdate
@subsection usermethods_sec User Methods
@subsection directmessagemethods_sec Direct Message Methods
@subsection friendshipmethods_sec Friendship Methods
@subsection socialgraphmethods_sec Social Graph Methods
@subsection accountmethods_sec Account Methods
@subsection favoritesmethods_sec Favorites Methods
@subsection blockmethods_sec Block Methods
@subsection oauthmethods_sec OAuth Methods
@subsection helpmethods_sec Help Methods
@subsection groupmethods_sec Group Methods
@page apiroot API Root
The URLs for methods referred to in this API documentation are
relative to the StatusNet API root. The API root is determined by the
site's @b server and @b path variables, which are generally specified
in config.php. For example:
@code
$config['site']['server'] = 'example.org';
$config['site']['path'] = 'statusnet'
@endcode
The pattern for a site's API root is: @c protocol://server/path/api E.g:
@c http://example.org/statusnet/api
The @b path can be empty. In that case the API root would simply be:
@c http://example.org/api
*/
if (!defined('STATUSNET')) {
exit(1);
}
class ApiValidationException extends Exception { }
/**
* Contains most of the Twitter-compatible API output functions.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Toby Inkster <mail@tobyinkster.co.uk>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiAction extends Action
{
const READ_ONLY = 1;
const READ_WRITE = 2;
var $user = null;
var $auth_user = null;
var $page = null;
var $count = null;
var $max_id = null;
var $since_id = null;
var $source = null;
var $callback = null;
var $access = self::READ_ONLY; // read (default) or read-write
static $reserved_sources = array('web', 'omb', 'ostatus', 'mail', 'xmpp', 'api');
/**
* Initialization.
*
* @param array $args Web and URL arguments
*
* @return boolean false if user doesn't exist
*/
protected function prepare(array $args=array())
{
StatusNet::setApi(true); // reduce exception reports to aid in debugging
parent::prepare($args);
$this->format = $this->arg('format');
$this->callback = $this->arg('callback');
$this->page = (int)$this->arg('page', 1);
$this->count = (int)$this->arg('count', 20);
$this->max_id = (int)$this->arg('max_id', 0);
$this->since_id = (int)$this->arg('since_id', 0);
if ($this->arg('since')) {
header('X-StatusNet-Warning: since parameter is disabled; use since_id');
}
$this->source = $this->trimmed('source');
if (empty($this->source) || in_array($this->source, self::$reserved_sources)) {
$this->source = 'api';
}
return true;
}
/**
* Handle a request
*
* @param array $args Arguments from $_REQUEST
*
* @return void
*/
protected function handle()
{
header('Access-Control-Allow-Origin: *');
parent::handle();
}
/**
* Overrides XMLOutputter::element to write booleans as strings (true|false).
* See that method's documentation for more info.
*
* @param string $tag Element type or tagname
* @param array $attrs Array of element attributes, as
* key-value pairs
* @param string $content string content of the element
*
* @return void
*/
function element($tag, $attrs=null, $content=null)
{
if (is_bool($content)) {
$content = ($content ? 'true' : 'false');
}
return parent::element($tag, $attrs, $content);
}
function twitterUserArray($profile, $get_notice=false)
{
$twitter_user = array();
try {
$user = $profile->getUser();
} catch (NoSuchUserException $e) {
$user = null;
}
$twitter_user['id'] = intval($profile->id);
$twitter_user['name'] = $profile->getBestName();
$twitter_user['screen_name'] = $profile->nickname;
$twitter_user['location'] = ($profile->location) ? $profile->location : null;
$twitter_user['description'] = ($profile->bio) ? $profile->bio : null;
// TODO: avatar url template (example.com/user/avatar?size={x}x{y})
$twitter_user['profile_image_url'] = Avatar::urlByProfile($profile, AVATAR_STREAM_SIZE);
$twitter_user['profile_image_url_https'] = $twitter_user['profile_image_url'];
// START introduced by qvitter API, not necessary for StatusNet API
$twitter_user['profile_image_url_profile_size'] = Avatar::urlByProfile($profile, AVATAR_PROFILE_SIZE);
try {
$avatar = Avatar::getUploaded($profile);
$origurl = $avatar->displayUrl();
} catch (Exception $e) {
$origurl = $twitter_user['profile_image_url_profile_size'];
}
$twitter_user['profile_image_url_original'] = $origurl;
$twitter_user['groups_count'] = $profile->getGroupCount();
foreach (array('linkcolor', 'backgroundcolor') as $key) {
$twitter_user[$key] = Profile_prefs::getConfigData($profile, 'theme', $key);
}
$twitter_user['cover_photo'] = Profile_prefs::getConfigData($profile, 'qvitter', 'cover_photo');
// END introduced by qvitter API, not necessary for StatusNet API
$twitter_user['url'] = ($profile->homepage) ? $profile->homepage : null;
$twitter_user['protected'] = (!empty($user) && $user->private_stream) ? true : false;
$twitter_user['followers_count'] = $profile->subscriberCount();
// Note: some profiles don't have an associated user
$twitter_user['friends_count'] = $profile->subscriptionCount();
$twitter_user['created_at'] = $this->dateTwitter($profile->created);
$twitter_user['favourites_count'] = $profile->faveCount(); // British spelling!
$timezone = 'UTC';
if (!empty($user) && $user->timezone) {
$timezone = $user->timezone;
}
$t = new DateTime;
$t->setTimezone(new DateTimeZone($timezone));
$twitter_user['utc_offset'] = $t->format('Z');
$twitter_user['time_zone'] = $timezone;
$twitter_user['statuses_count'] = $profile->noticeCount();
// 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' =>
$this->auth_user->id,
'subscribed' => $profile->id));
if ($sub) {
$twitter_user['notifications'] = ($sub->jabber || $sub->sms);
}
}
if ($get_notice) {
$notice = $profile->getCurrentNotice();
if ($notice instanceof Notice) {
// don't get user!
$twitter_user['status'] = $this->twitterStatusArray($notice, false);
}
}
// StatusNet-specific
$twitter_user['statusnet_profile_url'] = $profile->profileurl;
return $twitter_user;
}
function twitterStatusArray($notice, $include_user=true)
{
$base = $this->twitterSimpleStatusArray($notice, $include_user);
if (!empty($notice->repeat_of)) {
$original = Notice::getKV('id', $notice->repeat_of);
if (!empty($original)) {
$original_array = $this->twitterSimpleStatusArray($original, $include_user);
$base['retweeted_status'] = $original_array;
}
}
return $base;
}
function twitterSimpleStatusArray($notice, $include_user=true)
{
$profile = $notice->getProfile();
$twitter_status = array();
$twitter_status['text'] = $notice->content;
$twitter_status['truncated'] = false; # Not possible on StatusNet
$twitter_status['created_at'] = $this->dateTwitter($notice->created);
try {
// We could just do $notice->reply_to but maybe the future holds a
// different story for parenting.
$parent = $notice->getParent();
$in_reply_to = $parent->id;
} catch (Exception $e) {
$in_reply_to = null;
}
$twitter_status['in_reply_to_status_id'] = $in_reply_to;
$source = null;
$ns = $notice->getSource();
if ($ns) {
if (!empty($ns->name) && !empty($ns->url)) {
$source = '<a href="'
. htmlspecialchars($ns->url)
. '" rel="nofollow">'
. htmlspecialchars($ns->name)
. '</a>';
} else {
$source = $ns->code;
}
}
$twitter_status['uri'] = $notice->getUri();
$twitter_status['source'] = $source;
$twitter_status['id'] = intval($notice->id);
$replier_profile = null;
if ($notice->reply_to) {
$reply = Notice::getKV(intval($notice->reply_to));
if ($reply) {
$replier_profile = $reply->getProfile();
}
}
$twitter_status['in_reply_to_user_id'] =
($replier_profile) ? intval($replier_profile->id) : null;
$twitter_status['in_reply_to_screen_name'] =
($replier_profile) ? $replier_profile->nickname : null;
if (isset($notice->lat) && isset($notice->lon)) {
// This is the format that GeoJSON expects stuff to be in
$twitter_status['geo'] = array('type' => 'Point',
'coordinates' => array((float) $notice->lat,
(float) $notice->lon));
} else {
$twitter_status['geo'] = null;
}
if (!is_null($this->scoped)) {
$twitter_status['favorited'] = $this->scoped->hasFave($notice);
$twitter_status['repeated'] = $this->scoped->hasRepeated($notice);
} else {
$twitter_status['favorited'] = false;
$twitter_status['repeated'] = false;
}
// Enclosures
$attachments = $notice->attachments();
if (!empty($attachments)) {
$twitter_status['attachments'] = array();
foreach ($attachments as $attachment) {
$enclosure_o=$attachment->getEnclosure();
if ($enclosure_o) {
$enclosure = array();
$enclosure['url'] = $enclosure_o->url;
$enclosure['mimetype'] = $enclosure_o->mimetype;
$enclosure['size'] = $enclosure_o->size;
$twitter_status['attachments'][] = $enclosure;
}
}
}
if ($include_user && $profile) {
// Don't get notice (recursive!)
$twitter_user = $this->twitterUserArray($profile, false);
$twitter_status['user'] = $twitter_user;
}
// StatusNet-specific
$notice_groups = $notice->getGroups();
$group_addressees = false;
foreach($notice_groups as $g) {
$group_addressees .= '!'.$g->nickname.' ';
}
$group_addressees = trim($group_addressees);
if($group_addressees == '') $group_addressees = false;
$twitter_status['statusnet_in_groups'] = $group_addressees;
$twitter_status['statusnet_html'] = $notice->rendered;
$twitter_status['statusnet_conversation_id'] = intval($notice->conversation);
return $twitter_status;
}
function twitterGroupArray($group)
{
$twitter_group = array();
$twitter_group['id'] = intval($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['admin_count'] = $group->getAdminCount();
$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;
}