From a37a4cb9f934da4e35d37d3a9d8f76977d1dc20a Mon Sep 17 00:00:00 2001 From: Hannes Mannerheim Date: Thu, 6 Apr 2017 13:45:13 +0200 Subject: [PATCH] remote profiles open locally, and "find someone" user search/remote follow input field --- QvitterPlugin.php | 17 ++- actions/apiexternalusershow.php | 27 +++-- actions/apiqvittertimelineuser.php | 181 +++++++++++++++++++++++++++++ actions/qvitter.php | 7 +- css/qvitter.css | 62 ++++++++++ js/dom-functions.js | 45 ++++++- js/misc-functions.js | 38 +++++- js/qvitter.js | 151 ++++++++++++++++++------ js/stream-router.js | 11 +- locale/ar.json | 5 +- locale/ast.json | 5 +- locale/ca.json | 5 +- locale/de.json | 5 +- locale/en.json | 5 +- locale/eo.json | 6 +- locale/es.json | 5 +- locale/es_419.json | 5 +- locale/es_ahorita.json | 5 +- locale/eu.json | 5 +- locale/fa.json | 5 +- locale/fi.json | 5 +- locale/fr.json | 5 +- locale/gl.json | 5 +- locale/he.json | 5 +- locale/hy.json | 5 +- locale/ia.json | 5 +- locale/io.json | 5 +- locale/it.json | 5 +- locale/ja.json | 5 +- locale/nb.json | 5 +- locale/nl.json | 5 +- locale/pl.json | 7 +- locale/pt.json | 5 +- locale/pt_br.json | 5 +- locale/ru.json | 5 +- locale/sq.json | 5 +- locale/sv.json | 5 +- locale/tr.json | 5 +- locale/uk.json | 5 +- locale/zh_cn.json | 5 +- locale/zh_tw.json | 5 +- 41 files changed, 614 insertions(+), 88 deletions(-) create mode 100644 actions/apiqvittertimelineuser.php diff --git a/QvitterPlugin.php b/QvitterPlugin.php index 7887994..a05e836 100644 --- a/QvitterPlugin.php +++ b/QvitterPlugin.php @@ -160,6 +160,10 @@ class QvitterPlugin extends Plugin { // route/reroute urls public function onRouterInitialized($m) { + + $m->connect('api/qvitter/statuses/user_timeline.:format', + array('action' => 'ApiQvitterTimelineUser', + 'format' => '(xml|json|rss|atom|as)')); $m->connect(':nickname/mutes', array('action' => 'qvitter', 'nickname' => Nickname::INPUT_FMT)); @@ -371,6 +375,10 @@ class QvitterPlugin extends Plugin { array('action' => 'shownotice'), array('notice' => '[0-9]+'), 'qvitter'); + URLMapperOverwrite::overwrite_variable($m, 'user/:id', + array('action' => 'userbyid'), + array('id' => '[0-9]+'), + 'qvitter'); URLMapperOverwrite::overwrite_variable($m, 'conversation/:id', array('action' => 'conversation'), array('id' => '[0-9]+'), @@ -397,6 +405,7 @@ class QvitterPlugin extends Plugin { case 'api/favorites.json': case 'api/statuses/friends_timeline.json': case 'api/statuses/user_timeline.json': + case 'api/qvitter/statuses/user_timeline.json': // add logged in user's user array if (common_logged_in() && !isset($_GET['screen_name']) && !isset($_GET['id'])) { @@ -409,13 +418,17 @@ class QvitterPlugin extends Plugin { if(isset($_GET['screen_name'])) { $user = User::getKV('nickname', $_GET['screen_name']); + if($user instanceof User) { + $profile = $user->getProfile(); + } } elseif(isset($_GET['id'])) { + $profile = Profile::getKV('id', $_GET['id']); $user = User::getKV('id', $_GET['id']); } - if($user instanceof User) { - header('Qvitter-User-Array: '.json_encode($this->qvitterTwitterUserArray($user->getProfile()))); + if(isset($profile) && $profile instanceof Profile) { + header('Qvitter-User-Array: '.json_encode($this->qvitterTwitterUserArray($profile))); } } break; diff --git a/actions/apiexternalusershow.php b/actions/apiexternalusershow.php index dd6d13e..d39a4d3 100644 --- a/actions/apiexternalusershow.php +++ b/actions/apiexternalusershow.php @@ -46,14 +46,23 @@ class ApiExternalUserShowAction extends ApiPrivateAuthAction { parent::prepare($args); - $this->format = 'json'; + $this->format = 'json'; $profileurl = urldecode($this->arg('profileurl')); $nickname = urldecode($this->arg('nickname')); $this->profile = new stdClass(); - $this->profile->external = null; + $this->profile->external = null; $this->profile->local = null; + $this->profile->ostatus = null; + + // the user might not exist in our db yet, try to use the Ostatus plugin + // to get it in there + $validate = new Validate(); + if ($validate->uri($profileurl)) { + $ostatus_profile = Ostatus_profile::ensureProfileURL($profileurl); + $local_profile = Profile::getKV('id',$ostatus_profile->profile_id); + } // we can get urls of two types of urls (1) ://instance/nickname // (2) ://instance/user/1234 @@ -97,7 +106,9 @@ class ApiExternalUserShowAction extends ApiPrivateAuthAction } // case (1) - $local_profile = Profile::getKV('profileurl',$profileurl); + if(!isset($local_profile)) { + $local_profile = Profile::getKV('profileurl',$profileurl); + } if($local_profile instanceof Profile) { @@ -138,13 +149,9 @@ class ApiExternalUserShowAction extends ApiPrivateAuthAction { parent::handle(); - if(is_null($this->profile->local) && is_null($this->profile->external)) { - $this->clientError(_('List not found.'), 404); - } else { - $this->initDocument('json'); - $this->showJsonObjects($this->profile); - $this->endDocument('json'); - } + $this->initDocument('json'); + $this->showJsonObjects($this->profile); + $this->endDocument('json'); } /** diff --git a/actions/apiqvittertimelineuser.php b/actions/apiqvittertimelineuser.php new file mode 100644 index 0000000..19c26df --- /dev/null +++ b/actions/apiqvittertimelineuser.php @@ -0,0 +1,181 @@ + \\\\_\ · + · \\) \____) · + · · + · · + · · + · Qvitter 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 three of the License or (at · + · your option) any later version. · + · · + · Qvitter is distributed in hope that it will be useful but WITHOUT ANY · + · WARRANTY; without even the implied warranty of MERCHANTABILTY 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 Qvitter. If not, see . · + · · + · Contact h@nnesmannerhe.im if you have any questions. · + · · + · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · */ + +if (!defined('GNUSOCIAL')) { exit(1); } + +class ApiQvitterTimelineUserAction extends ApiBareAuthAction +{ + var $notices = null; + + var $next_id = null; + + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + */ + protected function prepare(array $args=array()) + { + parent::prepare($args); + + $this->target = $this->getTargetProfile($this->arg('id')); + + if (!($this->target instanceof Profile)) { + // TRANS: Client error displayed requesting most recent notices for a non-existing user. + $this->clientError(_('No such user.'), 404); + } + + // if (!$this->target->isLocal()) { + // $this->serverError(_('Remote user timelines are not available here yet.'), 501); + // } + + $this->notices = $this->getNotices(); + + return true; + } + + /** + * Handle the request + * + * Just show the notices + * + * @return void + */ + protected function handle() + { + parent::handle(); + + $this->showTimeline(); + } + + /** + * Show the timeline of notices + * + * @return void + */ + function showTimeline() + { + $this->showJsonTimeline($this->notices); + } + + /** + * Get notices + * + * @return array notices + */ + function getNotices() + { + $notices = array(); + + $notice = $this->target->getNotices(($this->page-1) * $this->count, + $this->count + 1, + $this->since_id, + $this->max_id, + $this->scoped); + + while ($notice->fetch()) { + if (count($notices) < $this->count) { + $notices[] = clone($notice); + } else { + $this->next_id = $notice->id; + break; + } + } + + return $notices; + } + + /** + * We expose AtomPub here, so non-GET/HEAD reqs must be read/write. + * + * @param array $args other arguments + * + * @return boolean true + */ + + function isReadOnly($args) + { + return true; + } + + /** + * When was this feed last modified? + * + * @return string datestamp of the latest notice in the stream + */ + function lastModified() + { + if (!empty($this->notices) && (count($this->notices) > 0)) { + return strtotime($this->notices[0]->created); + } + + return null; + } + + /** + * An entity tag for this stream + * + * Returns an Etag based on the action name, language, user ID, and + * timestamps of the first and last notice in the timeline + * + * @return string etag + */ + function etag() + { + if (!empty($this->notices) && (count($this->notices) > 0)) { + $last = count($this->notices) - 1; + + return '"' . implode( + ':', + array($this->arg('action'), + common_user_cache_hash($this->scoped), + common_language(), + $this->target->getID(), + strtotime($this->notices[0]->created), + strtotime($this->notices[$last]->created)) + ) + . '"'; + } + + return null; + } + +} diff --git a/actions/qvitter.php b/actions/qvitter.php index 9d5c288..3ee1577 100644 --- a/actions/qvitter.php +++ b/actions/qvitter.php @@ -694,6 +694,7 @@ class QvitterAction extends ApiAction ?> +
.ostatus-link, +.profile-card.local-user .username .screen-name-with-server, +.profile-card.remote-user .username .screen-name, +.profile-card.remote-user ul.stats { + display:none; + } +.profile-card .remote-user-info { + background-color: pink; + color: #333; + text-shadow: none; + border: 10px solid #fff; + padding: 10px; + } +.profile-card .remote-user-info a { + text-decoration: underline; + color:#333; + } +.hover-card .profile-card .remote-user-info { + display:none; + } + .hover-card .profile-card { width:290px; margin-bottom:0; diff --git a/js/dom-functions.js b/js/dom-functions.js index fbc8988..e2416b8 100644 --- a/js/dom-functions.js +++ b/js/dom-functions.js @@ -394,6 +394,18 @@ function buildProfileCard(data) { if(isUserMuted(data.id)) { is_muted = ' user-muted'; } + // local or remote user? + var local_or_remote = ''; + var remote_user_info = ''; + var serverUrl = guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname(data.statusnet_profile_url, data.screen_name); + data.screenNameWithServer = '@' + data.screen_name + '@' + serverUrl; + if(data.is_local !== true) { + remote_user_info = '' + local_or_remote = ' remote-user'; + } + else { + local_or_remote = ' local-user'; + } var followButton = ''; @@ -420,8 +432,9 @@ function buildProfileCard(data) { // full card html var profileCardHtml = '\ -
\ + \