diff --git a/mod/follow.php b/mod/follow.php deleted file mode 100644 index cd39d4dda7..0000000000 --- a/mod/follow.php +++ /dev/null @@ -1,210 +0,0 @@ -. - * - */ - -use Friendica\App; -use Friendica\Content\Widget; -use Friendica\Core\Protocol; -use Friendica\Core\Renderer; -use Friendica\DI; -use Friendica\Model\Contact; -use Friendica\Model\Profile; -use Friendica\Model\Item; -use Friendica\Network\Probe; -use Friendica\Database\DBA; -use Friendica\Model\Post; -use Friendica\Model\User; -use Friendica\Util\Strings; - -function follow_post(App $a) -{ - if (!DI::userSession()->getLocalUserId()) { - throw new \Friendica\Network\HTTPException\ForbiddenException(DI::l10n()->t('Access denied.')); - } - - if (isset($_REQUEST['cancel'])) { - DI::baseUrl()->redirect('contact'); - } - - $url = Probe::cleanURI($_REQUEST['url']); - - follow_process($a, $url); -} - -function follow_content(App $a) -{ - $return_path = 'contact'; - - if (!DI::userSession()->getLocalUserId()) { - DI::sysmsg()->addNotice(DI::l10n()->t('Permission denied.')); - DI::baseUrl()->redirect($return_path); - // NOTREACHED - } - - $uid = DI::userSession()->getLocalUserId(); - - $url = Probe::cleanURI(trim($_REQUEST['url'] ?? '')); - - // Issue 6874: Allow remote following from Peertube - if (strpos($url, 'acct:') === 0) { - $url = str_replace('acct:', '', $url); - } - - if (!$url) { - DI::baseUrl()->redirect($return_path); - } - - $submit = DI::l10n()->t('Submit Request'); - - // Don't try to add a pending contact - $user_contact = DBA::selectFirst('contact', ['pending'], ["`uid` = ? AND ((`rel` != ?) OR (`network` = ?)) AND - (`nurl` = ? OR `alias` = ? OR `alias` = ?) AND `network` != ?", - $uid, Contact::FOLLOWER, Protocol::DFRN, Strings::normaliseLink($url), - Strings::normaliseLink($url), $url, Protocol::STATUSNET]); - - if (DBA::isResult($user_contact)) { - if ($user_contact['pending']) { - DI::sysmsg()->addNotice(DI::l10n()->t('You already added this contact.')); - $submit = ''; - } - } - - $contact = Contact::getByURL($url, true); - - // Possibly it is a mail contact - if (empty($contact)) { - $contact = Probe::uri($url, Protocol::MAIL, $uid); - } - - if (empty($contact) || ($contact['network'] == Protocol::PHANTOM)) { - // Possibly it is a remote item and not an account - follow_remote_item($url); - - DI::sysmsg()->addNotice(DI::l10n()->t("The network type couldn't be detected. Contact can't be added.")); - $submit = ''; - $contact = ['url' => $url, 'network' => Protocol::PHANTOM, 'name' => $url, 'keywords' => '']; - } - - $protocol = Contact::getProtocol($contact['url'], $contact['network']); - - if (($protocol == Protocol::DIASPORA) && !DI::config()->get('system', 'diaspora_enabled')) { - DI::sysmsg()->addNotice(DI::l10n()->t("Diaspora support isn't enabled. Contact can't be added.")); - $submit = ''; - } - - if (($protocol == Protocol::OSTATUS) && DI::config()->get('system', 'ostatus_disabled')) { - DI::sysmsg()->addNotice(DI::l10n()->t("OStatus support is disabled. Contact can't be added.")); - $submit = ''; - } - - if ($protocol == Protocol::MAIL) { - $contact['url'] = $contact['addr']; - } - - if (!empty($_REQUEST['auto'])) { - follow_process($a, $contact['url']); - } - - $request = DI::baseUrl() . '/follow'; - $tpl = Renderer::getMarkupTemplate('auto_request.tpl'); - - $owner = User::getOwnerDataById($uid); - if (empty($owner)) { - DI::sysmsg()->addNotice(DI::l10n()->t('Permission denied.')); - DI::baseUrl()->redirect($return_path); - // NOTREACHED - } - - $myaddr = $owner['url']; - - $o = Renderer::replaceMacros($tpl, [ - '$header' => DI::l10n()->t('Connect/Follow'), - '$pls_answer' => DI::l10n()->t('Please answer the following:'), - '$your_address' => DI::l10n()->t('Your Identity Address:'), - '$url_label' => DI::l10n()->t('Profile URL'), - '$keywords_label'=> DI::l10n()->t('Tags:'), - '$submit' => $submit, - '$cancel' => DI::l10n()->t('Cancel'), - - '$action' => $request, - '$name' => $contact['name'], - '$url' => $contact['url'], - '$zrl' => Profile::zrl($contact['url']), - '$myaddr' => $myaddr, - '$keywords' => $contact['keywords'], - - '$does_know_you' => ['knowyou', DI::l10n()->t('%s knows you', $contact['name'])], - '$addnote_field' => ['dfrn-request-message', DI::l10n()->t('Add a personal note:')], - ]); - - DI::page()['aside'] = ''; - - if (!in_array($protocol, [Protocol::PHANTOM, Protocol::MAIL])) { - DI::page()['aside'] = Widget\VCard::getHTML($contact); - - $o .= Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), - ['$title' => DI::l10n()->t('Status Messages and Posts')] - ); - - // Show last public posts - $o .= Contact::getPostsFromUrl($contact['url']); - } - - return $o; -} - -function follow_process(App $a, string $url) -{ - $return_path = 'follow?url=' . urlencode($url); - - $result = Contact::createFromProbeForUser($a->getLoggedInUserId(), $url); - - if ($result['success'] == false) { - // Possibly it is a remote item and not an account - follow_remote_item($url); - - if ($result['message']) { - DI::sysmsg()->addNotice($result['message']); - } - DI::baseUrl()->redirect($return_path); - } elseif ($result['cid']) { - DI::baseUrl()->redirect('contact/' . $result['cid']); - } - - DI::sysmsg()->addNotice(DI::l10n()->t('The contact could not be added.')); - - DI::baseUrl()->redirect($return_path); -} - -function follow_remote_item($url) -{ - $item_id = Item::fetchByLink($url, DI::userSession()->getLocalUserId()); - if (!$item_id) { - // If the user-specific search failed, we search and probe a public post - $item_id = Item::fetchByLink($url); - } - - if (!empty($item_id)) { - $item = Post::selectFirst(['guid'], ['id' => $item_id]); - if (DBA::isResult($item)) { - DI::baseUrl()->redirect('display/' . $item['guid']); - } - } -} diff --git a/src/Module/Follow.php b/src/Module/Follow.php new file mode 100644 index 0000000000..2844e40bf2 --- /dev/null +++ b/src/Module/Follow.php @@ -0,0 +1,240 @@ +. + * + */ + +namespace Friendica\Module; + +use Friendica\App; +use Friendica\BaseModule; +use Friendica\Content\Widget\VCard; +use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\L10n; +use Friendica\Core\Protocol; +use Friendica\Core\Renderer; +use Friendica\Core\Session\Capability\IHandleUserSessions; +use Friendica\Model\Contact; +use Friendica\Model\Item; +use Friendica\Model\Post; +use Friendica\Model\Profile; +use Friendica\Model\User; +use Friendica\Navigation\SystemMessages; +use Friendica\Network\HTTPException\ForbiddenException; +use Friendica\Network\Probe; +use Friendica\Util\Profiler; +use Friendica\Util\Strings; +use Psr\Log\LoggerInterface; + +class Follow extends BaseModule +{ + /** @var IHandleUserSessions */ + protected $session; + /** @var SystemMessages */ + protected $sysMessages; + /** @var App */ + protected $app; + /** @var IManageConfigValues */ + protected $config; + /** @var App\Page */ + protected $page; + + public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $session, SystemMessages $sysMessages, App $app, IManageConfigValues $config, App\Page $page, array $server, array $parameters = []) + { + parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters); + + $this->session = $session; + $this->sysMessages = $sysMessages; + $this->app = $app; + $this->config = $config; + $this->page = $page; + } + + protected function post(array $request = []) + { + parent::post($request); + + if (!$this->session->getLocalUserId()) { + throw new ForbiddenException($this->t('Access denied.')); + } + + if (!empty($request['url'])) { + $this->baseUrl->redirect($request['url']); + } + + $url = Probe::cleanURI($this->session->get('url')); + } + + protected function content(array $request = []): string + { + $returnPath = 'contact'; + + if (!$this->session->getLocalUserId()) { + $this->sysMessages->addNotice($this->t('Permission denied.')); + $this->baseUrl->redirect($returnPath); + } + + $uid = $this->session->getLocalUserId(); + $url = Probe::cleanURI(trim($request['url'] ?? '')); + + // Issue 6874: Allow remote following from Peertube + if (strpos($url, 'acct:') === 0) { + $url = str_replace('acct:', '', $url); + } + + if (empty($url)) { + $this->baseUrl->redirect($returnPath); + } + + $submit = $this->t('Submit Request'); + + // Don't try to add a pending contact + $userContact = Contact::selectFirst(['pending'], [ + "`uid` = ? AND ((`rel` != ?) OR (`network` = ?)) AND (`nurl` = ? OR `alias` = ? OR `alias` = ?) AND `network` != ?", + $uid, Contact::FOLLOWER, Protocol::DFRN, + Strings::normaliseLink($url), + Strings::normaliseLink($url), $url, + Protocol::STATUSNET]); + + if (!empty($userContact['pending'])) { + $this->sysMessages->addNotice($this->t('You already added this contact.')); + $submit = ''; + } + + $contact = Contact::getByURL($url, true); + + // Possibly it is a mail contact + if (empty($contact)) { + $contact = Probe::uri($url, Protocol::MAIL, $uid); + } + + if (empty($contact) || ($contact['network'] == Protocol::PHANTOM)) { + // Possibly it is a remote item and not an account + $this->followRemoteItem($url); + + $this->sysMessages->addNotice($this->t('The network type couldn\'t be detected. Contact can\'t be added.')); + $submit = ''; + $contact = ['url' => $url, 'network' => Protocol::PHANTOM, 'name' => $url, 'keywords' => '']; + } + + $protocol = Contact::getProtocol($contact['url'], $contact['network']); + + if (($protocol == Protocol::DIASPORA) && !$this->config->get('system', 'diaspora_enabled')) { + $this->sysMessages->addNotice($this->t('Diaspora support isn\'t enabled. Contact can\'t be added.')); + $submit = ''; + } + + if (($protocol == Protocol::OSTATUS) && $this->config->get('system', 'ostatus_disabled')) { + $this->sysMessages->addNotice($this->t("OStatus support is disabled. Contact can't be added.")); + $submit = ''; + } + + if ($protocol == Protocol::MAIL) { + $contact['url'] = $contact['addr']; + } + + if (!empty($request['auto'])) { + $this->process($contact['url']); + } + + $request = $this->baseUrl . '/follow'; + $tpl = Renderer::getMarkupTemplate('auto_request.tpl'); + + $owner = User::getOwnerDataById($uid); + if (empty($owner)) { + $this->sysMessages->addNotice($this->t('Permission denied.')); + $this->baseUrl->redirect($returnPath); + } + + $myaddr = $owner['url']; + + $output = Renderer::replaceMacros($tpl, [ + '$header' => $this->t('Connect/Follow'), + '$pls_answer' => $this->t('Please answer the following:'), + '$your_address' => $this->t('Your Identity Address:'), + '$url_label' => $this->t('Profile URL'), + '$keywords_label' => $this->t('Tags:'), + '$submit' => $submit, + '$cancel' => $this->t('Cancel'), + + '$request' => $request, + '$name' => $contact['name'], + '$url' => $contact['url'], + '$zrl' => Profile::zrl($contact['url']), + '$myaddr' => $myaddr, + '$keywords' => $contact['keywords'], + + '$does_know_you' => ['knowyou', $this->t('%s knows you', $contact['name'])], + '$addnote_field' => ['dfrn-request-message', $this->t('Add a personal note:')], + ]); + + $this['aside'] = ''; + + if (!in_array($protocol, [Protocol::PHANTOM, Protocol::MAIL])) { + $this['aside'] = VCard::getHTML($contact); + + $output .= Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), + ['$title' => $this->t('Status Messages and Posts')] + ); + + // Show last public posts + $output .= Contact::getPostsFromUrl($contact['url']); + } + + return $output; + } + + protected function process(string $url) + { + $returnPath = 'follow?rul=' . urlencode($url); + + $result = Contact::createFromProbeForUser($this->app->getLoggedInUserId(), $url); + + if (!$result['success']) { + // Possibly it is a remote item and not an account + $this->followRemoteItem($url); + + if (!empty($result['message'])) { + $this->sysMessages->addNotice($result['message']); + } + + $this->baseUrl->redirect($returnPath); + } else if (!empty($result['cid'])) { + $this->baseUrl->redirect('contact/' . $result['cid']); + } + + $this->sysMessages->addNotice($this->t('The contact could not be added.')); + $this->baseUrl->redirect($returnPath); + } + + protected function followRemoteItem(string $url) + { + $itemId = Item::fetchByLink($url, $this->session->getLocalUserId()); + if (!$itemId) { + // If the user-specific search failed, we search and probe a public post + $itemId = Item::fetchByLink($url); + } + + if (!empty($itemId)) { + $item = Post::selectFirst(['guid'], ['id' => $itemId]); + if (!empty($item['guid'])) { + $this->baseUrl->redirect('display/' . $item['guid']); + } + } + } +} diff --git a/static/routes.config.php b/static/routes.config.php index 62742b6593..a478ff05bb 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -418,6 +418,7 @@ return [ '/filed' => [Module\Search\Filed::class, [R::GET]], '/filer[/{id:\d+}]' => [Module\Filer\SaveTag::class, [R::GET]], '/filerm/{id:\d+}' => [Module\Filer\RemoveTag::class, [R::GET, R::POST]], + '/follow[/{url}]' => [Module\Follow::class, [R::GET, R::POST]], '/follow_confirm' => [Module\FollowConfirm::class, [R::GET, R::POST]], '/followers/{nickname}' => [Module\ActivityPub\Followers::class, [R::GET]], '/following/{nickname}' => [Module\ActivityPub\Following::class, [R::GET]],