123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- <?php
- // This file is part of GNU social - https://www.gnu.org/software/social
- //
- // GNU social 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.
- //
- // GNU social 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 GNU social. If not, see <http://www.gnu.org/licenses/>.
- /**
- * Remote Follow implementation for GNU social
- *
- * @package GNUsocial
- * @author Bruno Casteleiro <brunoccast@fc.up.pt>
- * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
- defined('GNUSOCIAL') || die();
- /**
- * Remote-follow follow action
- *
- * @category Plugin
- * @package GNUsocial
- * @author Bruno Casteleiro <brunoccast@fc.up.pt>
- * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
- */
- class RemoteFollowSubAction extends Action
- {
- protected $uri; // acct: or uri of remote entity
- protected $profile; // profile of remote entity, if valid
- protected function prepare(array $args = [])
- {
- parent::prepare($args);
- if (!common_logged_in()) {
- common_set_returnto($_SERVER['REQUEST_URI']);
- if (Event::handle('RedirectToLogin', [$this, null])) {
- common_redirect(common_local_url('login'), 303);
- }
- return false;
- }
- if (!$this->profile && $this->arg('profile')) {
- $this->uri = $this->trimmed('profile');
- $profile = null;
- if (!Event::handle('RemoteFollowPullProfile', [$this->uri, &$profile]) && !is_null($profile)) {
- $this->profile = $profile;
- } else {
- // TRANS: Error displayed when there's failure in fetching the remote profile.
- $this->error = _m('Sorry, we could not reach that address. ' .
- 'Please make sure it is a valid address and try again later.');
- }
- }
- return true;
- }
- /**
- * Handles the submission.
- *
- * @return void
- */
- protected function handle(): void
- {
- parent::handle();
- if ($_SERVER['REQUEST_METHOD'] == 'POST') {
- $this->handlePost();
- } else {
- $this->showForm();
- }
- }
- /**
- * Show the initial form, when we haven't yet been given a valid
- * remote profile.
- *
- * @return void
- */
- public function showInputForm(): void
- {
- $this->elementStart('form', ['method' => 'post',
- 'id' => 'form_ostatus_sub',
- 'class' => 'form_settings',
- 'action' => $this->selfLink()]);
- $this->hidden('token', common_session_token());
- $this->elementStart('fieldset', ['id' => 'settings_feeds']);
- $this->elementStart('ul', 'form_data');
- $this->elementStart('li');
- $this->input(
- 'profile',
- // TRANS: Field label for a field that takes an user address.
- _m('Subscribe to'),
- $this->uri,
- // TRANS: Tooltip for field label "Subscribe to".
- _m('User\'s address, like nickname@example.com or http://example.net/nickname.')
- );
- $this->elementEnd('li');
- $this->elementEnd('ul');
- // TRANS: Button text.
- $this->submit('validate', _m('BUTTON', 'Continue'));
- $this->elementEnd('fieldset');
- $this->elementEnd('form');
- }
- /**
- * Show the preview-and-confirm form. We've got a valid remote
- * profile and are ready to poke it!
- *
- * @return void
- */
- public function showPreviewForm(): void
- {
- if (!$this->preview()) {
- return;
- }
- $this->elementStart('div', 'entity_actions');
- $this->elementStart('ul');
- $this->elementStart('li', 'entity_subscribe');
- $this->elementStart('form', ['method' => 'post',
- 'id' => 'form_ostatus_sub',
- 'class' => 'form_remote_authorize',
- 'action' => $this->selfLink()]);
- $this->elementStart('fieldset');
- $this->hidden('token', common_session_token());
- $this->hidden('profile', $this->uri);
- $this->submit(
- 'submit',
- // TRANS: Button text.
- _m('BUTTON', 'Confirm'),
- 'submit',
- null,
- // TRANS: Tooltip for button "Confirm".
- _m('Subscribe to this user')
- );
- $this->elementEnd('fieldset');
- $this->elementEnd('form');
- $this->elementEnd('li');
- $this->elementEnd('ul');
- $this->elementEnd('div');
- }
- /**
- * Show a preview for a remote user's profile.
- *
- * @return bool true if we're ok to try subscribing, false otherwise
- */
- public function preview(): bool
- {
- if ($this->scoped->isSubscribed($this->profile)) {
- $this->element(
- 'div',
- ['class' => 'error'],
- // TRANS: Extra paragraph in remote profile view when already subscribed.
- _m('You are already subscribed to this user.')
- );
- $ok = false;
- } else {
- $ok = true;
- }
- $avatarUrl = $this->profile->avatarUrl(AVATAR_PROFILE_SIZE);
- $this->showEntity(
- $this->profile,
- $this->profile->getUrl(),
- $avatarUrl,
- $this->profile->getDescription()
- );
- return $ok;
- }
- /**
- * Show someone's profile.
- *
- * @return void
- */
- public function showEntity(Profile $entity, string $profile_url, string $avatar, ?string $note): void
- {
- $nickname = $entity->getNickname();
- $fullname = $entity->getFullname();
- $homepage = $entity->getHomepage();
- $location = $entity->getLocation();
- $this->elementStart('div', 'entity_profile vcard');
- $this->element('img', ['src' => $avatar,
- 'class' => 'photo avatar entity_depiction',
- 'width' => AVATAR_PROFILE_SIZE,
- 'height' => AVATAR_PROFILE_SIZE,
- 'alt' => $nickname]);
- $hasFN = ($fullname !== '') ? 'nickname' : 'fn nickname entity_nickname';
- $this->elementStart('a', ['href' => $profile_url,
- 'class' => 'url '.$hasFN]);
- $this->text($nickname);
- $this->elementEnd('a');
- if (!is_null($fullname)) {
- $this->elementStart('div', 'fn entity_fn');
- $this->text($fullname);
- $this->elementEnd('div');
- }
- $location_name = (is_null($location) ? null : $location->getName());
- if (!is_null($location_name)) {
- $this->elementStart('div', 'label entity_location');
- $this->text($location_name);
- $this->elementEnd('div');
- }
- if (!is_null($homepage)) {
- $this->elementStart('a', ['href' => $homepage,
- 'class' => 'url entity_url']);
- $this->text($homepage);
- $this->elementEnd('a');
- }
- if (!is_null($note)) {
- $this->elementStart('div', 'note entity_note');
- $this->text($note);
- $this->elementEnd('div');
- }
- $this->elementEnd('div');
- }
- /**
- * Redirect on successful remote follow
- *
- * @return void
- */
- public function success(): void
- {
- $url = common_local_url('subscriptions', ['nickname' => $this->scoped->getNickname()]);
- common_redirect($url, 303);
- }
- /**
- * Attempt to finalize subscription.
- *
- * @return void
- */
- public function follow(): void
- {
- if ($this->scoped->isSubscribed($this->profile)) {
- // TRANS: Remote subscription dialog error.
- $this->showForm(_m('Already subscribed!'));
- } elseif (Subscription::start($this->scoped, $this->profile)) {
- $this->success();
- } else {
- // TRANS: Remote subscription dialog error.
- $this->showForm(_m('Remote subscription failed!'));
- }
- }
- /**
- * Handle posts to this form
- *
- * @return void
- */
- public function handlePost(): void
- {
- // CSRF protection
- $token = $this->trimmed('token');
- if (!$token || $token != common_session_token()) {
- // TRANS: Client error displayed when the session token does not match or is not given.
- $this->showForm(_m('There was a problem with your session token. '.
- 'Try again, please.'));
- return;
- }
- if ($this->profile && $this->arg('submit')) {
- $this->follow();
- return;
- }
- $this->showForm();
- }
- /**
- * Show the appropriate form based on our input state.
- *
- * @return void
- */
- public function showForm(?string $err = null): void
- {
- if ($err) {
- $this->error = $err;
- }
- if ($this->boolean('ajax')) {
- $this->startHTML('text/xml;charset=utf-8');
- $this->elementStart('head');
- // TRANS: Form title.
- $this->element('title', null, _m('Subscribe to user'));
- $this->elementEnd('head');
- $this->elementStart('body');
- $this->showContent();
- $this->elementEnd('body');
- $this->endHTML();
- } else {
- $this->showPage();
- }
- }
- /**
- * Title of the page
- *
- * @return string title of the page
- */
- public function title(): string
- {
- // TRANS: Page title for remote subscription form.
- return !empty($this->uri) ? _m('Confirm') : _m('Remote subscription');
- }
- /**
- * Instructions for use
- *
- * @return string instructions for use
- */
- public function getInstructions(): string
- {
- // TRANS: Instructions.
- return _m('You can subscribe to users from other supported sites. Paste their address or profile URI below:');
- }
- /**
- * Show page notice.
- *
- * @return void
- */
- public function showPageNotice(): void
- {
- if (!empty($this->error)) {
- $this->element('p', 'error', $this->error);
- }
- }
- /**
- * Content area of the page
- *
- * @return void
- */
- public function showContent(): void
- {
- if ($this->profile) {
- $this->showPreviewForm();
- } else {
- $this->showInputForm();
- }
- }
- /**
- * Show javascript headers
- *
- * @return void
- */
- public function showScripts(): void
- {
- parent::showScripts();
- $this->autofocus('profile');
- }
- /**
- * Return url for this action
- *
- * @return string
- */
- public function selfLink(): string
- {
- return common_local_url('RemoteFollowSub');
- }
- /**
- * Disable the send-notice form at the top of the page.
- * This is really just a hack for the broken CSS in the Cloudy theme,
- * I think; copying from other non-notice-navigation pages that do this
- * as well. There will be plenty of others also broken.
- *
- * @fixme fix the cloudy theme
- * @fixme do this in a more general way
- */
- public function showNoticeForm(): void
- {
- // nop
- }
- }
|