X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=plugins%2FOStatus%2Factions%2Fostatussub.php;h=28714514f5ec402827bfd9cbe4df06fee5e26cc6;hb=b69936498238b3bb92e7e388aea55188f87c796b;hp=23912250163f0330585ff737bb6bc2c893aa46b5;hpb=f30af7047b0131f4a7e6ed9c77f49fe5c113ea59;p=quix0rs-gnu-social.git diff --git a/plugins/OStatus/actions/ostatussub.php b/plugins/OStatus/actions/ostatussub.php index 2391225016..28714514f5 100644 --- a/plugins/OStatus/actions/ostatussub.php +++ b/plugins/OStatus/actions/ostatussub.php @@ -1,7 +1,7 @@ + * @maintainer Brion Vibber */ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } +/** + * Key UI methods: + * + * showInputForm() - form asking for a remote profile account or URL + * We end up back here on errors + * + * showPreviewForm() - surrounding form for preview-and-confirm + * preview() - display profile for a remote user + * + * success() - redirects to subscriptions page on subscribe + */ class OStatusSubAction extends Action { + protected $profile_uri; // provided acct: or URI of remote entity + protected $oprofile; // Ostatus_profile of remote entity, if valid - protected $feedurl; - - function title() - { - return _m("OStatus Subscribe"); - } - - function handle($args) - { - if ($this->validateFeed()) { - $this->showForm(); - } - - return true; - - } - - function showForm($err = null) - { - $this->err = $err; - $this->showPage(); - } - - - function showContent() + /** + * Show the initial form, when we haven't yet been given a valid + * remote profile. + */ + function showInputForm() { $user = common_current_user(); $profile = $user->getProfile(); - $fuser = null; - - $flink = Foreign_link::getByUserID($user->id, FEEDSUB_SERVICE); - - if (!empty($flink)) { - $fuser = $flink->getForeignUser(); - } - $this->elementStart('form', array('method' => 'post', - 'id' => 'form_settings_feedsub', + 'id' => 'form_ostatus_sub', 'class' => 'form_settings', - 'action' => - common_local_url('feedsubsettings'))); + 'action' => $this->selfLink())); $this->hidden('token', common_session_token()); @@ -77,150 +61,403 @@ class OStatusSubAction extends Action $this->elementStart('ul', 'form_data'); $this->elementStart('li'); - $this->input('feedurl', _('Feed URL'), $this->feedurl, _('Enter the URL of a PubSubHubbub-enabled feed')); + $this->input('profile', + _m('Subscribe to'), + $this->profile_uri, + _m("OStatus user's address, like nickname@example.com or http://example.net/nickname")); $this->elementEnd('li'); $this->elementEnd('ul'); - $this->submit('subscribe', _m('Subscribe')); + $this->submit('validate', _m('Continue')); $this->elementEnd('fieldset'); $this->elementEnd('form'); - - $this->previewFeed(); } /** - * Handle posts to this form - * - * Based on the button that was pressed, muxes out to other functions - * to do the actual task requested. + * Show the preview-and-confirm form. We've got a valid remote + * profile and are ready to poke it! * - * All sub-functions reload the form with a message -- success or failure. - * - * @return void + * This controls the wrapper form; actual profile display will + * be in previewUser() or previewGroup() depending on the type. */ - - function handlePost() + function showPreviewForm() { - // CSRF protection - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->showForm(_('There was a problem with your session token. '. - 'Try again, please.')); + $ok = $this->preview(); + if (!$ok) { + // @fixme maybe provide a cancel button or link back? return; } - if ($this->arg('subscribe')) { - $this->saveFeed(); + $this->elementStart('div', 'entity_actions'); + $this->elementStart('ul'); + $this->elementStart('li', 'entity_subscribe'); + $this->elementStart('form', array('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->profile_uri); + if ($this->oprofile->isGroup()) { + $this->submit('submit', _m('Join'), 'submit', null, + _m('Join this group')); + } else { + $this->submit('submit', _m('Confirm'), 'submit', null, + _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 boolean true if we're ok to try subscribing + */ + function preview() + { + $oprofile = $this->oprofile; + $profile = $oprofile->localProfile(); + + $cur = common_current_user(); + if ($cur->isSubscribed($profile)) { + $this->element('div', array('class' => 'error'), + _m("You are already subscribed to this user.")); + $ok = false; } else { - $this->showForm(_('Unexpected form submission.')); + $ok = true; + } + + $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + $avatarUrl = $avatar ? $avatar->displayUrl() : false; + + $this->showEntity($profile, + $profile->profileurl, + $avatarUrl, + $profile->bio); + return $ok; + } + + function showEntity($entity, $profile, $avatar, $note) + { + $nickname = $entity->nickname; + $fullname = $entity->fullname; + $homepage = $entity->homepage; + $location = $entity->location; + + if (!$avatar) { + $avatar = Avatar::defaultImage(AVATAR_PROFILE_SIZE); + } + + $this->elementStart('div', 'entity_profile vcard'); + $this->elementStart('dl', 'entity_depiction'); + $this->element('dt', null, _('Photo')); + $this->elementStart('dd'); + $this->element('img', array('src' => $avatar, + 'class' => 'photo avatar', + 'width' => AVATAR_PROFILE_SIZE, + 'height' => AVATAR_PROFILE_SIZE, + 'alt' => $nickname)); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_nickname'); + $this->element('dt', null, _('Nickname')); + $this->elementStart('dd'); + $hasFN = ($fullname !== '') ? 'nickname' : 'fn nickname'; + $this->elementStart('a', array('href' => $profile, + 'class' => 'url '.$hasFN)); + $this->raw($nickname); + $this->elementEnd('a'); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + + if (!is_null($fullname)) { + $this->elementStart('dl', 'entity_fn'); + $this->elementStart('dd'); + $this->elementStart('span', 'fn'); + $this->raw($fullname); + $this->elementEnd('span'); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + } + if (!is_null($location)) { + $this->elementStart('dl', 'entity_location'); + $this->element('dt', null, _('Location')); + $this->elementStart('dd', 'label'); + $this->raw($location); + $this->elementEnd('dd'); + $this->elementEnd('dl'); } + + if (!is_null($homepage)) { + $this->elementStart('dl', 'entity_url'); + $this->element('dt', null, _('URL')); + $this->elementStart('dd'); + $this->elementStart('a', array('href' => $homepage, + 'class' => 'url')); + $this->raw($homepage); + $this->elementEnd('a'); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + } + + if (!is_null($note)) { + $this->elementStart('dl', 'entity_note'); + $this->element('dt', null, _('Note')); + $this->elementStart('dd', 'note'); + $this->raw($note); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + } + $this->elementEnd('div'); } - /** - * Set up and add a feed + * Redirect on successful remote user subscription + */ + function success() + { + $cur = common_current_user(); + $url = common_local_url('subscriptions', array('nickname' => $cur->nickname)); + common_redirect($url, 303); + } + + /** + * Pull data for a remote profile and check if it's valid. + * Fills out error UI string in $this->error + * Fills out $this->oprofile on success. * - * @return boolean true if feed successfully read - * Sends you back to input form if not. + * @return boolean */ - function validateFeed() + function pullRemoteProfile() { - $feedurl = $this->trimmed('feed'); - - if ($feedurl == '') { - $this->showForm(_m('Empty feed URL!')); - return; - } - $this->feedurl = $feedurl; - - // Get the canonical feed URI and check it + $this->profile_uri = $this->trimmed('profile'); try { - $discover = new FeedDiscovery(); - $uri = $discover->discoverFromURL($feedurl); + if (Validate::email($this->profile_uri)) { + $this->oprofile = Ostatus_profile::ensureWebfinger($this->profile_uri); + } else if (Validate::uri($this->profile_uri)) { + $this->oprofile = Ostatus_profile::ensureProfileURL($this->profile_uri); + } else { + $this->error = _m("Sorry, we could not reach that address. Please make sure that the OStatus address is like nickname@example.com or http://example.net/nickname"); + common_debug('Invalid address format.', __FILE__); + return false; + } + return true; } catch (FeedSubBadURLException $e) { - $this->showForm(_m('Invalid URL or could not reach server.')); - return false; + $this->error = _m("Sorry, we could not reach that address. Please make sure that the OStatus address is like nickname@example.com or http://example.net/nickname"); + common_debug('Invalid URL or could not reach server.', __FILE__); } catch (FeedSubBadResponseException $e) { - $this->showForm(_m('Cannot read feed; server returned error.')); - return false; + $this->error = _m("Sorry, we could not reach that feed. Please try that OStatus address again later."); + common_debug('Cannot read feed; server returned error.', __FILE__); } catch (FeedSubEmptyException $e) { - $this->showForm(_m('Cannot read feed; server returned an empty page.')); - return false; + $this->error = _m("Sorry, we could not reach that feed. Please try that OStatus address again later."); + common_debug('Cannot read feed; server returned an empty page.', __FILE__); } catch (FeedSubBadHTMLException $e) { - $this->showForm(_m('Bad HTML, could not find feed link.')); - return false; + $this->error = _m("Sorry, we could not reach that feed. Please try that OStatus address again later."); + common_debug('Bad HTML, could not find feed link.', __FILE__); } catch (FeedSubNoFeedException $e) { - $this->showForm(_m('Could not find a feed linked from this URL.')); - return false; + $this->error = _m("Sorry, we could not reach that feed. Please try that OStatus address again later."); + common_debug('Could not find a feed linked from this URL.', __FILE__); } catch (FeedSubUnrecognizedTypeException $e) { - $this->showForm(_m('Not a recognized feed type.')); - return false; - } catch (FeedSubException $e) { + $this->error = _m("Sorry, we could not reach that feed. Please try that OStatus address again later."); + common_debug('Not a recognized feed type.', __FILE__); + } catch (Exception $e) { // Any new ones we forgot about - $this->showForm(_m('Bad feed URL.')); - return false; + $this->error = _m("Sorry, we could not reach that address. Please make sure that the OStatus address is like nickname@example.com or http://example.net/nickname"); + common_debug(sprintf('Bad feed URL: %s %s', get_class($e), $e->getMessage()), __FILE__); } - - $this->munger = $discover->feedMunger(); - $this->profile = $this->munger->ostatusProfile(); - if ($this->profile->huburi == '') { - $this->showForm(_m('Feed is not PuSH-enabled; cannot subscribe.')); - return false; + return false; + } + + function validateRemoteProfile() + { + if ($this->oprofile->isGroup()) { + // Send us to the group subscription form for conf + $target = common_local_url('ostatusgroup', array(), array('profile' => $this->profile_uri)); + common_redirect($target, 303); } - - return true; } + /** + * Attempt to finalize subscription. + * validateFeed must have been run first. + * + * Calls showForm on failure or success on success. + */ function saveFeed() { - if ($this->validateFeed()) { - $this->preview = true; - $this->profile = Ostatus_profile::ensureProfile($this->munger); + // And subscribe the current user to the local profile + $user = common_current_user(); + $local = $this->oprofile->localProfile(); + if ($user->isSubscribed($local)) { + // TRANS: OStatus remote subscription dialog error. + $this->showForm(_m('Already subscribed!')); + } elseif (Subscription::start($user, $local)) { + $this->success(); + } else { + // TRANS: OStatus remote subscription dialog error. + $this->showForm(_m('Remote subscription failed!')); + } + } - // If not already in use, subscribe to updates via the hub - if ($this->profile->sub_start) { - common_log(LOG_INFO, __METHOD__ . ": double the fun! new sub for {$this->profile->feeduri} last subbed {$this->profile->sub_start}"); - } else { - $ok = $this->profile->subscribe(); - common_log(LOG_INFO, __METHOD__ . ": sub was $ok"); - if (!$ok) { - $this->showForm(_m('Feed subscription failed! Bad response from hub.')); - return; - } + function prepare($args) + { + parent::prepare($args); + + if (!common_logged_in()) { + // XXX: selfURL() didn't work. :< + common_set_returnto($_SERVER['REQUEST_URI']); + if (Event::handle('RedirectToLogin', array($this, null))) { + common_redirect(common_local_url('login'), 303); } - - // And subscribe the current user to the local profile - $user = common_current_user(); - $profile = $this->profile->getProfile(); - - if ($user->isSubscribed($profile)) { - $this->showForm(_m('Already subscribed!')); - } elseif ($user->subscribeTo($profile)) { - $this->showForm(_m('Feed subscribed!')); - } else { - $this->showForm(_m('Feed subscription failed!')); + return false; + } + + if ($this->pullRemoteProfile()) { + $this->validateRemoteProfile(); + } + return true; + } + + /** + * Handle the submission. + */ + function handle($args) + { + parent::handle($args); + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $this->handlePost(); + } else { + $this->showForm(); + } + } + + /** + * Handle posts to this form + * + * @return void + */ + + function handlePost() + { + // CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->showForm(_('There was a problem with your session token. '. + 'Try again, please.')); + return; + } + + if ($this->oprofile) { + if ($this->arg('submit')) { + $this->saveFeed(); + return; } } + $this->showForm(); + } + + /** + * Show the appropriate form based on our input state. + */ + function showForm($err=null) + { + if ($err) { + $this->error = $err; + } + if ($this->boolean('ajax')) { + header('Content-Type: text/xml;charset=utf-8'); + $this->xw->startDocument('1.0', 'UTF-8'); + $this->elementStart('html'); + $this->elementStart('head'); + $this->element('title', null, _m('Subscribe to user')); + $this->elementEnd('head'); + $this->elementStart('body'); + $this->showContent(); + $this->elementEnd('body'); + $this->elementEnd('html'); + } else { + $this->showPage(); + } + } + + /** + * Title of the page + * + * @return string Title of the page + */ + + function title() + { + // TRANS: Page title for OStatus remote subscription form + return _m('Confirm'); + } + + /** + * Instructions for use + * + * @return instructions for use + */ + + function getInstructions() + { + return _m('You can subscribe to users from other supported sites. Paste their address or profile URI below:'); } - - function previewFeed() + function showPageNotice() { - $profile = $this->munger->ostatusProfile(); - $notice = $this->munger->notice(0, true); // preview + if (!empty($this->error)) { + $this->element('p', 'error', $this->error); + } + } - if ($notice) { - $this->element('b', null, 'Preview of latest post from this feed:'); + /** + * Content area of the page + * + * Shows a form for associating a remote OStatus account with this + * StatusNet account. + * + * @return void + */ - $item = new NoticeList($notice, $this); - $item->show(); + function showContent() + { + if ($this->oprofile) { + $this->showPreviewForm(); } else { - $this->element('b', null, 'No posts in this feed yet.'); + $this->showInputForm(); } } + function showScripts() + { + parent::showScripts(); + $this->autofocus('feedurl'); + } + + function selfLink() + { + return common_local_url('ostatussub'); + } + + /** + * 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 + */ + function showNoticeForm() { + // nop + } }