From 5344efef71f3a2978d50f1cf24771df28afc5aaa Mon Sep 17 00:00:00 2001 From: nupplaPhil Date: Wed, 4 Mar 2020 22:07:05 +0100 Subject: [PATCH 01/44] Move post/curl/fetchUrl/fetchUrlFull to own class "Network\HTTPRequest" --- mod/dfrn_confirm.php | 4 +- mod/dfrn_poll.php | 28 +-- mod/dfrn_request.php | 5 +- mod/match.php | 4 +- mod/oexchange.php | 4 +- mod/ostatus_subscribe.php | 4 +- mod/parse_url.php | 4 +- mod/pubsubhubbub.php | 4 +- mod/redir.php | 4 +- src/Content/OEmbed.php | 7 +- src/Content/Text/BBCode.php | 9 +- src/Core/Installer.php | 6 +- src/Core/Protocol.php | 4 +- src/Core/Search.php | 5 +- src/Core/Worker.php | 4 +- src/Model/APContact.php | 6 +- src/Model/GContact.php | 11 +- src/Model/GServer.php | 53 +++-- src/Model/Photo.php | 4 +- src/Model/Profile.php | 3 +- src/Model/User.php | 3 +- src/Module/Admin/Summary.php | 6 +- src/Module/Debug/Feed.php | 4 +- src/Module/Magic.php | 4 +- src/Network/HTTPRequest.php | 364 +++++++++++++++++++++++++++++++ src/Network/Probe.php | 22 +- src/Protocol/ActivityPub.php | 6 +- src/Protocol/DFRN.php | 7 +- src/Protocol/Diaspora.php | 5 +- src/Protocol/OStatus.php | 16 +- src/Protocol/PortableContact.php | 12 +- src/Protocol/Salmon.php | 10 +- src/Util/ExAuth.php | 3 +- src/Util/HTTPSignature.php | 9 +- src/Util/Images.php | 3 +- src/Util/Network.php | 331 ---------------------------- src/Util/ParseUrl.php | 3 +- src/Worker/CheckVersion.php | 4 +- src/Worker/CronJobs.php | 6 +- src/Worker/Directory.php | 4 +- src/Worker/OnePoll.php | 10 +- src/Worker/PubSubPublish.php | 4 +- src/Worker/SearchDirectory.php | 4 +- 43 files changed, 528 insertions(+), 485 deletions(-) create mode 100644 src/Network/HTTPRequest.php diff --git a/mod/dfrn_confirm.php b/mod/dfrn_confirm.php index 8b87bae5d..f78e8d55a 100644 --- a/mod/dfrn_confirm.php +++ b/mod/dfrn_confirm.php @@ -42,10 +42,10 @@ use Friendica\Model\Contact; use Friendica\Model\Group; use Friendica\Model\Notify\Type; use Friendica\Model\User; +use Friendica\Network\HTTPRequest; use Friendica\Protocol\Activity; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Network; use Friendica\Util\Strings; use Friendica\Util\XML; @@ -224,7 +224,7 @@ function dfrn_confirm_post(App $a, $handsfree = null) * */ - $res = Network::post($dfrn_confirm, $params, [], 120)->getBody(); + $res = HTTPRequest::post($dfrn_confirm, $params, [], 120)->getBody(); Logger::log(' Confirm: received data: ' . $res, Logger::DATA); diff --git a/mod/dfrn_poll.php b/mod/dfrn_poll.php index 8d50761db..faa55a108 100644 --- a/mod/dfrn_poll.php +++ b/mod/dfrn_poll.php @@ -21,13 +21,13 @@ use Friendica\App; use Friendica\Core\Logger; -use Friendica\Core\System; use Friendica\Core\Session; +use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; +use Friendica\Network\HTTPRequest; use Friendica\Protocol\DFRN; use Friendica\Protocol\OStatus; -use Friendica\Util\Network; use Friendica\Util\Strings; use Friendica\Util\XML; @@ -115,7 +115,7 @@ function dfrn_poll_init(App $a) ); if (DBA::isResult($r)) { - $s = Network::fetchUrl($r[0]['poll'] . '?dfrn_id=' . $my_id . '&type=profile-check'); + $s = HTTPRequest::fetchUrl($r[0]['poll'] . '?dfrn_id=' . $my_id . '&type=profile-check'); Logger::log("dfrn_poll: old profile returns " . $s, Logger::DATA); @@ -499,20 +499,20 @@ function dfrn_poll_content(App $a) // URL reply if ($dfrn_version < 2.2) { - $s = Network::fetchUrl($r[0]['poll'] - . '?dfrn_id=' . $encrypted_id - . '&type=profile-check' - . '&dfrn_version=' . DFRN_PROTOCOL_VERSION - . '&challenge=' . $challenge - . '&sec=' . $sec + $s = HTTPRequest::fetchUrl($r[0]['poll'] + . '?dfrn_id=' . $encrypted_id + . '&type=profile-check' + . '&dfrn_version=' . DFRN_PROTOCOL_VERSION + . '&challenge=' . $challenge + . '&sec=' . $sec ); } else { - $s = Network::post($r[0]['poll'], [ - 'dfrn_id' => $encrypted_id, - 'type' => 'profile-check', + $s = HTTPRequest::post($r[0]['poll'], [ + 'dfrn_id' => $encrypted_id, + 'type' => 'profile-check', 'dfrn_version' => DFRN_PROTOCOL_VERSION, - 'challenge' => $challenge, - 'sec' => $sec + 'challenge' => $challenge, + 'sec' => $sec ])->getBody(); } diff --git a/mod/dfrn_request.php b/mod/dfrn_request.php index f8e4c9023..bdc407b0b 100644 --- a/mod/dfrn_request.php +++ b/mod/dfrn_request.php @@ -29,8 +29,8 @@ use Friendica\Core\Logger; use Friendica\Core\Protocol; use Friendica\Core\Renderer; use Friendica\Core\Search; -use Friendica\Core\System; use Friendica\Core\Session; +use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; @@ -39,6 +39,7 @@ use Friendica\Model\Notify\Type; use Friendica\Model\Profile; use Friendica\Model\User; use Friendica\Module\Security\Login; +use Friendica\Network\HTTPRequest; use Friendica\Network\Probe; use Friendica\Protocol\Activity; use Friendica\Util\DateTimeFormat; @@ -203,7 +204,7 @@ function dfrn_request_post(App $a) } if (!empty($dfrn_request) && strlen($confirm_key)) { - Network::fetchUrl($dfrn_request . '?confirm_key=' . $confirm_key); + HTTPRequest::fetchUrl($dfrn_request . '?confirm_key=' . $confirm_key); } // (ignore reply, nothing we can do it failed) diff --git a/mod/match.php b/mod/match.php index 747e0b2f0..4ec47c4cc 100644 --- a/mod/match.php +++ b/mod/match.php @@ -27,7 +27,7 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Profile; -use Friendica\Util\Network; +use Friendica\Network\HTTPRequest; use Friendica\Util\Proxy as ProxyUtils; /** @@ -76,7 +76,7 @@ function match_content(App $a) $host = DI::baseUrl(); } - $msearch_json = Network::post($host . '/msearch', $params)->getBody(); + $msearch_json = HTTPRequest::post($host . '/msearch', $params)->getBody(); $msearch = json_decode($msearch_json); diff --git a/mod/oexchange.php b/mod/oexchange.php index 97367c3ea..523889332 100644 --- a/mod/oexchange.php +++ b/mod/oexchange.php @@ -23,7 +23,7 @@ use Friendica\App; use Friendica\Core\Renderer; use Friendica\DI; use Friendica\Module\Security\Login; -use Friendica\Util\Network; +use Friendica\Network\HTTPRequest; use Friendica\Util\Strings; function oexchange_init(App $a) { @@ -58,7 +58,7 @@ function oexchange_content(App $a) { $tags = ((!empty($_REQUEST['tags'])) ? '&tags=' . urlencode(Strings::escapeTags(trim($_REQUEST['tags']))) : ''); - $s = Network::fetchUrl(DI::baseUrl() . '/parse_url?url=' . $url . $title . $description . $tags); + $s = HTTPRequest::fetchUrl(DI::baseUrl() . '/parse_url?url=' . $url . $title . $description . $tags); if (!strlen($s)) { return; diff --git a/mod/ostatus_subscribe.php b/mod/ostatus_subscribe.php index 751afcc73..6b6c94987 100644 --- a/mod/ostatus_subscribe.php +++ b/mod/ostatus_subscribe.php @@ -23,7 +23,7 @@ use Friendica\App; use Friendica\Core\Protocol; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Util\Network; +use Friendica\Network\HTTPRequest; function ostatus_subscribe_content(App $a) { @@ -55,7 +55,7 @@ function ostatus_subscribe_content(App $a) $api = $contact['baseurl'] . '/api/'; // Fetching friends - $curlResult = Network::curl($api . 'statuses/friends.json?screen_name=' . $contact['nick']); + $curlResult = HTTPRequest::curl($api . 'statuses/friends.json?screen_name=' . $contact['nick']); if (!$curlResult->isSuccess()) { DI::pConfig()->delete($uid, 'ostatus', 'legacy_contact'); diff --git a/mod/parse_url.php b/mod/parse_url.php index 67610140b..49e41246c 100644 --- a/mod/parse_url.php +++ b/mod/parse_url.php @@ -28,7 +28,7 @@ use Friendica\Content\PageInfo; use Friendica\Core\Hook; use Friendica\Core\Logger; use Friendica\Core\System; -use Friendica\Util\Network; +use Friendica\Network\HTTPRequest; use Friendica\Util\ParseUrl; use Friendica\Util\Strings; @@ -85,7 +85,7 @@ function parse_url_content(App $a) // Check if the URL is an image, video or audio file. If so format // the URL with the corresponding BBCode media tag // Fetch the header of the URL - $curlResponse = Network::curl($url, false, ['novalidate' => true, 'nobody' => true]); + $curlResponse = HTTPRequest::curl($url, false, ['novalidate' => true, 'nobody' => true]); if ($curlResponse->isSuccess()) { // Convert the header fields into an array diff --git a/mod/pubsubhubbub.php b/mod/pubsubhubbub.php index 4d3350379..9403d3eb7 100644 --- a/mod/pubsubhubbub.php +++ b/mod/pubsubhubbub.php @@ -24,7 +24,7 @@ use Friendica\Core\Logger; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\PushSubscriber; -use Friendica\Util\Network; +use Friendica\Network\HTTPRequest; use Friendica\Util\Strings; function post_var($name) { @@ -126,7 +126,7 @@ function pubsubhubbub_init(App $a) { $hub_callback = rtrim($hub_callback, ' ?&#'); $separator = parse_url($hub_callback, PHP_URL_QUERY) === null ? '?' : '&'; - $fetchResult = Network::fetchUrlFull($hub_callback . $separator . $params); + $fetchResult = HTTPRequest::fetchUrlFull($hub_callback . $separator . $params); $body = $fetchResult->getBody(); $ret = $fetchResult->getReturnCode(); diff --git a/mod/redir.php b/mod/redir.php index d928e66df..deb97ca1c 100644 --- a/mod/redir.php +++ b/mod/redir.php @@ -27,7 +27,7 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Profile; -use Friendica\Util\Network; +use Friendica\Network\HTTPRequest; use Friendica\Util\Strings; function redir_init(App $a) { @@ -171,7 +171,7 @@ function redir_magic($a, $cid, $url) } // Test for magic auth on the target system - $serverret = Network::curl($basepath . '/magic'); + $serverret = HTTPRequest::curl($basepath . '/magic'); if ($serverret->isSuccess()) { $separator = strpos($target_url, '?') ? '&' : '?'; $target_url .= $separator . 'zrl=' . urlencode($visitor) . '&addr=' . urlencode($contact_url); diff --git a/src/Content/OEmbed.php b/src/Content/OEmbed.php index db467a263..8cfb8ce0a 100644 --- a/src/Content/OEmbed.php +++ b/src/Content/OEmbed.php @@ -31,6 +31,7 @@ use Friendica\Core\Hook; use Friendica\Core\Renderer; use Friendica\Database\DBA; use Friendica\DI; +use Friendica\Network\HTTPRequest; use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; use Friendica\Util\ParseUrl; @@ -95,7 +96,7 @@ class OEmbed if (!in_array($ext, $noexts)) { // try oembed autodiscovery - $html_text = Network::fetchUrl($embedurl, false, 15, 'text/*'); + $html_text = HTTPRequest::fetchUrl($embedurl, false, 15, 'text/*'); if ($html_text) { $dom = @DOMDocument::loadHTML($html_text); if ($dom) { @@ -103,14 +104,14 @@ class OEmbed $entries = $xpath->query("//link[@type='application/json+oembed']"); foreach ($entries as $e) { $href = $e->getAttributeNode('href')->nodeValue; - $json_string = Network::fetchUrl($href . '&maxwidth=' . $a->videowidth); + $json_string = HTTPRequest::fetchUrl($href . '&maxwidth=' . $a->videowidth); break; } $entries = $xpath->query("//link[@type='text/json+oembed']"); foreach ($entries as $e) { $href = $e->getAttributeNode('href')->nodeValue; - $json_string = Network::fetchUrl($href . '&maxwidth=' . $a->videowidth); + $json_string = HTTPRequest::fetchUrl($href . '&maxwidth=' . $a->videowidth); break; } } diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index ce34d58ac..f03ea6104 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -38,12 +38,11 @@ use Friendica\Model\Contact; use Friendica\Model\Event; use Friendica\Model\Photo; use Friendica\Model\Tag; -use Friendica\Network\Probe; +use Friendica\Network\HTTPRequest; use Friendica\Object\Image; use Friendica\Protocol\Activity; use Friendica\Util\Images; use Friendica\Util\Map; -use Friendica\Util\Network; use Friendica\Util\ParseUrl; use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Strings; @@ -487,7 +486,7 @@ class BBCode continue; } - $curlResult = Network::curl($mtch[1], true); + $curlResult = HTTPRequest::curl($mtch[1], true); if (!$curlResult->isSuccess()) { continue; } @@ -1108,7 +1107,7 @@ class BBCode $text = "[url=" . $match[2] . ']' . $match[2] . "[/url]"; // if its not a picture then look if its a page that contains a picture link - $body = Network::fetchUrl($match[1]); + $body = HTTPRequest::fetchUrl($match[1]); $doc = new DOMDocument(); @$doc->loadHTML($body); @@ -1187,7 +1186,7 @@ class BBCode } // if its not a picture then look if its a page that contains a picture link - $body = Network::fetchUrl($match[1]); + $body = HTTPRequest::fetchUrl($match[1]); $doc = new DOMDocument(); @$doc->loadHTML($body); diff --git a/src/Core/Installer.php b/src/Core/Installer.php index 37b51d2ed..7b6291ff3 100644 --- a/src/Core/Installer.php +++ b/src/Core/Installer.php @@ -27,8 +27,8 @@ use Friendica\Core\Config\Cache; use Friendica\Database\Database; use Friendica\Database\DBStructure; use Friendica\DI; +use Friendica\Network\HTTPRequest; use Friendica\Util\Images; -use Friendica\Util\Network; use Friendica\Util\Strings; /** @@ -548,11 +548,11 @@ class Installer $help = ""; $error_msg = ""; if (function_exists('curl_init')) { - $fetchResult = Network::fetchUrlFull($baseurl . "/install/testrewrite"); + $fetchResult = HTTPRequest::fetchUrlFull($baseurl . "/install/testrewrite"); $url = Strings::normaliseLink($baseurl . "/install/testrewrite"); if ($fetchResult->getReturnCode() != 204) { - $fetchResult = Network::fetchUrlFull($url); + $fetchResult = HTTPRequest::fetchUrlFull($url); } if ($fetchResult->getReturnCode() != 204) { diff --git a/src/Core/Protocol.php b/src/Core/Protocol.php index e510f1868..84b589bf2 100644 --- a/src/Core/Protocol.php +++ b/src/Core/Protocol.php @@ -21,7 +21,7 @@ namespace Friendica\Core; -use Friendica\Util\Network; +use Friendica\Network\HTTPRequest; /** * Manage compatibility with federated networks @@ -123,7 +123,7 @@ class Protocol if (preg_match('=https?://(.*)/user/(.*)=ism', $profile_url, $matches)) { $statusnet_host = $matches[1]; $statusnet_user = $matches[2]; - $UserData = Network::fetchUrl('http://' . $statusnet_host . '/api/users/show.json?user_id=' . $statusnet_user); + $UserData = HTTPRequest::fetchUrl('http://' . $statusnet_host . '/api/users/show.json?user_id=' . $statusnet_user); $user = json_decode($UserData); if ($user) { $matches[2] = $user->screen_name; diff --git a/src/Core/Search.php b/src/Core/Search.php index edc88ffd7..26af05e74 100644 --- a/src/Core/Search.php +++ b/src/Core/Search.php @@ -26,6 +26,7 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\GContact; use Friendica\Network\HTTPException; +use Friendica\Network\HTTPRequest; use Friendica\Object\Search\ContactResult; use Friendica\Object\Search\ResultList; use Friendica\Util\Network; @@ -123,7 +124,7 @@ class Search $searchUrl .= '&page=' . $page; } - $resultJson = Network::fetchUrl($searchUrl, false, 0, 'application/json'); + $resultJson = HTTPRequest::fetchUrl($searchUrl, false, 0, 'application/json'); $results = json_decode($resultJson, true); @@ -284,7 +285,7 @@ class Search $return = GContact::searchByName($search, $mode); } else { $p = $page > 1 ? 'p=' . $page : ''; - $curlResult = Network::curl(self::getGlobalDirectory() . '/search/people?' . $p . '&q=' . urlencode($search), false, ['accept_content' => 'application/json']); + $curlResult = HTTPRequest::curl(self::getGlobalDirectory() . '/search/people?' . $p . '&q=' . urlencode($search), false, ['accept_content' => 'application/json']); if ($curlResult->isSuccess()) { $searchResult = json_decode($curlResult->getBody(), true); if (!empty($searchResult['profiles'])) { diff --git a/src/Core/Worker.php b/src/Core/Worker.php index fe3d17ad7..a5c4226c4 100644 --- a/src/Core/Worker.php +++ b/src/Core/Worker.php @@ -25,8 +25,8 @@ use Friendica\Core; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Process; +use Friendica\Network\HTTPRequest; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Network; /** * Contains the class for the worker background job processing @@ -997,7 +997,7 @@ class Worker } $url = DI::baseUrl() . '/worker'; - Network::fetchUrl($url, false, 1); + HTTPRequest::fetchUrl($url, false, 1); } /** diff --git a/src/Model/APContact.php b/src/Model/APContact.php index 5966b8c25..9fc72ac5a 100644 --- a/src/Model/APContact.php +++ b/src/Model/APContact.php @@ -24,15 +24,13 @@ namespace Friendica\Model; use Friendica\Content\Text\HTML; use Friendica\Core\Logger; use Friendica\Database\DBA; -use Friendica\DI; use Friendica\Network\Probe; use Friendica\Protocol\ActivityNamespace; use Friendica\Protocol\ActivityPub; use Friendica\Util\Crypto; -use Friendica\Util\Network; -use Friendica\Util\JsonLD; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Strings; +use Friendica\Util\JsonLD; +use Friendica\Util\Network; class APContact { diff --git a/src/Model/GContact.php b/src/Model/GContact.php index 6a3c7da74..a7cf837bd 100644 --- a/src/Model/GContact.php +++ b/src/Model/GContact.php @@ -26,11 +26,12 @@ use DOMXPath; use Exception; use Friendica\Core\Logger; use Friendica\Core\Protocol; -use Friendica\Core\System; use Friendica\Core\Search; +use Friendica\Core\System; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; +use Friendica\Network\HTTPRequest; use Friendica\Network\Probe; use Friendica\Protocol\ActivityPub; use Friendica\Protocol\PortableContact; @@ -537,7 +538,7 @@ class GContact $done[] = DI::baseUrl() . '/poco'; if (strlen(DI::config()->get('system', 'directory'))) { - $x = Network::fetchUrl(Search::getGlobalDirectory() . '/pubsites'); + $x = HTTPRequest::fetchUrl(Search::getGlobalDirectory() . '/pubsites'); if (!empty($x)) { $j = json_decode($x); if (!empty($j->entries)) { @@ -845,7 +846,7 @@ class GContact return false; } - $curlResult = Network::curl($gserver['noscrape'] . '/' . $data['nick']); + $curlResult = HTTPRequest::curl($gserver['noscrape'] . '/' . $data['nick']); if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { $noscrape = json_decode($curlResult->getBody(), true); @@ -927,7 +928,7 @@ class GContact private static function updateFromFeed(array $data) { // Search for the newest entry in the feed - $curlResult = Network::curl($data['poll']); + $curlResult = HTTPRequest::curl($data['poll']); if (!$curlResult->isSuccess()) { $fields = ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()]; DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); @@ -1205,7 +1206,7 @@ class GContact $url = $server . '/main/statistics'; - $curlResult = Network::curl($url); + $curlResult = HTTPRequest::curl($url); if (!$curlResult->isSuccess()) { return false; } diff --git a/src/Model/GServer.php b/src/Model/GServer.php index 8cad1aad0..80ef201a8 100644 --- a/src/Model/GServer.php +++ b/src/Model/GServer.php @@ -23,20 +23,21 @@ namespace Friendica\Model; use DOMDocument; use DOMXPath; +use Friendica\Core\Logger; use Friendica\Core\Protocol; +use Friendica\Core\System; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Module\Register; use Friendica\Network\CurlResult; -use Friendica\Util\Network; +use Friendica\Network\HTTPRequest; +use Friendica\Protocol\Diaspora; +use Friendica\Protocol\PortableContact; use Friendica\Util\DateTimeFormat; +use Friendica\Util\Network; use Friendica\Util\Strings; use Friendica\Util\XML; -use Friendica\Core\Logger; -use Friendica\Core\System; -use Friendica\Protocol\PortableContact; -use Friendica\Protocol\Diaspora; /** * This class handles GServer related functions @@ -309,7 +310,7 @@ class GServer // When a nodeinfo is present, we don't need to dig further $xrd_timeout = DI::config()->get('system', 'xrd_timeout'); - $curlResult = Network::curl($url . '/.well-known/nodeinfo', false, ['timeout' => $xrd_timeout]); + $curlResult = HTTPRequest::curl($url . '/.well-known/nodeinfo', false, ['timeout' => $xrd_timeout]); if ($curlResult->isTimeout()) { self::setFailure($url); return false; @@ -342,7 +343,7 @@ class GServer $basedata = ['detection-method' => self::DETECT_MANUAL]; } - $curlResult = Network::curl($baseurl, false, ['timeout' => $xrd_timeout]); + $curlResult = HTTPRequest::curl($baseurl, false, ['timeout' => $xrd_timeout]); if ($curlResult->isSuccess()) { $basedata = self::analyseRootHeader($curlResult, $basedata); $basedata = self::analyseRootBody($curlResult, $basedata, $baseurl); @@ -498,7 +499,7 @@ class GServer { Logger::info('Discover relay data', ['server' => $server_url]); - $curlResult = Network::curl($server_url . '/.well-known/x-social-relay'); + $curlResult = HTTPRequest::curl($server_url . '/.well-known/x-social-relay'); if (!$curlResult->isSuccess()) { return; } @@ -579,7 +580,7 @@ class GServer */ private static function fetchStatistics(string $url) { - $curlResult = Network::curl($url . '/statistics.json'); + $curlResult = HTTPRequest::curl($url . '/statistics.json'); if (!$curlResult->isSuccess()) { return []; } @@ -689,7 +690,8 @@ class GServer */ private static function parseNodeinfo1(string $nodeinfo_url) { - $curlResult = Network::curl($nodeinfo_url); + $curlResult = HTTPRequest::curl($nodeinfo_url); + if (!$curlResult->isSuccess()) { return []; } @@ -765,7 +767,7 @@ class GServer */ private static function parseNodeinfo2(string $nodeinfo_url) { - $curlResult = Network::curl($nodeinfo_url); + $curlResult = HTTPRequest::curl($nodeinfo_url); if (!$curlResult->isSuccess()) { return []; } @@ -842,7 +844,7 @@ class GServer */ private static function fetchSiteinfo(string $url, array $serverdata) { - $curlResult = Network::curl($url . '/siteinfo.json'); + $curlResult = HTTPRequest::curl($url . '/siteinfo.json'); if (!$curlResult->isSuccess()) { return $serverdata; } @@ -911,7 +913,7 @@ class GServer private static function validHostMeta(string $url) { $xrd_timeout = DI::config()->get('system', 'xrd_timeout'); - $curlResult = Network::curl($url . '/.well-known/host-meta', false, ['timeout' => $xrd_timeout]); + $curlResult = HTTPRequest::curl($url . '/.well-known/host-meta', false, ['timeout' => $xrd_timeout]); if (!$curlResult->isSuccess()) { return false; } @@ -1007,7 +1009,7 @@ class GServer { $serverdata['poco'] = ''; - $curlResult = Network::curl($url. '/poco'); + $curlResult = HTTPRequest::curl($url . '/poco'); if (!$curlResult->isSuccess()) { return $serverdata; } @@ -1037,7 +1039,7 @@ class GServer */ public static function checkMastodonDirectory(string $url, array $serverdata) { - $curlResult = Network::curl($url . '/api/v1/directory?limit=1'); + $curlResult = HTTPRequest::curl($url . '/api/v1/directory?limit=1'); if (!$curlResult->isSuccess()) { return $serverdata; } @@ -1064,7 +1066,8 @@ class GServer */ private static function detectNextcloud(string $url, array $serverdata) { - $curlResult = Network::curl($url . '/status.php'); + $curlResult = HTTPRequest::curl($url . '/status.php'); + if (!$curlResult->isSuccess() || ($curlResult->getBody() == '')) { return $serverdata; } @@ -1097,7 +1100,8 @@ class GServer */ private static function detectMastodonAlikes(string $url, array $serverdata) { - $curlResult = Network::curl($url . '/api/v1/instance'); + $curlResult = HTTPRequest::curl($url . '/api/v1/instance'); + if (!$curlResult->isSuccess() || ($curlResult->getBody() == '')) { return $serverdata; } @@ -1162,7 +1166,7 @@ class GServer */ private static function detectHubzilla(string $url, array $serverdata) { - $curlResult = Network::curl($url . '/api/statusnet/config.json'); + $curlResult = HTTPRequest::curl($url . '/api/statusnet/config.json'); if (!$curlResult->isSuccess() || ($curlResult->getBody() == '')) { return $serverdata; } @@ -1260,7 +1264,7 @@ class GServer private static function detectGNUSocial(string $url, array $serverdata) { // Test for GNU Social - $curlResult = Network::curl($url . '/api/gnusocial/version.json'); + $curlResult = HTTPRequest::curl($url . '/api/gnusocial/version.json'); if ($curlResult->isSuccess() && ($curlResult->getBody() != '{"error":"not implemented"}') && ($curlResult->getBody() != '') && (strlen($curlResult->getBody()) < 30)) { $serverdata['platform'] = 'gnusocial'; @@ -1278,7 +1282,7 @@ class GServer } // Test for Statusnet - $curlResult = Network::curl($url . '/api/statusnet/version.json'); + $curlResult = HTTPRequest::curl($url . '/api/statusnet/version.json'); if ($curlResult->isSuccess() && ($curlResult->getBody() != '{"error":"not implemented"}') && ($curlResult->getBody() != '') && (strlen($curlResult->getBody()) < 30)) { @@ -1314,9 +1318,9 @@ class GServer */ private static function detectFriendica(string $url, array $serverdata) { - $curlResult = Network::curl($url . '/friendica/json'); + $curlResult = HTTPRequest::curl($url . '/friendica/json'); if (!$curlResult->isSuccess()) { - $curlResult = Network::curl($url . '/friendika/json'); + $curlResult = HTTPRequest::curl($url . '/friendika/json'); $friendika = true; $platform = 'Friendika'; } else { @@ -1631,7 +1635,7 @@ class GServer $protocols = ['activitypub', 'diaspora', 'dfrn', 'ostatus']; foreach ($protocols as $protocol) { $query = '{nodes(protocol:"' . $protocol . '"){host}}'; - $curlResult = Network::fetchUrl('https://the-federation.info/graphql?query=' . urlencode($query)); + $curlResult = HTTPRequest::fetchUrl('https://the-federation.info/graphql?query=' . urlencode($query)); if (!empty($curlResult)) { $data = json_decode($curlResult, true); if (!empty($data['data']['nodes'])) { @@ -1649,7 +1653,8 @@ class GServer if (!empty($accesstoken)) { $api = 'https://instances.social/api/1.0/instances/list?count=0'; $header = ['Authorization: Bearer '.$accesstoken]; - $curlResult = Network::curl($api, false, ['headers' => $header]); + $curlResult = HTTPRequest::curl($api, false, ['headers' => $header]); + if ($curlResult->isSuccess()) { $servers = json_decode($curlResult->getBody(), true); diff --git a/src/Model/Photo.php b/src/Model/Photo.php index 9d8b5611f..125718bf5 100644 --- a/src/Model/Photo.php +++ b/src/Model/Photo.php @@ -28,10 +28,10 @@ use Friendica\Database\DBA; use Friendica\Database\DBStructure; use Friendica\DI; use Friendica\Model\Storage\SystemResource; +use Friendica\Network\HTTPRequest; use Friendica\Object\Image; use Friendica\Util\DateTimeFormat; use Friendica\Util\Images; -use Friendica\Util\Network; use Friendica\Util\Security; use Friendica\Util\Strings; @@ -421,7 +421,7 @@ class Photo $filename = basename($image_url); if (!empty($image_url)) { - $ret = Network::curl($image_url, true); + $ret = HTTPRequest::curl($image_url, true); $img_str = $ret->getBody(); $type = $ret->getContentType(); } else { diff --git a/src/Model/Profile.php b/src/Model/Profile.php index 2fcbde077..c8fd9d029 100644 --- a/src/Model/Profile.php +++ b/src/Model/Profile.php @@ -33,6 +33,7 @@ use Friendica\Core\Session; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; +use Friendica\Network\HTTPRequest; use Friendica\Protocol\Activity; use Friendica\Protocol\Diaspora; use Friendica\Util\DateTimeFormat; @@ -737,7 +738,7 @@ class Profile $magic_path = $basepath . '/magic' . '?owa=1&dest=' . $dest . '&' . $addr_request; // We have to check if the remote server does understand /magic without invoking something - $serverret = Network::curl($basepath . '/magic'); + $serverret = HTTPRequest::curl($basepath . '/magic'); if ($serverret->isSuccess()) { Logger::log('Doing magic auth for visitor ' . $my_url . ' to ' . $magic_path, Logger::DEBUG); System::externalRedirect($magic_path); diff --git a/src/Model/User.php b/src/Model/User.php index b4ada344e..fda105687 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -34,6 +34,7 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\TwoFactor\AppSpecificPassword; use Friendica\Network\HTTPException\InternalServerErrorException; +use Friendica\Network\HTTPRequest; use Friendica\Object\Image; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; @@ -823,7 +824,7 @@ class User $photo_failure = false; $filename = basename($photo); - $curlResult = Network::curl($photo, true); + $curlResult = HTTPRequest::curl($photo, true); if ($curlResult->isSuccess()) { $img_str = $curlResult->getBody(); $type = $curlResult->getContentType(); diff --git a/src/Module/Admin/Summary.php b/src/Module/Admin/Summary.php index c19b7f7f8..6dcef2ea4 100644 --- a/src/Module/Admin/Summary.php +++ b/src/Module/Admin/Summary.php @@ -31,12 +31,10 @@ use Friendica\Database\DBStructure; use Friendica\DI; use Friendica\Model\Register; use Friendica\Module\BaseAdmin; -use Friendica\Module\Update\Profile; use Friendica\Network\HTTPException\InternalServerErrorException; -use Friendica\Render\FriendicaSmarty; +use Friendica\Network\HTTPRequest; use Friendica\Util\ConfigFileLoader; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Network; class Summary extends BaseAdmin { @@ -249,7 +247,7 @@ class Summary extends BaseAdmin private static function checkSelfHostMeta() { // Fetch the host-meta to check if this really is a vital server - return Network::curl(DI::baseUrl()->get() . '/.well-known/host-meta')->isSuccess(); + return HTTPRequest::curl(DI::baseUrl()->get() . '/.well-known/host-meta')->isSuccess(); } } diff --git a/src/Module/Debug/Feed.php b/src/Module/Debug/Feed.php index e969de9cc..6214b49dd 100644 --- a/src/Module/Debug/Feed.php +++ b/src/Module/Debug/Feed.php @@ -25,8 +25,8 @@ use Friendica\BaseModule; use Friendica\Core\Renderer; use Friendica\DI; use Friendica\Model; +use Friendica\Network\HTTPRequest; use Friendica\Protocol; -use Friendica\Util\Network; /** * Tests a given feed of a contact @@ -49,7 +49,7 @@ class Feed extends BaseModule $contact = Model\Contact::getByURLForUser($url, local_user(), false); - $xml = Network::fetchUrl($contact['poll']); + $xml = HTTPRequest::fetchUrl($contact['poll']); $import_result = Protocol\Feed::import($xml); diff --git a/src/Module/Magic.php b/src/Module/Magic.php index f27ffeac5..b65159585 100644 --- a/src/Module/Magic.php +++ b/src/Module/Magic.php @@ -27,8 +27,8 @@ use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; +use Friendica\Network\HTTPRequest; use Friendica\Util\HTTPSignature; -use Friendica\Util\Network; use Friendica\Util\Strings; /** @@ -101,7 +101,7 @@ class Magic extends BaseModule ); // Try to get an authentication token from the other instance. - $curlResult = Network::curl($basepath . '/owa', false, ['headers' => $headers]); + $curlResult = HTTPRequest::curl($basepath . '/owa', false, ['headers' => $headers]); if ($curlResult->isSuccess()) { $j = json_decode($curlResult->getBody(), true); diff --git a/src/Network/HTTPRequest.php b/src/Network/HTTPRequest.php new file mode 100644 index 000000000..6986a9359 --- /dev/null +++ b/src/Network/HTTPRequest.php @@ -0,0 +1,364 @@ +. + * + */ + +namespace Friendica\Network; + +use Friendica\Core\Logger; +use Friendica\Core\System; +use Friendica\DI; +use Friendica\Util\Network; + +/** + * Performs HTTP requests to a given URL + */ +class HTTPRequest +{ + /** + * fetches an URL. + * + * @param string $url URL to fetch + * @param bool $binary default false + * TRUE if asked to return binary results (file download) + * @param array $opts (optional parameters) assoziative array with: + * 'accept_content' => supply Accept: header with 'accept_content' as the value + * 'timeout' => int Timeout in seconds, default system config value or 60 seconds + * 'http_auth' => username:password + * 'novalidate' => do not validate SSL certs, default is to validate using our CA list + * 'nobody' => only return the header + * 'cookiejar' => path to cookie jar file + * 'header' => header array + * @param int $redirects The recursion counter for internal use - default 0 + * + * @return CurlResult + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public static function curl(string $url, bool $binary = false, array $opts = [], int &$redirects = 0) + { + $stamp1 = microtime(true); + + $a = DI::app(); + + if (strlen($url) > 1000) { + Logger::log('URL is longer than 1000 characters. Callstack: ' . System::callstack(20), Logger::DEBUG); + return CurlResult::createErrorCurl(substr($url, 0, 200)); + } + + $parts2 = []; + $parts = parse_url($url); + $path_parts = explode('/', $parts['path'] ?? ''); + foreach ($path_parts as $part) { + if (strlen($part) <> mb_strlen($part)) { + $parts2[] = rawurlencode($part); + } else { + $parts2[] = $part; + } + } + $parts['path'] = implode('/', $parts2); + $url = Network::unparseURL($parts); + + if (Network::isUrlBlocked($url)) { + Logger::log('domain of ' . $url . ' is blocked', Logger::DATA); + return CurlResult::createErrorCurl($url); + } + + $ch = @curl_init($url); + + if (($redirects > 8) || (!$ch)) { + return CurlResult::createErrorCurl($url); + } + + @curl_setopt($ch, CURLOPT_HEADER, true); + + if (!empty($opts['cookiejar'])) { + curl_setopt($ch, CURLOPT_COOKIEJAR, $opts["cookiejar"]); + curl_setopt($ch, CURLOPT_COOKIEFILE, $opts["cookiejar"]); + } + + // These settings aren't needed. We're following the location already. + // @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + // @curl_setopt($ch, CURLOPT_MAXREDIRS, 5); + + if (!empty($opts['accept_content'])) { + curl_setopt( + $ch, + CURLOPT_HTTPHEADER, + ['Accept: ' . $opts['accept_content']] + ); + } + + if (!empty($opts['header'])) { + curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['header']); + } + + @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + @curl_setopt($ch, CURLOPT_USERAGENT, $a->getUserAgent()); + + $range = intval(DI::config()->get('system', 'curl_range_bytes', 0)); + + if ($range > 0) { + @curl_setopt($ch, CURLOPT_RANGE, '0-' . $range); + } + + // Without this setting it seems as if some webservers send compressed content + // This seems to confuse curl so that it shows this uncompressed. + /// @todo We could possibly set this value to "gzip" or something similar + curl_setopt($ch, CURLOPT_ENCODING, ''); + + if (!empty($opts['headers'])) { + @curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['headers']); + } + + if (!empty($opts['nobody'])) { + @curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']); + } + + if (!empty($opts['timeout'])) { + @curl_setopt($ch, CURLOPT_TIMEOUT, $opts['timeout']); + } else { + $curl_time = DI::config()->get('system', 'curl_timeout', 60); + @curl_setopt($ch, CURLOPT_TIMEOUT, intval($curl_time)); + } + + // by default we will allow self-signed certs + // but you can override this + + $check_cert = DI::config()->get('system', 'verifyssl'); + @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false)); + + if ($check_cert) { + @curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + } + + $proxy = DI::config()->get('system', 'proxy'); + + if (strlen($proxy)) { + @curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); + @curl_setopt($ch, CURLOPT_PROXY, $proxy); + $proxyuser = @DI::config()->get('system', 'proxyuser'); + + if (strlen($proxyuser)) { + @curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyuser); + } + } + + if (DI::config()->get('system', 'ipv4_resolve', false)) { + curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + } + + if ($binary) { + @curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1); + } + + // don't let curl abort the entire application + // if it throws any errors. + + $s = @curl_exec($ch); + $curl_info = @curl_getinfo($ch); + + // Special treatment for HTTP Code 416 + // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/416 + if (($curl_info['http_code'] == 416) && ($range > 0)) { + @curl_setopt($ch, CURLOPT_RANGE, ''); + $s = @curl_exec($ch); + $curl_info = @curl_getinfo($ch); + } + + $curlResponse = new CurlResult($url, $s, $curl_info, curl_errno($ch), curl_error($ch)); + + if ($curlResponse->isRedirectUrl()) { + $redirects++; + Logger::log('curl: redirect ' . $url . ' to ' . $curlResponse->getRedirectUrl()); + @curl_close($ch); + return self::curl($curlResponse->getRedirectUrl(), $binary, $opts, $redirects); + } + + @curl_close($ch); + + DI::profiler()->saveTimestamp($stamp1, 'network', System::callstack()); + + return $curlResponse; + } + + /** + * Send POST request to $url + * + * @param string $url URL to post + * @param mixed $params array of POST variables + * @param array $headers HTTP headers + * @param int $redirects Recursion counter for internal use - default = 0 + * @param int $timeout The timeout in seconds, default system config value or 60 seconds + * + * @return CurlResult The content + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public static function post(string $url, $params, array $headers = [], int $timeout = 0, int &$redirects = 0) + { + $stamp1 = microtime(true); + + if (Network::isUrlBlocked($url)) { + Logger::log('post_url: domain of ' . $url . ' is blocked', Logger::DATA); + return CurlResult::createErrorCurl($url); + } + + $a = DI::app(); + $ch = curl_init($url); + + if (($redirects > 8) || (!$ch)) { + return CurlResult::createErrorCurl($url); + } + + Logger::log('post_url: start ' . $url, Logger::DATA); + + curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $params); + curl_setopt($ch, CURLOPT_USERAGENT, $a->getUserAgent()); + + if (DI::config()->get('system', 'ipv4_resolve', false)) { + curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + } + + if (intval($timeout)) { + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + } else { + $curl_time = DI::config()->get('system', 'curl_timeout', 60); + curl_setopt($ch, CURLOPT_TIMEOUT, intval($curl_time)); + } + + if (!empty($headers)) { + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + } + + $check_cert = DI::config()->get('system', 'verifyssl'); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false)); + + if ($check_cert) { + @curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + } + + $proxy = DI::config()->get('system', 'proxy'); + + if (strlen($proxy)) { + curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); + curl_setopt($ch, CURLOPT_PROXY, $proxy); + $proxyuser = DI::config()->get('system', 'proxyuser'); + if (strlen($proxyuser)) { + curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyuser); + } + } + + // don't let curl abort the entire application + // if it throws any errors. + + $s = @curl_exec($ch); + + $curl_info = curl_getinfo($ch); + + $curlResponse = new CurlResult($url, $s, $curl_info, curl_errno($ch), curl_error($ch)); + + if ($curlResponse->isRedirectUrl()) { + $redirects++; + Logger::log('post_url: redirect ' . $url . ' to ' . $curlResponse->getRedirectUrl()); + curl_close($ch); + return self::post($curlResponse->getRedirectUrl(), $params, $headers, $redirects, $timeout); + } + + curl_close($ch); + + DI::profiler()->saveTimestamp($stamp1, 'network', System::callstack()); + + // Very old versions of Lighttpd don't like the "Expect" header, so we remove it when needed + if ($curlResponse->getReturnCode() == 417) { + $redirects++; + + if (empty($headers)) { + $headers = ['Expect:']; + } else { + if (!in_array('Expect:', $headers)) { + array_push($headers, 'Expect:'); + } + } + Logger::info('Server responds with 417, applying workaround', ['url' => $url]); + return self::post($url, $params, $headers, $redirects, $timeout); + } + + Logger::log('post_url: end ' . $url, Logger::DATA); + + return $curlResponse; + } + + /** + * Curl wrapper + * + * If binary flag is true, return binary results. + * Set the cookiejar argument to a string (e.g. "/tmp/friendica-cookies.txt") + * to preserve cookies from one request to the next. + * + * @param string $url URL to fetch + * @param bool $binary default false + * TRUE if asked to return binary results (file download) + * @param int $timeout Timeout in seconds, default system config value or 60 seconds + * @param string $accept_content supply Accept: header with 'accept_content' as the value + * @param string $cookiejar Path to cookie jar file + * @param int $redirects The recursion counter for internal use - default 0 + * + * @return string The fetched content + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public static function fetchUrl(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = '', int &$redirects = 0) + { + $ret = self::fetchUrlFull($url, $binary, $timeout, $accept_content, $cookiejar, $redirects); + + return $ret->getBody(); + } + + /** + * Curl wrapper with array of return values. + * + * Inner workings and parameters are the same as @ref fetchUrl but returns an array with + * all the information collected during the fetch. + * + * @param string $url URL to fetch + * @param bool $binary default false + * TRUE if asked to return binary results (file download) + * @param int $timeout Timeout in seconds, default system config value or 60 seconds + * @param string $accept_content supply Accept: header with 'accept_content' as the value + * @param string $cookiejar Path to cookie jar file + * @param int $redirects The recursion counter for internal use - default 0 + * + * @return CurlResult With all relevant information, 'body' contains the actual fetched content. + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public static function fetchUrlFull(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = '', int &$redirects = 0) + { + return self::curl( + $url, + $binary, + [ + 'timeout' => $timeout, + 'accept_content' => $accept_content, + 'cookiejar' => $cookiejar + ], + $redirects + ); + } +} diff --git a/src/Network/Probe.php b/src/Network/Probe.php index 920dac47e..dadd794fe 100644 --- a/src/Network/Probe.php +++ b/src/Network/Probe.php @@ -166,7 +166,7 @@ class Probe Logger::info('Probing', ['host' => $host, 'ssl_url' => $ssl_url, 'url' => $url, 'callstack' => System::callstack(20)]); $xrd = null; - $curlResult = Network::curl($ssl_url, false, ['timeout' => $xrd_timeout, 'accept_content' => 'application/xrd+xml']); + $curlResult = HTTPRequest::curl($ssl_url, false, ['timeout' => $xrd_timeout, 'accept_content' => 'application/xrd+xml']); $ssl_connection_error = ($curlResult->getErrorNumber() == CURLE_COULDNT_CONNECT) || ($curlResult->getReturnCode() == 0); if ($curlResult->isSuccess()) { $xml = $curlResult->getBody(); @@ -183,7 +183,7 @@ class Probe } if (!is_object($xrd) && !empty($url)) { - $curlResult = Network::curl($url, false, ['timeout' => $xrd_timeout, 'accept_content' => 'application/xrd+xml']); + $curlResult = HTTPRequest::curl($url, false, ['timeout' => $xrd_timeout, 'accept_content' => 'application/xrd+xml']); $connection_error = ($curlResult->getErrorNumber() == CURLE_COULDNT_CONNECT) || ($curlResult->getReturnCode() == 0); if ($curlResult->isTimeout()) { Logger::info('Probing timeout', ['url' => $url]); @@ -427,7 +427,7 @@ class Probe */ private static function getHideStatus($url) { - $curlResult = Network::curl($url); + $curlResult = HTTPRequest::curl($url); if (!$curlResult->isSuccess()) { return false; } @@ -841,7 +841,7 @@ class Probe public static function pollZot($url, $data) { - $curlResult = Network::curl($url); + $curlResult = HTTPRequest::curl($url); if ($curlResult->isTimeout()) { return $data; } @@ -938,7 +938,7 @@ class Probe { $xrd_timeout = DI::config()->get('system', 'xrd_timeout', 20); - $curlResult = Network::curl($url, false, ['timeout' => $xrd_timeout, 'accept_content' => $type]); + $curlResult = HTTPRequest::curl($url, false, ['timeout' => $xrd_timeout, 'accept_content' => $type]); if ($curlResult->isTimeout()) { self::$istimeout = true; return []; @@ -1007,7 +1007,7 @@ class Probe */ private static function pollNoscrape($noscrape_url, $data) { - $curlResult = Network::curl($noscrape_url); + $curlResult = HTTPRequest::curl($noscrape_url); if ($curlResult->isTimeout()) { self::$istimeout = true; return []; @@ -1265,7 +1265,7 @@ class Probe */ private static function pollHcard($hcard_url, $data, $dfrn = false) { - $curlResult = Network::curl($hcard_url); + $curlResult = HTTPRequest::curl($hcard_url); if ($curlResult->isTimeout()) { self::$istimeout = true; return []; @@ -1519,7 +1519,7 @@ class Probe $pubkey = substr($pubkey, 5); } } elseif (Strings::normaliseLink($pubkey) == 'http://') { - $curlResult = Network::curl($pubkey); + $curlResult = HTTPRequest::curl($pubkey); if ($curlResult->isTimeout()) { self::$istimeout = true; return $short ? false : []; @@ -1552,7 +1552,7 @@ class Probe } // Fetch all additional data from the feed - $curlResult = Network::curl($data["poll"]); + $curlResult = HTTPRequest::curl($data["poll"]); if ($curlResult->isTimeout()) { self::$istimeout = true; return []; @@ -1604,7 +1604,7 @@ class Probe */ private static function pumpioProfileData($profile_link) { - $curlResult = Network::curl($profile_link); + $curlResult = HTTPRequest::curl($profile_link); if (!$curlResult->isSuccess()) { return []; } @@ -1835,7 +1835,7 @@ class Probe */ private static function feed($url, $probe = true) { - $curlResult = Network::curl($url); + $curlResult = HTTPRequest::curl($url); if ($curlResult->isTimeout()) { self::$istimeout = true; return []; diff --git a/src/Protocol/ActivityPub.php b/src/Protocol/ActivityPub.php index 2f8c2f419..24c6250a4 100644 --- a/src/Protocol/ActivityPub.php +++ b/src/Protocol/ActivityPub.php @@ -21,12 +21,12 @@ namespace Friendica\Protocol; -use Friendica\Util\JsonLD; -use Friendica\Util\Network; use Friendica\Core\Protocol; use Friendica\Model\APContact; use Friendica\Model\User; +use Friendica\Network\HTTPRequest; use Friendica\Util\HTTPSignature; +use Friendica\Util\JsonLD; /** * ActivityPub Protocol class @@ -93,7 +93,7 @@ class ActivityPub return HTTPSignature::fetch($url, $uid); } - $curlResult = Network::curl($url, false, ['accept_content' => 'application/activity+json, application/ld+json']); + $curlResult = HTTPRequest::curl($url, false, ['accept_content' => 'application/activity+json, application/ld+json']); if (!$curlResult->isSuccess() || empty($curlResult->getBody())) { return false; } diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index b7c3204b7..21adc58cc 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -43,6 +43,7 @@ use Friendica\Model\Post\Category; use Friendica\Model\Profile; use Friendica\Model\Tag; use Friendica\Model\User; +use Friendica\Network\HTTPRequest; use Friendica\Network\Probe; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; @@ -1194,7 +1195,7 @@ class DFRN Logger::log('dfrn_deliver: ' . $url); - $curlResult = Network::curl($url); + $curlResult = HTTPRequest::curl($url); if ($curlResult->isTimeout()) { return -2; // timed out @@ -1343,7 +1344,7 @@ class DFRN Logger::debug('dfrn_deliver', ['post' => $postvars]); - $postResult = Network::post($contact['notify'], $postvars); + $postResult = HTTPRequest::post($contact['notify'], $postvars); $xml = $postResult->getBody(); @@ -1440,7 +1441,7 @@ class DFRN $content_type = ($public_batch ? "application/magic-envelope+xml" : "application/json"); - $postResult = Network::post($dest_url, $envelope, ["Content-Type: ".$content_type]); + $postResult = HTTPRequest::post($dest_url, $envelope, ["Content-Type: " . $content_type]); $xml = $postResult->getBody(); $curl_stat = $postResult->getReturnCode(); diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index bd99b361e..46d2bc1d4 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -41,6 +41,7 @@ use Friendica\Model\Mail; use Friendica\Model\Post; use Friendica\Model\Tag; use Friendica\Model\User; +use Friendica\Network\HTTPRequest; use Friendica\Network\Probe; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; @@ -1379,7 +1380,7 @@ class Diaspora Logger::log("Fetch post from ".$source_url, Logger::DEBUG); - $envelope = Network::fetchUrl($source_url); + $envelope = HTTPRequest::fetchUrl($source_url); if ($envelope) { Logger::log("Envelope was fetched.", Logger::DEBUG); $x = self::verifyMagicEnvelope($envelope); @@ -3260,7 +3261,7 @@ class Diaspora if (!intval(DI::config()->get("system", "diaspora_test"))) { $content_type = (($public_batch) ? "application/magic-envelope+xml" : "application/json"); - $postResult = Network::post($dest_url."/", $envelope, ["Content-Type: ".$content_type]); + $postResult = HTTPRequest::post($dest_url . "/", $envelope, ["Content-Type: " . $content_type]); $return_code = $postResult->getReturnCode(); } else { Logger::log("test_mode"); diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 9a52476b5..779c99358 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -39,10 +39,10 @@ use Friendica\Model\Item; use Friendica\Model\ItemURI; use Friendica\Model\Tag; use Friendica\Model\User; +use Friendica\Network\HTTPRequest; use Friendica\Network\Probe; use Friendica\Util\DateTimeFormat; use Friendica\Util\Images; -use Friendica\Util\Network; use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Strings; use Friendica\Util\XML; @@ -756,7 +756,7 @@ class OStatus self::$conv_list[$conversation] = true; - $curlResult = Network::curl($conversation, false, ['accept_content' => 'application/atom+xml, text/html']); + $curlResult = HTTPRequest::curl($conversation, false, ['accept_content' => 'application/atom+xml, text/html']); if (!$curlResult->isSuccess()) { return; @@ -785,7 +785,7 @@ class OStatus } } if ($file != '') { - $conversation_atom = Network::curl($attribute['href']); + $conversation_atom = HTTPRequest::curl($attribute['href']); if ($conversation_atom->isSuccess()) { $xml = $conversation_atom->getBody(); @@ -902,7 +902,7 @@ class OStatus return; } - $curlResult = Network::curl($self); + $curlResult = HTTPRequest::curl($self); if (!$curlResult->isSuccess()) { return; @@ -949,7 +949,7 @@ class OStatus } $stored = false; - $curlResult = Network::curl($related, false, ['accept_content' => 'application/atom+xml, text/html']); + $curlResult = HTTPRequest::curl($related, false, ['accept_content' => 'application/atom+xml, text/html']); if (!$curlResult->isSuccess()) { return; @@ -980,7 +980,7 @@ class OStatus } } if ($atom_file != '') { - $curlResult = Network::curl($atom_file); + $curlResult = HTTPRequest::curl($atom_file); if ($curlResult->isSuccess()) { Logger::log('Fetched XML for URI ' . $related_uri, Logger::DEBUG); @@ -992,7 +992,7 @@ class OStatus // Workaround for older GNU Social servers if (($xml == '') && strstr($related, '/notice/')) { - $curlResult = Network::curl(str_replace('/notice/', '/api/statuses/show/', $related).'.atom'); + $curlResult = HTTPRequest::curl(str_replace('/notice/', '/api/statuses/show/', $related) . '.atom'); if ($curlResult->isSuccess()) { Logger::log('GNU Social workaround to fetch XML for URI ' . $related_uri, Logger::DEBUG); @@ -1003,7 +1003,7 @@ class OStatus // Even more worse workaround for GNU Social ;-) if ($xml == '') { $related_guess = self::convertHref($related_uri); - $curlResult = Network::curl(str_replace('/notice/', '/api/statuses/show/', $related_guess).'.atom'); + $curlResult = HTTPRequest::curl(str_replace('/notice/', '/api/statuses/show/', $related_guess) . '.atom'); if ($curlResult->isSuccess()) { Logger::log('GNU Social workaround 2 to fetch XML for URI ' . $related_uri, Logger::DEBUG); diff --git a/src/Protocol/PortableContact.php b/src/Protocol/PortableContact.php index d66676cac..f2ad51070 100644 --- a/src/Protocol/PortableContact.php +++ b/src/Protocol/PortableContact.php @@ -30,8 +30,8 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\GContact; use Friendica\Model\GServer; +use Friendica\Network\HTTPRequest; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Network; use Friendica\Util\Strings; /** @@ -103,7 +103,7 @@ class PortableContact Logger::log('load: ' . $url, Logger::DEBUG); - $fetchresult = Network::fetchUrlFull($url); + $fetchresult = HTTPRequest::fetchUrlFull($url); $s = $fetchresult->getBody(); Logger::log('load: returns ' . $s, Logger::DATA); @@ -251,7 +251,7 @@ class PortableContact */ private static function fetchServerlist($poco) { - $curlResult = Network::curl($poco . "/@server"); + $curlResult = HTTPRequest::curl($poco . "/@server"); if (!$curlResult->isSuccess()) { return; @@ -291,7 +291,7 @@ class PortableContact Logger::info("Fetch all users from the server " . $server["url"]); - $curlResult = Network::curl($url); + $curlResult = HTTPRequest::curl($url); if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { $data = json_decode($curlResult->getBody(), true); @@ -314,7 +314,7 @@ class PortableContact $success = false; - $curlResult = Network::curl($url); + $curlResult = HTTPRequest::curl($url); if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { Logger::info("Fetch all global contacts from the server " . $server["nurl"]); @@ -372,7 +372,7 @@ class PortableContact // Fetch all contacts from a given user from the other server $url = $server['poco'] . '/' . $username . '/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,contactType,generation'; - $curlResult = Network::curl($url); + $curlResult = HTTPRequest::curl($url); if ($curlResult->isSuccess()) { $data = json_decode($curlResult->getBody(), true); diff --git a/src/Protocol/Salmon.php b/src/Protocol/Salmon.php index 770845910..d01ea2ce1 100644 --- a/src/Protocol/Salmon.php +++ b/src/Protocol/Salmon.php @@ -22,9 +22,9 @@ namespace Friendica\Protocol; use Friendica\Core\Logger; +use Friendica\Network\HTTPRequest; use Friendica\Network\Probe; use Friendica\Util\Crypto; -use Friendica\Util\Network; use Friendica\Util\Strings; use Friendica\Util\XML; @@ -72,7 +72,7 @@ class Salmon $ret[$x] = substr($ret[$x], 5); } } elseif (Strings::normaliseLink($ret[$x]) == 'http://') { - $ret[$x] = Network::fetchUrl($ret[$x]); + $ret[$x] = HTTPRequest::fetchUrl($ret[$x]); } } } @@ -155,7 +155,7 @@ class Salmon $salmon = XML::fromArray($xmldata, $xml, false, $namespaces); // slap them - $postResult = Network::post($url, $salmon, [ + $postResult = HTTPRequest::post($url, $salmon, [ 'Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon) ]); @@ -180,7 +180,7 @@ class Salmon $salmon = XML::fromArray($xmldata, $xml, false, $namespaces); // slap them - $postResult = Network::post($url, $salmon, [ + $postResult = HTTPRequest::post($url, $salmon, [ 'Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon) ]); @@ -203,7 +203,7 @@ class Salmon $salmon = XML::fromArray($xmldata, $xml, false, $namespaces); // slap them - $postResult = Network::post($url, $salmon, [ + $postResult = HTTPRequest::post($url, $salmon, [ 'Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon)]); $return_code = $postResult->getReturnCode(); diff --git a/src/Util/ExAuth.php b/src/Util/ExAuth.php index de13ee82f..710c00979 100644 --- a/src/Util/ExAuth.php +++ b/src/Util/ExAuth.php @@ -37,6 +37,7 @@ namespace Friendica\Util; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\User; +use Friendica\Network\HTTPRequest; class ExAuth { @@ -181,7 +182,7 @@ class ExAuth $url = ($ssl ? 'https' : 'http') . '://' . $host . '/noscrape/' . $user; - $curlResult = Network::curl($url); + $curlResult = HTTPRequest::curl($url); if (!$curlResult->isSuccess()) { return false; diff --git a/src/Util/HTTPSignature.php b/src/Util/HTTPSignature.php index d151516be..5d57c2281 100644 --- a/src/Util/HTTPSignature.php +++ b/src/Util/HTTPSignature.php @@ -21,11 +21,12 @@ namespace Friendica\Util; -use Friendica\Database\DBA; use Friendica\Core\Logger; +use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Model\User; use Friendica\Model\APContact; +use Friendica\Model\User; +use Friendica\Network\HTTPRequest; /** * Implements HTTP Signatures per draft-cavage-http-signatures-07. @@ -297,7 +298,7 @@ class HTTPSignature $headers[] = 'Content-Type: application/activity+json'; - $postResult = Network::post($target, $content, $headers); + $postResult = HTTPRequest::post($target, $content, $headers); $return_code = $postResult->getReturnCode(); Logger::log('Transmit to ' . $target . ' returned ' . $return_code, Logger::DEBUG); @@ -442,7 +443,7 @@ class HTTPSignature $curl_opts = $opts; $curl_opts['header'] = $headers; - $curlResult = Network::curl($request, false, $curl_opts); + $curlResult = HTTPRequest::curl($request, false, $curl_opts); $return_code = $curlResult->getReturnCode(); Logger::log('Fetched for user ' . $uid . ' from ' . $request . ' returned ' . $return_code, Logger::DEBUG); diff --git a/src/Util/Images.php b/src/Util/Images.php index 35f0cfc04..9e3be4f4f 100644 --- a/src/Util/Images.php +++ b/src/Util/Images.php @@ -24,6 +24,7 @@ namespace Friendica\Util; use Friendica\Core\Logger; use Friendica\Core\System; use Friendica\DI; +use Friendica\Network\HTTPRequest; /** * Image utilities @@ -184,7 +185,7 @@ class Images return $data; } - $img_str = Network::fetchUrl($url, true, 4); + $img_str = HTTPRequest::fetchUrl($url, true, 4); if (!$img_str) { return []; diff --git a/src/Util/Network.php b/src/Util/Network.php index ddec35990..888dc20a6 100644 --- a/src/Util/Network.php +++ b/src/Util/Network.php @@ -27,340 +27,9 @@ use Friendica\Core\Hook; use Friendica\Core\Logger; use Friendica\Core\System; use Friendica\DI; -use Friendica\Network\CurlResult; class Network { - /** - * Curl wrapper - * - * If binary flag is true, return binary results. - * Set the cookiejar argument to a string (e.g. "/tmp/friendica-cookies.txt") - * to preserve cookies from one request to the next. - * - * @param string $url URL to fetch - * @param bool $binary default false - * TRUE if asked to return binary results (file download) - * @param int $timeout Timeout in seconds, default system config value or 60 seconds - * @param string $accept_content supply Accept: header with 'accept_content' as the value - * @param string $cookiejar Path to cookie jar file - * @param int $redirects The recursion counter for internal use - default 0 - * - * @return string The fetched content - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public static function fetchUrl(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = '', int &$redirects = 0) - { - $ret = self::fetchUrlFull($url, $binary, $timeout, $accept_content, $cookiejar, $redirects); - - return $ret->getBody(); - } - - /** - * Curl wrapper with array of return values. - * - * Inner workings and parameters are the same as @ref fetchUrl but returns an array with - * all the information collected during the fetch. - * - * @param string $url URL to fetch - * @param bool $binary default false - * TRUE if asked to return binary results (file download) - * @param int $timeout Timeout in seconds, default system config value or 60 seconds - * @param string $accept_content supply Accept: header with 'accept_content' as the value - * @param string $cookiejar Path to cookie jar file - * @param int $redirects The recursion counter for internal use - default 0 - * - * @return CurlResult With all relevant information, 'body' contains the actual fetched content. - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public static function fetchUrlFull(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = '', int &$redirects = 0) - { - return self::curl( - $url, - $binary, - [ - 'timeout' => $timeout, - 'accept_content' => $accept_content, - 'cookiejar' => $cookiejar - ], - $redirects - ); - } - - /** - * fetches an URL. - * - * @param string $url URL to fetch - * @param bool $binary default false - * TRUE if asked to return binary results (file download) - * @param array $opts (optional parameters) assoziative array with: - * 'accept_content' => supply Accept: header with 'accept_content' as the value - * 'timeout' => int Timeout in seconds, default system config value or 60 seconds - * 'http_auth' => username:password - * 'novalidate' => do not validate SSL certs, default is to validate using our CA list - * 'nobody' => only return the header - * 'cookiejar' => path to cookie jar file - * 'header' => header array - * @param int $redirects The recursion counter for internal use - default 0 - * - * @return CurlResult - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public static function curl(string $url, bool $binary = false, array $opts = [], int &$redirects = 0) - { - $stamp1 = microtime(true); - - $a = DI::app(); - - if (strlen($url) > 1000) { - Logger::log('URL is longer than 1000 characters. Callstack: ' . System::callstack(20), Logger::DEBUG); - return CurlResult::createErrorCurl(substr($url, 0, 200)); - } - - $parts2 = []; - $parts = parse_url($url); - $path_parts = explode('/', $parts['path'] ?? ''); - foreach ($path_parts as $part) { - if (strlen($part) <> mb_strlen($part)) { - $parts2[] = rawurlencode($part); - } else { - $parts2[] = $part; - } - } - $parts['path'] = implode('/', $parts2); - $url = self::unparseURL($parts); - - if (self::isUrlBlocked($url)) { - Logger::log('domain of ' . $url . ' is blocked', Logger::DATA); - return CurlResult::createErrorCurl($url); - } - - $ch = @curl_init($url); - - if (($redirects > 8) || (!$ch)) { - return CurlResult::createErrorCurl($url); - } - - @curl_setopt($ch, CURLOPT_HEADER, true); - - if (!empty($opts['cookiejar'])) { - curl_setopt($ch, CURLOPT_COOKIEJAR, $opts["cookiejar"]); - curl_setopt($ch, CURLOPT_COOKIEFILE, $opts["cookiejar"]); - } - - // These settings aren't needed. We're following the location already. - // @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - // @curl_setopt($ch, CURLOPT_MAXREDIRS, 5); - - if (!empty($opts['accept_content'])) { - curl_setopt( - $ch, - CURLOPT_HTTPHEADER, - ['Accept: ' . $opts['accept_content']] - ); - } - - if (!empty($opts['header'])) { - curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['header']); - } - - @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - @curl_setopt($ch, CURLOPT_USERAGENT, $a->getUserAgent()); - - $range = intval(DI::config()->get('system', 'curl_range_bytes', 0)); - - if ($range > 0) { - @curl_setopt($ch, CURLOPT_RANGE, '0-' . $range); - } - - // Without this setting it seems as if some webservers send compressed content - // This seems to confuse curl so that it shows this uncompressed. - /// @todo We could possibly set this value to "gzip" or something similar - curl_setopt($ch, CURLOPT_ENCODING, ''); - - if (!empty($opts['headers'])) { - @curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['headers']); - } - - if (!empty($opts['nobody'])) { - @curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']); - } - - if (!empty($opts['timeout'])) { - @curl_setopt($ch, CURLOPT_TIMEOUT, $opts['timeout']); - } else { - $curl_time = DI::config()->get('system', 'curl_timeout', 60); - @curl_setopt($ch, CURLOPT_TIMEOUT, intval($curl_time)); - } - - // by default we will allow self-signed certs - // but you can override this - - $check_cert = DI::config()->get('system', 'verifyssl'); - @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false)); - - if ($check_cert) { - @curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); - } - - $proxy = DI::config()->get('system', 'proxy'); - - if (strlen($proxy)) { - @curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); - @curl_setopt($ch, CURLOPT_PROXY, $proxy); - $proxyuser = @DI::config()->get('system', 'proxyuser'); - - if (strlen($proxyuser)) { - @curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyuser); - } - } - - if (DI::config()->get('system', 'ipv4_resolve', false)) { - curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - } - - if ($binary) { - @curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1); - } - - // don't let curl abort the entire application - // if it throws any errors. - - $s = @curl_exec($ch); - $curl_info = @curl_getinfo($ch); - - // Special treatment for HTTP Code 416 - // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/416 - if (($curl_info['http_code'] == 416) && ($range > 0)) { - @curl_setopt($ch, CURLOPT_RANGE, ''); - $s = @curl_exec($ch); - $curl_info = @curl_getinfo($ch); - } - - $curlResponse = new CurlResult($url, $s, $curl_info, curl_errno($ch), curl_error($ch)); - - if ($curlResponse->isRedirectUrl()) { - $redirects++; - Logger::log('curl: redirect ' . $url . ' to ' . $curlResponse->getRedirectUrl()); - @curl_close($ch); - return self::curl($curlResponse->getRedirectUrl(), $binary, $opts, $redirects); - } - - @curl_close($ch); - - DI::profiler()->saveTimestamp($stamp1, 'network', System::callstack()); - - return $curlResponse; - } - - /** - * Send POST request to $url - * - * @param string $url URL to post - * @param mixed $params array of POST variables - * @param array $headers HTTP headers - * @param int $redirects Recursion counter for internal use - default = 0 - * @param int $timeout The timeout in seconds, default system config value or 60 seconds - * - * @return CurlResult The content - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public static function post(string $url, $params, array $headers = [], int $timeout = 0, int &$redirects = 0) - { - $stamp1 = microtime(true); - - if (self::isUrlBlocked($url)) { - Logger::log('post_url: domain of ' . $url . ' is blocked', Logger::DATA); - return CurlResult::createErrorCurl($url); - } - - $a = DI::app(); - $ch = curl_init($url); - - if (($redirects > 8) || (!$ch)) { - return CurlResult::createErrorCurl($url); - } - - Logger::log('post_url: start ' . $url, Logger::DATA); - - curl_setopt($ch, CURLOPT_HEADER, true); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, $params); - curl_setopt($ch, CURLOPT_USERAGENT, $a->getUserAgent()); - - if (DI::config()->get('system', 'ipv4_resolve', false)) { - curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - } - - if (intval($timeout)) { - curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); - } else { - $curl_time = DI::config()->get('system', 'curl_timeout', 60); - curl_setopt($ch, CURLOPT_TIMEOUT, intval($curl_time)); - } - - if (!empty($headers)) { - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - } - - $check_cert = DI::config()->get('system', 'verifyssl'); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false)); - - if ($check_cert) { - @curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); - } - - $proxy = DI::config()->get('system', 'proxy'); - - if (strlen($proxy)) { - curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); - curl_setopt($ch, CURLOPT_PROXY, $proxy); - $proxyuser = DI::config()->get('system', 'proxyuser'); - if (strlen($proxyuser)) { - curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyuser); - } - } - - // don't let curl abort the entire application - // if it throws any errors. - - $s = @curl_exec($ch); - - $curl_info = curl_getinfo($ch); - - $curlResponse = new CurlResult($url, $s, $curl_info, curl_errno($ch), curl_error($ch)); - - if ($curlResponse->isRedirectUrl()) { - $redirects++; - Logger::log('post_url: redirect ' . $url . ' to ' . $curlResponse->getRedirectUrl()); - curl_close($ch); - return self::post($curlResponse->getRedirectUrl(), $params, $headers, $redirects, $timeout); - } - - curl_close($ch); - - DI::profiler()->saveTimestamp($stamp1, 'network', System::callstack()); - - // Very old versions of Lighttpd don't like the "Expect" header, so we remove it when needed - if ($curlResponse->getReturnCode() == 417) { - $redirects++; - - if (empty($headers)) { - $headers = ['Expect:']; - } else { - if (!in_array('Expect:', $headers)) { - array_push($headers, 'Expect:'); - } - } - Logger::info('Server responds with 417, applying workaround', ['url' => $url]); - return self::post($url, $params, $headers, $redirects, $timeout); - } - - Logger::log('post_url: end ' . $url, Logger::DATA); - - return $curlResponse; - } /** * Return raw post data from a post request diff --git a/src/Util/ParseUrl.php b/src/Util/ParseUrl.php index b6d172a3a..577ffd4c1 100644 --- a/src/Util/ParseUrl.php +++ b/src/Util/ParseUrl.php @@ -27,6 +27,7 @@ use Friendica\Content\OEmbed; use Friendica\Core\Hook; use Friendica\Core\Logger; use Friendica\Database\DBA; +use Friendica\Network\HTTPRequest; /** * Get information about a given URL @@ -159,7 +160,7 @@ class ParseUrl return $siteinfo; } - $curlResult = Network::curl($url); + $curlResult = HTTPRequest::curl($url); if (!$curlResult->isSuccess()) { return $siteinfo; } diff --git a/src/Worker/CheckVersion.php b/src/Worker/CheckVersion.php index 9572342c3..f0369daab 100644 --- a/src/Worker/CheckVersion.php +++ b/src/Worker/CheckVersion.php @@ -24,7 +24,7 @@ namespace Friendica\Worker; use Friendica\Core\Logger; use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Util\Network; +use Friendica\Network\HTTPRequest; /** * Check the git repository VERSION file and save the version to the DB @@ -55,7 +55,7 @@ class CheckVersion Logger::log("Checking VERSION from: ".$checked_url, Logger::DEBUG); // fetch the VERSION file - $gitversion = DBA::escape(trim(Network::fetchUrl($checked_url))); + $gitversion = DBA::escape(trim(HTTPRequest::fetchUrl($checked_url))); Logger::log("Upstream VERSION is: ".$gitversion, Logger::DEBUG); DI::config()->set('system', 'git_friendica_version', $gitversion); diff --git a/src/Worker/CronJobs.php b/src/Worker/CronJobs.php index 319a369d1..23434beb1 100644 --- a/src/Worker/CronJobs.php +++ b/src/Worker/CronJobs.php @@ -30,12 +30,10 @@ use Friendica\Database\PostUpdate; use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\GContact; -use Friendica\Model\GServer; use Friendica\Model\Nodeinfo; use Friendica\Model\Photo; use Friendica\Model\User; -use Friendica\Network\Probe; -use Friendica\Util\Network; +use Friendica\Network\HTTPRequest; use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Strings; @@ -63,7 +61,7 @@ class CronJobs // Now trying to register $url = 'http://the-federation.info/register/' . DI::baseUrl()->getHostname(); Logger::debug('Check registering url', ['url' => $url]); - $ret = Network::fetchUrl($url); + $ret = HTTPRequest::fetchUrl($url); Logger::debug('Check registering answer', ['answer' => $ret]); Logger::info('cron_end'); break; diff --git a/src/Worker/Directory.php b/src/Worker/Directory.php index 6c6d26f26..0dea9841f 100644 --- a/src/Worker/Directory.php +++ b/src/Worker/Directory.php @@ -26,7 +26,7 @@ use Friendica\Core\Logger; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Util\Network; +use Friendica\Network\HTTPRequest; /** * Sends updated profile data to the directory @@ -54,7 +54,7 @@ class Directory Logger::log('Updating directory: ' . $arr['url'], Logger::DEBUG); if (strlen($arr['url'])) { - Network::fetchUrl($dir . '?url=' . bin2hex($arr['url'])); + HTTPRequest::fetchUrl($dir . '?url=' . bin2hex($arr['url'])); } return; diff --git a/src/Worker/OnePoll.php b/src/Worker/OnePoll.php index fa8f74833..13d1b8ee7 100644 --- a/src/Worker/OnePoll.php +++ b/src/Worker/OnePoll.php @@ -28,13 +28,13 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Item; use Friendica\Model\User; +use Friendica\Network\HTTPRequest; use Friendica\Protocol\Activity; use Friendica\Protocol\ActivityPub; use Friendica\Protocol\Email; use Friendica\Protocol\Feed; use Friendica\Protocol\PortableContact; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Network; use Friendica\Util\Strings; use Friendica\Util\XML; @@ -291,7 +291,7 @@ class OnePoll . '&type=data&last_update=' . $last_update . '&perm=' . $perm; - $curlResult = Network::curl($url); + $curlResult = HTTPRequest::curl($url); if (!$curlResult->isSuccess() && ($curlResult->getErrorNumber() == CURLE_OPERATION_TIMEDOUT)) { // set the last-update so we don't keep polling @@ -405,7 +405,7 @@ class OnePoll $postvars['dfrn_version'] = DFRN_PROTOCOL_VERSION; $postvars['perm'] = 'rw'; - return Network::post($contact['poll'], $postvars)->getBody(); + return HTTPRequest::post($contact['poll'], $postvars)->getBody(); } /** @@ -444,7 +444,7 @@ class OnePoll } $cookiejar = tempnam(get_temppath(), 'cookiejar-onepoll-'); - $curlResult = Network::curl($contact['poll'], false, ['cookiejar' => $cookiejar]); + $curlResult = HTTPRequest::curl($contact['poll'], false, ['cookiejar' => $cookiejar]); unlink($cookiejar); if ($curlResult->isTimeout()) { @@ -756,7 +756,7 @@ class OnePoll DBA::update('contact', ['hub-verify' => $verify_token], ['id' => $contact['id']]); } - $postResult = Network::post($url, $params); + $postResult = HTTPRequest::post($url, $params); Logger::log('subscribe_to_hub: returns: ' . $postResult->getReturnCode(), Logger::DEBUG); diff --git a/src/Worker/PubSubPublish.php b/src/Worker/PubSubPublish.php index 2eb94eeb7..4fa3b3e89 100644 --- a/src/Worker/PubSubPublish.php +++ b/src/Worker/PubSubPublish.php @@ -25,8 +25,8 @@ use Friendica\Core\Logger; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\PushSubscriber; +use Friendica\Network\HTTPRequest; use Friendica\Protocol\OStatus; -use Friendica\Util\Network; class PubSubPublish { @@ -68,7 +68,7 @@ class PubSubPublish Logger::log('POST ' . print_r($headers, true) . "\n" . $params, Logger::DATA); - $postResult = Network::post($subscriber['callback_url'], $params, $headers); + $postResult = HTTPRequest::post($subscriber['callback_url'], $params, $headers); $ret = $postResult->getReturnCode(); if ($ret >= 200 && $ret <= 299) { diff --git a/src/Worker/SearchDirectory.php b/src/Worker/SearchDirectory.php index afe54e5fb..1dcf0c8db 100644 --- a/src/Worker/SearchDirectory.php +++ b/src/Worker/SearchDirectory.php @@ -30,7 +30,7 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\GContact; use Friendica\Model\GServer; -use Friendica\Util\Network; +use Friendica\Network\HTTPRequest; use Friendica\Util\Strings; class SearchDirectory @@ -52,7 +52,7 @@ class SearchDirectory } } - $x = Network::fetchUrl(Search::getGlobalDirectory() . '/lsearch?p=1&n=500&search=' . urlencode($search)); + $x = HTTPRequest::fetchUrl(Search::getGlobalDirectory() . '/lsearch?p=1&n=500&search=' . urlencode($search)); $j = json_decode($x); if (!empty($j->results)) { From 9d00e4f1bc8f6ec95bd4ab450676ab039b2ee2f9 Mon Sep 17 00:00:00 2001 From: nupplaPhil Date: Wed, 4 Mar 2020 22:11:01 +0100 Subject: [PATCH 02/44] Introduce HTPPRequest DI call and constructor --- src/DI.php | 12 +++++++ src/Network/HTTPRequest.php | 65 ++++++++++++++++++++++++------------- 2 files changed, 54 insertions(+), 23 deletions(-) diff --git a/src/DI.php b/src/DI.php index 9ed0c5b24..5986ca961 100644 --- a/src/DI.php +++ b/src/DI.php @@ -323,6 +323,18 @@ abstract class DI return self::$dice->create(Model\Storage\IStorage::class); } + // + // "Network" namespace + // + + /** + * @return Network\HTTPRequest + */ + public static function httpRequest() + { + return self::$dice->create(Network\HTTPRequest::class); + } + // // "Repository" namespace // diff --git a/src/Network/HTTPRequest.php b/src/Network/HTTPRequest.php index 6986a9359..7d7d59a6d 100644 --- a/src/Network/HTTPRequest.php +++ b/src/Network/HTTPRequest.php @@ -21,23 +21,44 @@ namespace Friendica\Network; +use Friendica\App; +use Friendica\Core\Config\IConfig; use Friendica\Core\Logger; use Friendica\Core\System; use Friendica\DI; use Friendica\Util\Network; +use Friendica\Util\Profiler; +use Psr\Log\LoggerInterface; /** * Performs HTTP requests to a given URL */ class HTTPRequest { + /** @var LoggerInterface */ + private $logger; + /** @var Profiler */ + private $profiler; + /** @var IConfig */ + private $config; + /** @var string */ + private $userAgent; + + public function __construct(LoggerInterface $logger, Profiler $profiler, IConfig $config, App $a) + { + $this->logger = $logger; + $this->profiler = $profiler; + $this->config = $config; + $this->userAgent = $a->getUserAgent(); + } + /** * fetches an URL. * - * @param string $url URL to fetch - * @param bool $binary default false + * @param string $url URL to fetch + * @param bool $binary default false * TRUE if asked to return binary results (file download) - * @param array $opts (optional parameters) assoziative array with: + * @param array $opts (optional parameters) assoziative array with: * 'accept_content' => supply Accept: header with 'accept_content' as the value * 'timeout' => int Timeout in seconds, default system config value or 60 seconds * 'http_auth' => username:password @@ -45,7 +66,7 @@ class HTTPRequest * 'nobody' => only return the header * 'cookiejar' => path to cookie jar file * 'header' => header array - * @param int $redirects The recursion counter for internal use - default 0 + * @param int $redirects The recursion counter for internal use - default 0 * * @return CurlResult * @throws \Friendica\Network\HTTPException\InternalServerErrorException @@ -54,8 +75,6 @@ class HTTPRequest { $stamp1 = microtime(true); - $a = DI::app(); - if (strlen($url) > 1000) { Logger::log('URL is longer than 1000 characters. Callstack: ' . System::callstack(20), Logger::DEBUG); return CurlResult::createErrorCurl(substr($url, 0, 200)); @@ -200,11 +219,11 @@ class HTTPRequest /** * Send POST request to $url * - * @param string $url URL to post - * @param mixed $params array of POST variables - * @param array $headers HTTP headers - * @param int $redirects Recursion counter for internal use - default = 0 - * @param int $timeout The timeout in seconds, default system config value or 60 seconds + * @param string $url URL to post + * @param mixed $params array of POST variables + * @param array $headers HTTP headers + * @param int $redirects Recursion counter for internal use - default = 0 + * @param int $timeout The timeout in seconds, default system config value or 60 seconds * * @return CurlResult The content * @throws \Friendica\Network\HTTPException\InternalServerErrorException @@ -313,13 +332,13 @@ class HTTPRequest * Set the cookiejar argument to a string (e.g. "/tmp/friendica-cookies.txt") * to preserve cookies from one request to the next. * - * @param string $url URL to fetch - * @param bool $binary default false + * @param string $url URL to fetch + * @param bool $binary default false * TRUE if asked to return binary results (file download) - * @param int $timeout Timeout in seconds, default system config value or 60 seconds - * @param string $accept_content supply Accept: header with 'accept_content' as the value - * @param string $cookiejar Path to cookie jar file - * @param int $redirects The recursion counter for internal use - default 0 + * @param int $timeout Timeout in seconds, default system config value or 60 seconds + * @param string $accept_content supply Accept: header with 'accept_content' as the value + * @param string $cookiejar Path to cookie jar file + * @param int $redirects The recursion counter for internal use - default 0 * * @return string The fetched content * @throws \Friendica\Network\HTTPException\InternalServerErrorException @@ -337,13 +356,13 @@ class HTTPRequest * Inner workings and parameters are the same as @ref fetchUrl but returns an array with * all the information collected during the fetch. * - * @param string $url URL to fetch - * @param bool $binary default false + * @param string $url URL to fetch + * @param bool $binary default false * TRUE if asked to return binary results (file download) - * @param int $timeout Timeout in seconds, default system config value or 60 seconds - * @param string $accept_content supply Accept: header with 'accept_content' as the value - * @param string $cookiejar Path to cookie jar file - * @param int $redirects The recursion counter for internal use - default 0 + * @param int $timeout Timeout in seconds, default system config value or 60 seconds + * @param string $accept_content supply Accept: header with 'accept_content' as the value + * @param string $cookiejar Path to cookie jar file + * @param int $redirects The recursion counter for internal use - default 0 * * @return CurlResult With all relevant information, 'body' contains the actual fetched content. * @throws \Friendica\Network\HTTPException\InternalServerErrorException From 2973ed6448f56dd807df3ec0d20d095226d14b65 Mon Sep 17 00:00:00 2001 From: nupplaPhil Date: Wed, 4 Mar 2020 22:15:46 +0100 Subject: [PATCH 03/44] Make "HTTPRequest::curl" dynamic --- mod/ostatus_subscribe.php | 3 +-- mod/parse_url.php | 3 +-- mod/redir.php | 3 +-- src/Core/Search.php | 2 +- src/Model/GContact.php | 7 +++---- src/Model/GServer.php | 36 ++++++++++++++++---------------- src/Model/Photo.php | 3 +-- src/Model/Profile.php | 3 +-- src/Module/Admin/Summary.php | 3 +-- src/Module/Magic.php | 3 +-- src/Network/HTTPRequest.php | 28 ++++++++++++------------- src/Network/Probe.php | 22 +++++++++---------- src/Protocol/ActivityPub.php | 3 +-- src/Protocol/DFRN.php | 2 +- src/Protocol/OStatus.php | 15 +++++++------ src/Protocol/PortableContact.php | 8 +++---- src/Util/ExAuth.php | 3 +-- src/Util/HTTPSignature.php | 2 +- src/Util/ParseUrl.php | 3 +-- src/Worker/OnePoll.php | 4 ++-- 20 files changed, 72 insertions(+), 84 deletions(-) diff --git a/mod/ostatus_subscribe.php b/mod/ostatus_subscribe.php index 6b6c94987..5a3a625ce 100644 --- a/mod/ostatus_subscribe.php +++ b/mod/ostatus_subscribe.php @@ -23,7 +23,6 @@ use Friendica\App; use Friendica\Core\Protocol; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Network\HTTPRequest; function ostatus_subscribe_content(App $a) { @@ -55,7 +54,7 @@ function ostatus_subscribe_content(App $a) $api = $contact['baseurl'] . '/api/'; // Fetching friends - $curlResult = HTTPRequest::curl($api . 'statuses/friends.json?screen_name=' . $contact['nick']); + $curlResult = DI::httpRequest()->curl($api . 'statuses/friends.json?screen_name=' . $contact['nick']); if (!$curlResult->isSuccess()) { DI::pConfig()->delete($uid, 'ostatus', 'legacy_contact'); diff --git a/mod/parse_url.php b/mod/parse_url.php index 49e41246c..0e80d971a 100644 --- a/mod/parse_url.php +++ b/mod/parse_url.php @@ -28,7 +28,6 @@ use Friendica\Content\PageInfo; use Friendica\Core\Hook; use Friendica\Core\Logger; use Friendica\Core\System; -use Friendica\Network\HTTPRequest; use Friendica\Util\ParseUrl; use Friendica\Util\Strings; @@ -85,7 +84,7 @@ function parse_url_content(App $a) // Check if the URL is an image, video or audio file. If so format // the URL with the corresponding BBCode media tag // Fetch the header of the URL - $curlResponse = HTTPRequest::curl($url, false, ['novalidate' => true, 'nobody' => true]); + $curlResponse = DI::httpRequest()->curl($url, false, ['novalidate' => true, 'nobody' => true]); if ($curlResponse->isSuccess()) { // Convert the header fields into an array diff --git a/mod/redir.php b/mod/redir.php index deb97ca1c..4069518cd 100644 --- a/mod/redir.php +++ b/mod/redir.php @@ -27,7 +27,6 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Profile; -use Friendica\Network\HTTPRequest; use Friendica\Util\Strings; function redir_init(App $a) { @@ -171,7 +170,7 @@ function redir_magic($a, $cid, $url) } // Test for magic auth on the target system - $serverret = HTTPRequest::curl($basepath . '/magic'); + $serverret = DI::httpRequest()->curl($basepath . '/magic'); if ($serverret->isSuccess()) { $separator = strpos($target_url, '?') ? '&' : '?'; $target_url .= $separator . 'zrl=' . urlencode($visitor) . '&addr=' . urlencode($contact_url); diff --git a/src/Core/Search.php b/src/Core/Search.php index 26af05e74..aafa4024a 100644 --- a/src/Core/Search.php +++ b/src/Core/Search.php @@ -285,7 +285,7 @@ class Search $return = GContact::searchByName($search, $mode); } else { $p = $page > 1 ? 'p=' . $page : ''; - $curlResult = HTTPRequest::curl(self::getGlobalDirectory() . '/search/people?' . $p . '&q=' . urlencode($search), false, ['accept_content' => 'application/json']); + $curlResult = DI::httpRequest()->curl(self::getGlobalDirectory() . '/search/people?' . $p . '&q=' . urlencode($search), false, ['accept_content' => 'application/json']); if ($curlResult->isSuccess()) { $searchResult = json_decode($curlResult->getBody(), true); if (!empty($searchResult['profiles'])) { diff --git a/src/Model/GContact.php b/src/Model/GContact.php index a7cf837bd..669bc60c5 100644 --- a/src/Model/GContact.php +++ b/src/Model/GContact.php @@ -36,7 +36,6 @@ use Friendica\Network\Probe; use Friendica\Protocol\ActivityPub; use Friendica\Protocol\PortableContact; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Network; use Friendica\Util\Strings; /** @@ -846,7 +845,7 @@ class GContact return false; } - $curlResult = HTTPRequest::curl($gserver['noscrape'] . '/' . $data['nick']); + $curlResult = DI::httpRequest()->curl($gserver['noscrape'] . '/' . $data['nick']); if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { $noscrape = json_decode($curlResult->getBody(), true); @@ -928,7 +927,7 @@ class GContact private static function updateFromFeed(array $data) { // Search for the newest entry in the feed - $curlResult = HTTPRequest::curl($data['poll']); + $curlResult = DI::httpRequest()->curl($data['poll']); if (!$curlResult->isSuccess()) { $fields = ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()]; DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); @@ -1206,7 +1205,7 @@ class GContact $url = $server . '/main/statistics'; - $curlResult = HTTPRequest::curl($url); + $curlResult = DI::httpRequest()->curl($url); if (!$curlResult->isSuccess()) { return false; } diff --git a/src/Model/GServer.php b/src/Model/GServer.php index 80ef201a8..c868e19d4 100644 --- a/src/Model/GServer.php +++ b/src/Model/GServer.php @@ -310,7 +310,7 @@ class GServer // When a nodeinfo is present, we don't need to dig further $xrd_timeout = DI::config()->get('system', 'xrd_timeout'); - $curlResult = HTTPRequest::curl($url . '/.well-known/nodeinfo', false, ['timeout' => $xrd_timeout]); + $curlResult = DI::httpRequest()->curl($url . '/.well-known/nodeinfo', false, ['timeout' => $xrd_timeout]); if ($curlResult->isTimeout()) { self::setFailure($url); return false; @@ -343,7 +343,7 @@ class GServer $basedata = ['detection-method' => self::DETECT_MANUAL]; } - $curlResult = HTTPRequest::curl($baseurl, false, ['timeout' => $xrd_timeout]); + $curlResult = DI::httpRequest()->curl($baseurl, false, ['timeout' => $xrd_timeout]); if ($curlResult->isSuccess()) { $basedata = self::analyseRootHeader($curlResult, $basedata); $basedata = self::analyseRootBody($curlResult, $basedata, $baseurl); @@ -499,7 +499,7 @@ class GServer { Logger::info('Discover relay data', ['server' => $server_url]); - $curlResult = HTTPRequest::curl($server_url . '/.well-known/x-social-relay'); + $curlResult = DI::httpRequest()->curl($server_url . '/.well-known/x-social-relay'); if (!$curlResult->isSuccess()) { return; } @@ -580,7 +580,7 @@ class GServer */ private static function fetchStatistics(string $url) { - $curlResult = HTTPRequest::curl($url . '/statistics.json'); + $curlResult = DI::httpRequest()->curl($url . '/statistics.json'); if (!$curlResult->isSuccess()) { return []; } @@ -690,7 +690,7 @@ class GServer */ private static function parseNodeinfo1(string $nodeinfo_url) { - $curlResult = HTTPRequest::curl($nodeinfo_url); + $curlResult = DI::httpRequest()->curl($nodeinfo_url); if (!$curlResult->isSuccess()) { return []; @@ -767,7 +767,7 @@ class GServer */ private static function parseNodeinfo2(string $nodeinfo_url) { - $curlResult = HTTPRequest::curl($nodeinfo_url); + $curlResult = DI::httpRequest()->curl($nodeinfo_url); if (!$curlResult->isSuccess()) { return []; } @@ -844,7 +844,7 @@ class GServer */ private static function fetchSiteinfo(string $url, array $serverdata) { - $curlResult = HTTPRequest::curl($url . '/siteinfo.json'); + $curlResult = DI::httpRequest()->curl($url . '/siteinfo.json'); if (!$curlResult->isSuccess()) { return $serverdata; } @@ -913,7 +913,7 @@ class GServer private static function validHostMeta(string $url) { $xrd_timeout = DI::config()->get('system', 'xrd_timeout'); - $curlResult = HTTPRequest::curl($url . '/.well-known/host-meta', false, ['timeout' => $xrd_timeout]); + $curlResult = DI::httpRequest()->curl($url . '/.well-known/host-meta', false, ['timeout' => $xrd_timeout]); if (!$curlResult->isSuccess()) { return false; } @@ -1009,7 +1009,7 @@ class GServer { $serverdata['poco'] = ''; - $curlResult = HTTPRequest::curl($url . '/poco'); + $curlResult = DI::httpRequest()->curl($url . '/poco'); if (!$curlResult->isSuccess()) { return $serverdata; } @@ -1039,7 +1039,7 @@ class GServer */ public static function checkMastodonDirectory(string $url, array $serverdata) { - $curlResult = HTTPRequest::curl($url . '/api/v1/directory?limit=1'); + $curlResult = DI::httpRequest()->curl($url . '/api/v1/directory?limit=1'); if (!$curlResult->isSuccess()) { return $serverdata; } @@ -1066,7 +1066,7 @@ class GServer */ private static function detectNextcloud(string $url, array $serverdata) { - $curlResult = HTTPRequest::curl($url . '/status.php'); + $curlResult = DI::httpRequest()->curl($url . '/status.php'); if (!$curlResult->isSuccess() || ($curlResult->getBody() == '')) { return $serverdata; @@ -1100,7 +1100,7 @@ class GServer */ private static function detectMastodonAlikes(string $url, array $serverdata) { - $curlResult = HTTPRequest::curl($url . '/api/v1/instance'); + $curlResult = DI::httpRequest()->curl($url . '/api/v1/instance'); if (!$curlResult->isSuccess() || ($curlResult->getBody() == '')) { return $serverdata; @@ -1166,7 +1166,7 @@ class GServer */ private static function detectHubzilla(string $url, array $serverdata) { - $curlResult = HTTPRequest::curl($url . '/api/statusnet/config.json'); + $curlResult = DI::httpRequest()->curl($url . '/api/statusnet/config.json'); if (!$curlResult->isSuccess() || ($curlResult->getBody() == '')) { return $serverdata; } @@ -1264,7 +1264,7 @@ class GServer private static function detectGNUSocial(string $url, array $serverdata) { // Test for GNU Social - $curlResult = HTTPRequest::curl($url . '/api/gnusocial/version.json'); + $curlResult = DI::httpRequest()->curl($url . '/api/gnusocial/version.json'); if ($curlResult->isSuccess() && ($curlResult->getBody() != '{"error":"not implemented"}') && ($curlResult->getBody() != '') && (strlen($curlResult->getBody()) < 30)) { $serverdata['platform'] = 'gnusocial'; @@ -1282,7 +1282,7 @@ class GServer } // Test for Statusnet - $curlResult = HTTPRequest::curl($url . '/api/statusnet/version.json'); + $curlResult = DI::httpRequest()->curl($url . '/api/statusnet/version.json'); if ($curlResult->isSuccess() && ($curlResult->getBody() != '{"error":"not implemented"}') && ($curlResult->getBody() != '') && (strlen($curlResult->getBody()) < 30)) { @@ -1318,9 +1318,9 @@ class GServer */ private static function detectFriendica(string $url, array $serverdata) { - $curlResult = HTTPRequest::curl($url . '/friendica/json'); + $curlResult = DI::httpRequest()->curl($url . '/friendica/json'); if (!$curlResult->isSuccess()) { - $curlResult = HTTPRequest::curl($url . '/friendika/json'); + $curlResult = DI::httpRequest()->curl($url . '/friendika/json'); $friendika = true; $platform = 'Friendika'; } else { @@ -1653,7 +1653,7 @@ class GServer if (!empty($accesstoken)) { $api = 'https://instances.social/api/1.0/instances/list?count=0'; $header = ['Authorization: Bearer '.$accesstoken]; - $curlResult = HTTPRequest::curl($api, false, ['headers' => $header]); + $curlResult = DI::httpRequest()->curl($api, false, ['headers' => $header]); if ($curlResult->isSuccess()) { $servers = json_decode($curlResult->getBody(), true); diff --git a/src/Model/Photo.php b/src/Model/Photo.php index 125718bf5..a10711db2 100644 --- a/src/Model/Photo.php +++ b/src/Model/Photo.php @@ -28,7 +28,6 @@ use Friendica\Database\DBA; use Friendica\Database\DBStructure; use Friendica\DI; use Friendica\Model\Storage\SystemResource; -use Friendica\Network\HTTPRequest; use Friendica\Object\Image; use Friendica\Util\DateTimeFormat; use Friendica\Util\Images; @@ -421,7 +420,7 @@ class Photo $filename = basename($image_url); if (!empty($image_url)) { - $ret = HTTPRequest::curl($image_url, true); + $ret = DI::httpRequest()->curl($image_url, true); $img_str = $ret->getBody(); $type = $ret->getContentType(); } else { diff --git a/src/Model/Profile.php b/src/Model/Profile.php index c8fd9d029..d32940ae6 100644 --- a/src/Model/Profile.php +++ b/src/Model/Profile.php @@ -33,7 +33,6 @@ use Friendica\Core\Session; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Network\HTTPRequest; use Friendica\Protocol\Activity; use Friendica\Protocol\Diaspora; use Friendica\Util\DateTimeFormat; @@ -738,7 +737,7 @@ class Profile $magic_path = $basepath . '/magic' . '?owa=1&dest=' . $dest . '&' . $addr_request; // We have to check if the remote server does understand /magic without invoking something - $serverret = HTTPRequest::curl($basepath . '/magic'); + $serverret = DI::httpRequest()->curl($basepath . '/magic'); if ($serverret->isSuccess()) { Logger::log('Doing magic auth for visitor ' . $my_url . ' to ' . $magic_path, Logger::DEBUG); System::externalRedirect($magic_path); diff --git a/src/Module/Admin/Summary.php b/src/Module/Admin/Summary.php index 6dcef2ea4..ad84bb6ab 100644 --- a/src/Module/Admin/Summary.php +++ b/src/Module/Admin/Summary.php @@ -32,7 +32,6 @@ use Friendica\DI; use Friendica\Model\Register; use Friendica\Module\BaseAdmin; use Friendica\Network\HTTPException\InternalServerErrorException; -use Friendica\Network\HTTPRequest; use Friendica\Util\ConfigFileLoader; use Friendica\Util\DateTimeFormat; @@ -247,7 +246,7 @@ class Summary extends BaseAdmin private static function checkSelfHostMeta() { // Fetch the host-meta to check if this really is a vital server - return HTTPRequest::curl(DI::baseUrl()->get() . '/.well-known/host-meta')->isSuccess(); + return DI::httpRequest()->curl(DI::baseUrl()->get() . '/.well-known/host-meta')->isSuccess(); } } diff --git a/src/Module/Magic.php b/src/Module/Magic.php index b65159585..a025617d8 100644 --- a/src/Module/Magic.php +++ b/src/Module/Magic.php @@ -27,7 +27,6 @@ use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Network\HTTPRequest; use Friendica\Util\HTTPSignature; use Friendica\Util\Strings; @@ -101,7 +100,7 @@ class Magic extends BaseModule ); // Try to get an authentication token from the other instance. - $curlResult = HTTPRequest::curl($basepath . '/owa', false, ['headers' => $headers]); + $curlResult = DI::httpRequest()->curl($basepath . '/owa', false, ['headers' => $headers]); if ($curlResult->isSuccess()) { $j = json_decode($curlResult->getBody(), true); diff --git a/src/Network/HTTPRequest.php b/src/Network/HTTPRequest.php index 7d7d59a6d..73e5cd8e0 100644 --- a/src/Network/HTTPRequest.php +++ b/src/Network/HTTPRequest.php @@ -71,12 +71,12 @@ class HTTPRequest * @return CurlResult * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function curl(string $url, bool $binary = false, array $opts = [], int &$redirects = 0) + public function curl(string $url, bool $binary = false, array $opts = [], int &$redirects = 0) { $stamp1 = microtime(true); if (strlen($url) > 1000) { - Logger::log('URL is longer than 1000 characters. Callstack: ' . System::callstack(20), Logger::DEBUG); + $this->logger->debug('URL is longer than 1000 characters.', ['url' => $url, 'callstack' => System::callstack(20)]); return CurlResult::createErrorCurl(substr($url, 0, 200)); } @@ -94,7 +94,7 @@ class HTTPRequest $url = Network::unparseURL($parts); if (Network::isUrlBlocked($url)) { - Logger::log('domain of ' . $url . ' is blocked', Logger::DATA); + $this->logger->info('Domain is blocked.', ['url' => $url]); return CurlResult::createErrorCurl($url); } @@ -128,9 +128,9 @@ class HTTPRequest } @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - @curl_setopt($ch, CURLOPT_USERAGENT, $a->getUserAgent()); + @curl_setopt($ch, CURLOPT_USERAGENT, $this->userAgent); - $range = intval(DI::config()->get('system', 'curl_range_bytes', 0)); + $range = intval($this->config->get('system', 'curl_range_bytes', 0)); if ($range > 0) { @curl_setopt($ch, CURLOPT_RANGE, '0-' . $range); @@ -152,33 +152,33 @@ class HTTPRequest if (!empty($opts['timeout'])) { @curl_setopt($ch, CURLOPT_TIMEOUT, $opts['timeout']); } else { - $curl_time = DI::config()->get('system', 'curl_timeout', 60); + $curl_time = $this->config->get('system', 'curl_timeout', 60); @curl_setopt($ch, CURLOPT_TIMEOUT, intval($curl_time)); } // by default we will allow self-signed certs // but you can override this - $check_cert = DI::config()->get('system', 'verifyssl'); + $check_cert = $this->config->get('system', 'verifyssl'); @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false)); if ($check_cert) { @curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); } - $proxy = DI::config()->get('system', 'proxy'); + $proxy = $this->config->get('system', 'proxy'); - if (strlen($proxy)) { + if (!empty($proxy)) { @curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); @curl_setopt($ch, CURLOPT_PROXY, $proxy); - $proxyuser = @DI::config()->get('system', 'proxyuser'); + $proxyuser = $this->config->get('system', 'proxyuser'); - if (strlen($proxyuser)) { + if (!empty($proxyuser)) { @curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyuser); } } - if (DI::config()->get('system', 'ipv4_resolve', false)) { + if ($this->config->get('system', 'ipv4_resolve', false)) { curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); } @@ -204,14 +204,14 @@ class HTTPRequest if ($curlResponse->isRedirectUrl()) { $redirects++; - Logger::log('curl: redirect ' . $url . ' to ' . $curlResponse->getRedirectUrl()); + $this->logger->notice('Curl redirect.', ['url' => $url, 'to' => $curlResponse->getRedirectUrl()]); @curl_close($ch); return self::curl($curlResponse->getRedirectUrl(), $binary, $opts, $redirects); } @curl_close($ch); - DI::profiler()->saveTimestamp($stamp1, 'network', System::callstack()); + $this->profiler->saveTimestamp($stamp1, 'network', System::callstack()); return $curlResponse; } diff --git a/src/Network/Probe.php b/src/Network/Probe.php index dadd794fe..01dc28408 100644 --- a/src/Network/Probe.php +++ b/src/Network/Probe.php @@ -166,7 +166,7 @@ class Probe Logger::info('Probing', ['host' => $host, 'ssl_url' => $ssl_url, 'url' => $url, 'callstack' => System::callstack(20)]); $xrd = null; - $curlResult = HTTPRequest::curl($ssl_url, false, ['timeout' => $xrd_timeout, 'accept_content' => 'application/xrd+xml']); + $curlResult = DI::httpRequest()->curl($ssl_url, false, ['timeout' => $xrd_timeout, 'accept_content' => 'application/xrd+xml']); $ssl_connection_error = ($curlResult->getErrorNumber() == CURLE_COULDNT_CONNECT) || ($curlResult->getReturnCode() == 0); if ($curlResult->isSuccess()) { $xml = $curlResult->getBody(); @@ -183,7 +183,7 @@ class Probe } if (!is_object($xrd) && !empty($url)) { - $curlResult = HTTPRequest::curl($url, false, ['timeout' => $xrd_timeout, 'accept_content' => 'application/xrd+xml']); + $curlResult = DI::httpRequest()->curl($url, false, ['timeout' => $xrd_timeout, 'accept_content' => 'application/xrd+xml']); $connection_error = ($curlResult->getErrorNumber() == CURLE_COULDNT_CONNECT) || ($curlResult->getReturnCode() == 0); if ($curlResult->isTimeout()) { Logger::info('Probing timeout', ['url' => $url]); @@ -427,7 +427,7 @@ class Probe */ private static function getHideStatus($url) { - $curlResult = HTTPRequest::curl($url); + $curlResult = DI::httpRequest()->curl($url); if (!$curlResult->isSuccess()) { return false; } @@ -841,7 +841,7 @@ class Probe public static function pollZot($url, $data) { - $curlResult = HTTPRequest::curl($url); + $curlResult = DI::httpRequest()->curl($url); if ($curlResult->isTimeout()) { return $data; } @@ -938,7 +938,7 @@ class Probe { $xrd_timeout = DI::config()->get('system', 'xrd_timeout', 20); - $curlResult = HTTPRequest::curl($url, false, ['timeout' => $xrd_timeout, 'accept_content' => $type]); + $curlResult = DI::httpRequest()->curl($url, false, ['timeout' => $xrd_timeout, 'accept_content' => $type]); if ($curlResult->isTimeout()) { self::$istimeout = true; return []; @@ -1007,7 +1007,7 @@ class Probe */ private static function pollNoscrape($noscrape_url, $data) { - $curlResult = HTTPRequest::curl($noscrape_url); + $curlResult = DI::httpRequest()->curl($noscrape_url); if ($curlResult->isTimeout()) { self::$istimeout = true; return []; @@ -1265,7 +1265,7 @@ class Probe */ private static function pollHcard($hcard_url, $data, $dfrn = false) { - $curlResult = HTTPRequest::curl($hcard_url); + $curlResult = DI::httpRequest()->curl($hcard_url); if ($curlResult->isTimeout()) { self::$istimeout = true; return []; @@ -1519,7 +1519,7 @@ class Probe $pubkey = substr($pubkey, 5); } } elseif (Strings::normaliseLink($pubkey) == 'http://') { - $curlResult = HTTPRequest::curl($pubkey); + $curlResult = DI::httpRequest()->curl($pubkey); if ($curlResult->isTimeout()) { self::$istimeout = true; return $short ? false : []; @@ -1552,7 +1552,7 @@ class Probe } // Fetch all additional data from the feed - $curlResult = HTTPRequest::curl($data["poll"]); + $curlResult = DI::httpRequest()->curl($data["poll"]); if ($curlResult->isTimeout()) { self::$istimeout = true; return []; @@ -1604,7 +1604,7 @@ class Probe */ private static function pumpioProfileData($profile_link) { - $curlResult = HTTPRequest::curl($profile_link); + $curlResult = DI::httpRequest()->curl($profile_link); if (!$curlResult->isSuccess()) { return []; } @@ -1835,7 +1835,7 @@ class Probe */ private static function feed($url, $probe = true) { - $curlResult = HTTPRequest::curl($url); + $curlResult = DI::httpRequest()->curl($url); if ($curlResult->isTimeout()) { self::$istimeout = true; return []; diff --git a/src/Protocol/ActivityPub.php b/src/Protocol/ActivityPub.php index 24c6250a4..f41708011 100644 --- a/src/Protocol/ActivityPub.php +++ b/src/Protocol/ActivityPub.php @@ -24,7 +24,6 @@ namespace Friendica\Protocol; use Friendica\Core\Protocol; use Friendica\Model\APContact; use Friendica\Model\User; -use Friendica\Network\HTTPRequest; use Friendica\Util\HTTPSignature; use Friendica\Util\JsonLD; @@ -93,7 +92,7 @@ class ActivityPub return HTTPSignature::fetch($url, $uid); } - $curlResult = HTTPRequest::curl($url, false, ['accept_content' => 'application/activity+json, application/ld+json']); + $curlResult = DI::httpRequest()->curl($url, false, ['accept_content' => 'application/activity+json, application/ld+json']); if (!$curlResult->isSuccess() || empty($curlResult->getBody())) { return false; } diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index 21adc58cc..ef7eb0f48 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -1195,7 +1195,7 @@ class DFRN Logger::log('dfrn_deliver: ' . $url); - $curlResult = HTTPRequest::curl($url); + $curlResult = DI::httpRequest()->curl($url); if ($curlResult->isTimeout()) { return -2; // timed out diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 779c99358..a9ee2277e 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -39,7 +39,6 @@ use Friendica\Model\Item; use Friendica\Model\ItemURI; use Friendica\Model\Tag; use Friendica\Model\User; -use Friendica\Network\HTTPRequest; use Friendica\Network\Probe; use Friendica\Util\DateTimeFormat; use Friendica\Util\Images; @@ -756,7 +755,7 @@ class OStatus self::$conv_list[$conversation] = true; - $curlResult = HTTPRequest::curl($conversation, false, ['accept_content' => 'application/atom+xml, text/html']); + $curlResult = DI::httpRequest()->curl($conversation, false, ['accept_content' => 'application/atom+xml, text/html']); if (!$curlResult->isSuccess()) { return; @@ -785,7 +784,7 @@ class OStatus } } if ($file != '') { - $conversation_atom = HTTPRequest::curl($attribute['href']); + $conversation_atom = DI::httpRequest()->curl($attribute['href']); if ($conversation_atom->isSuccess()) { $xml = $conversation_atom->getBody(); @@ -902,7 +901,7 @@ class OStatus return; } - $curlResult = HTTPRequest::curl($self); + $curlResult = DI::httpRequest()->curl($self); if (!$curlResult->isSuccess()) { return; @@ -949,7 +948,7 @@ class OStatus } $stored = false; - $curlResult = HTTPRequest::curl($related, false, ['accept_content' => 'application/atom+xml, text/html']); + $curlResult = DI::httpRequest()->curl($related, false, ['accept_content' => 'application/atom+xml, text/html']); if (!$curlResult->isSuccess()) { return; @@ -980,7 +979,7 @@ class OStatus } } if ($atom_file != '') { - $curlResult = HTTPRequest::curl($atom_file); + $curlResult = DI::httpRequest()->curl($atom_file); if ($curlResult->isSuccess()) { Logger::log('Fetched XML for URI ' . $related_uri, Logger::DEBUG); @@ -992,7 +991,7 @@ class OStatus // Workaround for older GNU Social servers if (($xml == '') && strstr($related, '/notice/')) { - $curlResult = HTTPRequest::curl(str_replace('/notice/', '/api/statuses/show/', $related) . '.atom'); + $curlResult = DI::httpRequest()->curl(str_replace('/notice/', '/api/statuses/show/', $related) . '.atom'); if ($curlResult->isSuccess()) { Logger::log('GNU Social workaround to fetch XML for URI ' . $related_uri, Logger::DEBUG); @@ -1003,7 +1002,7 @@ class OStatus // Even more worse workaround for GNU Social ;-) if ($xml == '') { $related_guess = self::convertHref($related_uri); - $curlResult = HTTPRequest::curl(str_replace('/notice/', '/api/statuses/show/', $related_guess) . '.atom'); + $curlResult = DI::httpRequest()->curl(str_replace('/notice/', '/api/statuses/show/', $related_guess) . '.atom'); if ($curlResult->isSuccess()) { Logger::log('GNU Social workaround 2 to fetch XML for URI ' . $related_uri, Logger::DEBUG); diff --git a/src/Protocol/PortableContact.php b/src/Protocol/PortableContact.php index f2ad51070..0284c9b8a 100644 --- a/src/Protocol/PortableContact.php +++ b/src/Protocol/PortableContact.php @@ -251,7 +251,7 @@ class PortableContact */ private static function fetchServerlist($poco) { - $curlResult = HTTPRequest::curl($poco . "/@server"); + $curlResult = DI::httpRequest()->curl($poco . "/@server"); if (!$curlResult->isSuccess()) { return; @@ -291,7 +291,7 @@ class PortableContact Logger::info("Fetch all users from the server " . $server["url"]); - $curlResult = HTTPRequest::curl($url); + $curlResult = DI::httpRequest()->curl($url); if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { $data = json_decode($curlResult->getBody(), true); @@ -314,7 +314,7 @@ class PortableContact $success = false; - $curlResult = HTTPRequest::curl($url); + $curlResult = DI::httpRequest()->curl($url); if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { Logger::info("Fetch all global contacts from the server " . $server["nurl"]); @@ -372,7 +372,7 @@ class PortableContact // Fetch all contacts from a given user from the other server $url = $server['poco'] . '/' . $username . '/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,contactType,generation'; - $curlResult = HTTPRequest::curl($url); + $curlResult = DI::httpRequest()->curl($url); if ($curlResult->isSuccess()) { $data = json_decode($curlResult->getBody(), true); diff --git a/src/Util/ExAuth.php b/src/Util/ExAuth.php index 710c00979..593386082 100644 --- a/src/Util/ExAuth.php +++ b/src/Util/ExAuth.php @@ -37,7 +37,6 @@ namespace Friendica\Util; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\User; -use Friendica\Network\HTTPRequest; class ExAuth { @@ -182,7 +181,7 @@ class ExAuth $url = ($ssl ? 'https' : 'http') . '://' . $host . '/noscrape/' . $user; - $curlResult = HTTPRequest::curl($url); + $curlResult = DI::httpRequest()->curl($url); if (!$curlResult->isSuccess()) { return false; diff --git a/src/Util/HTTPSignature.php b/src/Util/HTTPSignature.php index 5d57c2281..8c1d4f986 100644 --- a/src/Util/HTTPSignature.php +++ b/src/Util/HTTPSignature.php @@ -443,7 +443,7 @@ class HTTPSignature $curl_opts = $opts; $curl_opts['header'] = $headers; - $curlResult = HTTPRequest::curl($request, false, $curl_opts); + $curlResult = DI::httpRequest()->curl($request, false, $curl_opts); $return_code = $curlResult->getReturnCode(); Logger::log('Fetched for user ' . $uid . ' from ' . $request . ' returned ' . $return_code, Logger::DEBUG); diff --git a/src/Util/ParseUrl.php b/src/Util/ParseUrl.php index 577ffd4c1..cf38ffd7b 100644 --- a/src/Util/ParseUrl.php +++ b/src/Util/ParseUrl.php @@ -27,7 +27,6 @@ use Friendica\Content\OEmbed; use Friendica\Core\Hook; use Friendica\Core\Logger; use Friendica\Database\DBA; -use Friendica\Network\HTTPRequest; /** * Get information about a given URL @@ -160,7 +159,7 @@ class ParseUrl return $siteinfo; } - $curlResult = HTTPRequest::curl($url); + $curlResult = DI::httpRequest()->curl($url); if (!$curlResult->isSuccess()) { return $siteinfo; } diff --git a/src/Worker/OnePoll.php b/src/Worker/OnePoll.php index 13d1b8ee7..0dc67d80f 100644 --- a/src/Worker/OnePoll.php +++ b/src/Worker/OnePoll.php @@ -291,7 +291,7 @@ class OnePoll . '&type=data&last_update=' . $last_update . '&perm=' . $perm; - $curlResult = HTTPRequest::curl($url); + $curlResult = DI::httpRequest()->curl($url); if (!$curlResult->isSuccess() && ($curlResult->getErrorNumber() == CURLE_OPERATION_TIMEDOUT)) { // set the last-update so we don't keep polling @@ -444,7 +444,7 @@ class OnePoll } $cookiejar = tempnam(get_temppath(), 'cookiejar-onepoll-'); - $curlResult = HTTPRequest::curl($contact['poll'], false, ['cookiejar' => $cookiejar]); + $curlResult = DI::httpRequest()->curl($contact['poll'], false, ['cookiejar' => $cookiejar]); unlink($cookiejar); if ($curlResult->isTimeout()) { From 8793096c16ea97cf73fb4f04199e4aab8574dcd1 Mon Sep 17 00:00:00 2001 From: nupplaPhil Date: Wed, 4 Mar 2020 22:18:28 +0100 Subject: [PATCH 04/44] Make "HTTPRequest::post" dynamic --- mod/dfrn_confirm.php | 3 +-- mod/dfrn_poll.php | 2 +- mod/match.php | 3 +-- src/Network/HTTPRequest.php | 26 ++++++++++++-------------- src/Protocol/DFRN.php | 5 ++--- src/Protocol/Diaspora.php | 2 +- src/Protocol/Salmon.php | 6 +++--- src/Util/HTTPSignature.php | 3 +-- src/Worker/OnePoll.php | 5 ++--- src/Worker/PubSubPublish.php | 3 +-- 10 files changed, 25 insertions(+), 33 deletions(-) diff --git a/mod/dfrn_confirm.php b/mod/dfrn_confirm.php index f78e8d55a..f7460b79f 100644 --- a/mod/dfrn_confirm.php +++ b/mod/dfrn_confirm.php @@ -42,7 +42,6 @@ use Friendica\Model\Contact; use Friendica\Model\Group; use Friendica\Model\Notify\Type; use Friendica\Model\User; -use Friendica\Network\HTTPRequest; use Friendica\Protocol\Activity; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; @@ -224,7 +223,7 @@ function dfrn_confirm_post(App $a, $handsfree = null) * */ - $res = HTTPRequest::post($dfrn_confirm, $params, [], 120)->getBody(); + $res = DI::httpRequest()->post($dfrn_confirm, $params, [], 120)->getBody(); Logger::log(' Confirm: received data: ' . $res, Logger::DATA); diff --git a/mod/dfrn_poll.php b/mod/dfrn_poll.php index faa55a108..00732982e 100644 --- a/mod/dfrn_poll.php +++ b/mod/dfrn_poll.php @@ -507,7 +507,7 @@ function dfrn_poll_content(App $a) . '&sec=' . $sec ); } else { - $s = HTTPRequest::post($r[0]['poll'], [ + $s = DI::httpRequest()->post($r[0]['poll'], [ 'dfrn_id' => $encrypted_id, 'type' => 'profile-check', 'dfrn_version' => DFRN_PROTOCOL_VERSION, diff --git a/mod/match.php b/mod/match.php index 4ec47c4cc..064107764 100644 --- a/mod/match.php +++ b/mod/match.php @@ -27,7 +27,6 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Profile; -use Friendica\Network\HTTPRequest; use Friendica\Util\Proxy as ProxyUtils; /** @@ -76,7 +75,7 @@ function match_content(App $a) $host = DI::baseUrl(); } - $msearch_json = HTTPRequest::post($host . '/msearch', $params)->getBody(); + $msearch_json = DI::httpRequest()->post($host . '/msearch', $params)->getBody(); $msearch = json_decode($msearch_json); diff --git a/src/Network/HTTPRequest.php b/src/Network/HTTPRequest.php index 73e5cd8e0..131fac8a7 100644 --- a/src/Network/HTTPRequest.php +++ b/src/Network/HTTPRequest.php @@ -25,7 +25,6 @@ use Friendica\App; use Friendica\Core\Config\IConfig; use Friendica\Core\Logger; use Friendica\Core\System; -use Friendica\DI; use Friendica\Util\Network; use Friendica\Util\Profiler; use Psr\Log\LoggerInterface; @@ -228,23 +227,22 @@ class HTTPRequest * @return CurlResult The content * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function post(string $url, $params, array $headers = [], int $timeout = 0, int &$redirects = 0) + public function post(string $url, $params, array $headers = [], int $timeout = 0, int &$redirects = 0) { $stamp1 = microtime(true); if (Network::isUrlBlocked($url)) { - Logger::log('post_url: domain of ' . $url . ' is blocked', Logger::DATA); + $this->logger->info('Domain is blocked.'. ['url' => $url]); return CurlResult::createErrorCurl($url); } - $a = DI::app(); $ch = curl_init($url); if (($redirects > 8) || (!$ch)) { return CurlResult::createErrorCurl($url); } - Logger::log('post_url: start ' . $url, Logger::DATA); + $this->logger->debug('Post_url: start.', ['url' => $url]); curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); @@ -252,14 +250,14 @@ class HTTPRequest curl_setopt($ch, CURLOPT_POSTFIELDS, $params); curl_setopt($ch, CURLOPT_USERAGENT, $a->getUserAgent()); - if (DI::config()->get('system', 'ipv4_resolve', false)) { + if ($this->config->get('system', 'ipv4_resolve', false)) { curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); } if (intval($timeout)) { curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); } else { - $curl_time = DI::config()->get('system', 'curl_timeout', 60); + $curl_time = $this->config->get('system', 'curl_timeout', 60); curl_setopt($ch, CURLOPT_TIMEOUT, intval($curl_time)); } @@ -267,20 +265,20 @@ class HTTPRequest curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); } - $check_cert = DI::config()->get('system', 'verifyssl'); + $check_cert = $this->config->get('system', 'verifyssl'); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false)); if ($check_cert) { @curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); } - $proxy = DI::config()->get('system', 'proxy'); + $proxy = $this->config->get('system', 'proxy'); - if (strlen($proxy)) { + if (!empty($proxy)) { curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); curl_setopt($ch, CURLOPT_PROXY, $proxy); - $proxyuser = DI::config()->get('system', 'proxyuser'); - if (strlen($proxyuser)) { + $proxyuser = $this->config->get('system', 'proxyuser'); + if (!empty($proxyuser)) { curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyuser); } } @@ -296,14 +294,14 @@ class HTTPRequest if ($curlResponse->isRedirectUrl()) { $redirects++; - Logger::log('post_url: redirect ' . $url . ' to ' . $curlResponse->getRedirectUrl()); + $this->logger->info('Post redirect.', ['url' => $url, 'to' => $curlResponse->getRedirectUrl()]); curl_close($ch); return self::post($curlResponse->getRedirectUrl(), $params, $headers, $redirects, $timeout); } curl_close($ch); - DI::profiler()->saveTimestamp($stamp1, 'network', System::callstack()); + $this->profiler->saveTimestamp($stamp1, 'network', System::callstack()); // Very old versions of Lighttpd don't like the "Expect" header, so we remove it when needed if ($curlResponse->getReturnCode() == 417) { diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index ef7eb0f48..7c87ee0b2 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -43,7 +43,6 @@ use Friendica\Model\Post\Category; use Friendica\Model\Profile; use Friendica\Model\Tag; use Friendica\Model\User; -use Friendica\Network\HTTPRequest; use Friendica\Network\Probe; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; @@ -1344,7 +1343,7 @@ class DFRN Logger::debug('dfrn_deliver', ['post' => $postvars]); - $postResult = HTTPRequest::post($contact['notify'], $postvars); + $postResult = DI::httpRequest()->post($contact['notify'], $postvars); $xml = $postResult->getBody(); @@ -1441,7 +1440,7 @@ class DFRN $content_type = ($public_batch ? "application/magic-envelope+xml" : "application/json"); - $postResult = HTTPRequest::post($dest_url, $envelope, ["Content-Type: " . $content_type]); + $postResult = DI::httpRequest()->post($dest_url, $envelope, ["Content-Type: " . $content_type]); $xml = $postResult->getBody(); $curl_stat = $postResult->getReturnCode(); diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index 46d2bc1d4..ed369a304 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -3261,7 +3261,7 @@ class Diaspora if (!intval(DI::config()->get("system", "diaspora_test"))) { $content_type = (($public_batch) ? "application/magic-envelope+xml" : "application/json"); - $postResult = HTTPRequest::post($dest_url . "/", $envelope, ["Content-Type: " . $content_type]); + $postResult = DI::httpRequest()->post($dest_url . "/", $envelope, ["Content-Type: " . $content_type]); $return_code = $postResult->getReturnCode(); } else { Logger::log("test_mode"); diff --git a/src/Protocol/Salmon.php b/src/Protocol/Salmon.php index d01ea2ce1..35707b635 100644 --- a/src/Protocol/Salmon.php +++ b/src/Protocol/Salmon.php @@ -155,7 +155,7 @@ class Salmon $salmon = XML::fromArray($xmldata, $xml, false, $namespaces); // slap them - $postResult = HTTPRequest::post($url, $salmon, [ + $postResult = DI::httpRequest()->post($url, $salmon, [ 'Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon) ]); @@ -180,7 +180,7 @@ class Salmon $salmon = XML::fromArray($xmldata, $xml, false, $namespaces); // slap them - $postResult = HTTPRequest::post($url, $salmon, [ + $postResult = DI::httpRequest()->post($url, $salmon, [ 'Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon) ]); @@ -203,7 +203,7 @@ class Salmon $salmon = XML::fromArray($xmldata, $xml, false, $namespaces); // slap them - $postResult = HTTPRequest::post($url, $salmon, [ + $postResult = DI::httpRequest()->post($url, $salmon, [ 'Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon)]); $return_code = $postResult->getReturnCode(); diff --git a/src/Util/HTTPSignature.php b/src/Util/HTTPSignature.php index 8c1d4f986..84a11b8ec 100644 --- a/src/Util/HTTPSignature.php +++ b/src/Util/HTTPSignature.php @@ -26,7 +26,6 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\APContact; use Friendica\Model\User; -use Friendica\Network\HTTPRequest; /** * Implements HTTP Signatures per draft-cavage-http-signatures-07. @@ -298,7 +297,7 @@ class HTTPSignature $headers[] = 'Content-Type: application/activity+json'; - $postResult = HTTPRequest::post($target, $content, $headers); + $postResult = DI::httpRequest()->post($target, $content, $headers); $return_code = $postResult->getReturnCode(); Logger::log('Transmit to ' . $target . ' returned ' . $return_code, Logger::DEBUG); diff --git a/src/Worker/OnePoll.php b/src/Worker/OnePoll.php index 0dc67d80f..ed6bfacb3 100644 --- a/src/Worker/OnePoll.php +++ b/src/Worker/OnePoll.php @@ -28,7 +28,6 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Item; use Friendica\Model\User; -use Friendica\Network\HTTPRequest; use Friendica\Protocol\Activity; use Friendica\Protocol\ActivityPub; use Friendica\Protocol\Email; @@ -405,7 +404,7 @@ class OnePoll $postvars['dfrn_version'] = DFRN_PROTOCOL_VERSION; $postvars['perm'] = 'rw'; - return HTTPRequest::post($contact['poll'], $postvars)->getBody(); + return DI::httpRequest()->post($contact['poll'], $postvars)->getBody(); } /** @@ -756,7 +755,7 @@ class OnePoll DBA::update('contact', ['hub-verify' => $verify_token], ['id' => $contact['id']]); } - $postResult = HTTPRequest::post($url, $params); + $postResult = DI::httpRequest()->post($url, $params); Logger::log('subscribe_to_hub: returns: ' . $postResult->getReturnCode(), Logger::DEBUG); diff --git a/src/Worker/PubSubPublish.php b/src/Worker/PubSubPublish.php index 4fa3b3e89..eab68b430 100644 --- a/src/Worker/PubSubPublish.php +++ b/src/Worker/PubSubPublish.php @@ -25,7 +25,6 @@ use Friendica\Core\Logger; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\PushSubscriber; -use Friendica\Network\HTTPRequest; use Friendica\Protocol\OStatus; class PubSubPublish @@ -68,7 +67,7 @@ class PubSubPublish Logger::log('POST ' . print_r($headers, true) . "\n" . $params, Logger::DATA); - $postResult = HTTPRequest::post($subscriber['callback_url'], $params, $headers); + $postResult = DI::httpRequest()->post($subscriber['callback_url'], $params, $headers); $ret = $postResult->getReturnCode(); if ($ret >= 200 && $ret <= 299) { From 3b4cf87c95a6a2b5ca46b2fbcb65c4e9e0e94dbc Mon Sep 17 00:00:00 2001 From: nupplaPhil Date: Wed, 4 Mar 2020 22:19:59 +0100 Subject: [PATCH 05/44] Make "HTTPRequest::fetchUrlFull" dynamic --- mod/pubsubhubbub.php | 2 +- src/Core/Installer.php | 4 ++-- src/Network/HTTPRequest.php | 4 ++-- src/Protocol/PortableContact.php | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mod/pubsubhubbub.php b/mod/pubsubhubbub.php index 9403d3eb7..96f26838e 100644 --- a/mod/pubsubhubbub.php +++ b/mod/pubsubhubbub.php @@ -126,7 +126,7 @@ function pubsubhubbub_init(App $a) { $hub_callback = rtrim($hub_callback, ' ?&#'); $separator = parse_url($hub_callback, PHP_URL_QUERY) === null ? '?' : '&'; - $fetchResult = HTTPRequest::fetchUrlFull($hub_callback . $separator . $params); + $fetchResult = DI::httpRequest()->fetchUrlFull($hub_callback . $separator . $params); $body = $fetchResult->getBody(); $ret = $fetchResult->getReturnCode(); diff --git a/src/Core/Installer.php b/src/Core/Installer.php index 7b6291ff3..af7c7aa49 100644 --- a/src/Core/Installer.php +++ b/src/Core/Installer.php @@ -548,11 +548,11 @@ class Installer $help = ""; $error_msg = ""; if (function_exists('curl_init')) { - $fetchResult = HTTPRequest::fetchUrlFull($baseurl . "/install/testrewrite"); + $fetchResult = DI::httpRequest()->fetchUrlFull($baseurl . "/install/testrewrite"); $url = Strings::normaliseLink($baseurl . "/install/testrewrite"); if ($fetchResult->getReturnCode() != 204) { - $fetchResult = HTTPRequest::fetchUrlFull($url); + $fetchResult = DI::httpRequest()->fetchUrlFull($url); } if ($fetchResult->getReturnCode() != 204) { diff --git a/src/Network/HTTPRequest.php b/src/Network/HTTPRequest.php index 131fac8a7..14395c37f 100644 --- a/src/Network/HTTPRequest.php +++ b/src/Network/HTTPRequest.php @@ -365,9 +365,9 @@ class HTTPRequest * @return CurlResult With all relevant information, 'body' contains the actual fetched content. * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function fetchUrlFull(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = '', int &$redirects = 0) + public function fetchUrlFull(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = '', int &$redirects = 0) { - return self::curl( + return $this->curl( $url, $binary, [ diff --git a/src/Protocol/PortableContact.php b/src/Protocol/PortableContact.php index 0284c9b8a..f118ffa94 100644 --- a/src/Protocol/PortableContact.php +++ b/src/Protocol/PortableContact.php @@ -103,7 +103,7 @@ class PortableContact Logger::log('load: ' . $url, Logger::DEBUG); - $fetchresult = HTTPRequest::fetchUrlFull($url); + $fetchresult = DI::httpRequest()->fetchUrlFull($url); $s = $fetchresult->getBody(); Logger::log('load: returns ' . $s, Logger::DATA); From 1aa07f87a44b50e93f25006c4007450834abda6f Mon Sep 17 00:00:00 2001 From: nupplaPhil Date: Wed, 4 Mar 2020 22:20:28 +0100 Subject: [PATCH 06/44] Make "HTTPRequest::fetchUrl" dynamic --- mod/dfrn_poll.php | 5 ++--- mod/dfrn_request.php | 3 +-- mod/oexchange.php | 3 +-- src/Content/OEmbed.php | 7 +++---- src/Content/Text/BBCode.php | 7 +++---- src/Core/Protocol.php | 4 +--- src/Core/Search.php | 3 +-- src/Core/Worker.php | 3 +-- src/Model/GContact.php | 3 +-- src/Model/GServer.php | 3 +-- src/Model/User.php | 3 +-- src/Module/Debug/Feed.php | 3 +-- src/Network/HTTPRequest.php | 4 ++-- src/Protocol/Diaspora.php | 3 +-- src/Protocol/Salmon.php | 3 +-- src/Util/Images.php | 3 +-- src/Worker/CheckVersion.php | 3 +-- src/Worker/CronJobs.php | 3 +-- src/Worker/Directory.php | 3 +-- src/Worker/SearchDirectory.php | 3 +-- 20 files changed, 26 insertions(+), 46 deletions(-) diff --git a/mod/dfrn_poll.php b/mod/dfrn_poll.php index 00732982e..7f7fbe498 100644 --- a/mod/dfrn_poll.php +++ b/mod/dfrn_poll.php @@ -25,7 +25,6 @@ use Friendica\Core\Session; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Network\HTTPRequest; use Friendica\Protocol\DFRN; use Friendica\Protocol\OStatus; use Friendica\Util\Strings; @@ -115,7 +114,7 @@ function dfrn_poll_init(App $a) ); if (DBA::isResult($r)) { - $s = HTTPRequest::fetchUrl($r[0]['poll'] . '?dfrn_id=' . $my_id . '&type=profile-check'); + $s = DI::httpRequest()->fetchUrl($r[0]['poll'] . '?dfrn_id=' . $my_id . '&type=profile-check'); Logger::log("dfrn_poll: old profile returns " . $s, Logger::DATA); @@ -499,7 +498,7 @@ function dfrn_poll_content(App $a) // URL reply if ($dfrn_version < 2.2) { - $s = HTTPRequest::fetchUrl($r[0]['poll'] + $s = DI::httpRequest()->fetchUrl($r[0]['poll'] . '?dfrn_id=' . $encrypted_id . '&type=profile-check' . '&dfrn_version=' . DFRN_PROTOCOL_VERSION diff --git a/mod/dfrn_request.php b/mod/dfrn_request.php index bdc407b0b..8c8557650 100644 --- a/mod/dfrn_request.php +++ b/mod/dfrn_request.php @@ -39,7 +39,6 @@ use Friendica\Model\Notify\Type; use Friendica\Model\Profile; use Friendica\Model\User; use Friendica\Module\Security\Login; -use Friendica\Network\HTTPRequest; use Friendica\Network\Probe; use Friendica\Protocol\Activity; use Friendica\Util\DateTimeFormat; @@ -204,7 +203,7 @@ function dfrn_request_post(App $a) } if (!empty($dfrn_request) && strlen($confirm_key)) { - HTTPRequest::fetchUrl($dfrn_request . '?confirm_key=' . $confirm_key); + DI::httpRequest()->fetchUrl($dfrn_request . '?confirm_key=' . $confirm_key); } // (ignore reply, nothing we can do it failed) diff --git a/mod/oexchange.php b/mod/oexchange.php index 523889332..4746651c1 100644 --- a/mod/oexchange.php +++ b/mod/oexchange.php @@ -23,7 +23,6 @@ use Friendica\App; use Friendica\Core\Renderer; use Friendica\DI; use Friendica\Module\Security\Login; -use Friendica\Network\HTTPRequest; use Friendica\Util\Strings; function oexchange_init(App $a) { @@ -58,7 +57,7 @@ function oexchange_content(App $a) { $tags = ((!empty($_REQUEST['tags'])) ? '&tags=' . urlencode(Strings::escapeTags(trim($_REQUEST['tags']))) : ''); - $s = HTTPRequest::fetchUrl(DI::baseUrl() . '/parse_url?url=' . $url . $title . $description . $tags); + $s = DI::httpRequest()->fetchUrl(DI::baseUrl() . '/parse_url?url=' . $url . $title . $description . $tags); if (!strlen($s)) { return; diff --git a/src/Content/OEmbed.php b/src/Content/OEmbed.php index 8cfb8ce0a..592d25c10 100644 --- a/src/Content/OEmbed.php +++ b/src/Content/OEmbed.php @@ -31,7 +31,6 @@ use Friendica\Core\Hook; use Friendica\Core\Renderer; use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Network\HTTPRequest; use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; use Friendica\Util\ParseUrl; @@ -96,7 +95,7 @@ class OEmbed if (!in_array($ext, $noexts)) { // try oembed autodiscovery - $html_text = HTTPRequest::fetchUrl($embedurl, false, 15, 'text/*'); + $html_text = DI::httpRequest()->fetchUrl($embedurl, false, 15, 'text/*'); if ($html_text) { $dom = @DOMDocument::loadHTML($html_text); if ($dom) { @@ -104,14 +103,14 @@ class OEmbed $entries = $xpath->query("//link[@type='application/json+oembed']"); foreach ($entries as $e) { $href = $e->getAttributeNode('href')->nodeValue; - $json_string = HTTPRequest::fetchUrl($href . '&maxwidth=' . $a->videowidth); + $json_string = DI::httpRequest()->fetchUrl($href . '&maxwidth=' . $a->videowidth); break; } $entries = $xpath->query("//link[@type='text/json+oembed']"); foreach ($entries as $e) { $href = $e->getAttributeNode('href')->nodeValue; - $json_string = HTTPRequest::fetchUrl($href . '&maxwidth=' . $a->videowidth); + $json_string = DI::httpRequest()->fetchUrl($href . '&maxwidth=' . $a->videowidth); break; } } diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index f03ea6104..9d9679c49 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -38,7 +38,6 @@ use Friendica\Model\Contact; use Friendica\Model\Event; use Friendica\Model\Photo; use Friendica\Model\Tag; -use Friendica\Network\HTTPRequest; use Friendica\Object\Image; use Friendica\Protocol\Activity; use Friendica\Util\Images; @@ -486,7 +485,7 @@ class BBCode continue; } - $curlResult = HTTPRequest::curl($mtch[1], true); + $curlResult = DI::httpRequest()->curl($mtch[1], true); if (!$curlResult->isSuccess()) { continue; } @@ -1107,7 +1106,7 @@ class BBCode $text = "[url=" . $match[2] . ']' . $match[2] . "[/url]"; // if its not a picture then look if its a page that contains a picture link - $body = HTTPRequest::fetchUrl($match[1]); + $body = DI::httpRequest()->fetchUrl($match[1]); $doc = new DOMDocument(); @$doc->loadHTML($body); @@ -1186,7 +1185,7 @@ class BBCode } // if its not a picture then look if its a page that contains a picture link - $body = HTTPRequest::fetchUrl($match[1]); + $body = DI::httpRequest()->fetchUrl($match[1]); $doc = new DOMDocument(); @$doc->loadHTML($body); diff --git a/src/Core/Protocol.php b/src/Core/Protocol.php index 84b589bf2..e6133240c 100644 --- a/src/Core/Protocol.php +++ b/src/Core/Protocol.php @@ -21,8 +21,6 @@ namespace Friendica\Core; -use Friendica\Network\HTTPRequest; - /** * Manage compatibility with federated networks */ @@ -123,7 +121,7 @@ class Protocol if (preg_match('=https?://(.*)/user/(.*)=ism', $profile_url, $matches)) { $statusnet_host = $matches[1]; $statusnet_user = $matches[2]; - $UserData = HTTPRequest::fetchUrl('http://' . $statusnet_host . '/api/users/show.json?user_id=' . $statusnet_user); + $UserData = DI::httpRequest()->fetchUrl('http://' . $statusnet_host . '/api/users/show.json?user_id=' . $statusnet_user); $user = json_decode($UserData); if ($user) { $matches[2] = $user->screen_name; diff --git a/src/Core/Search.php b/src/Core/Search.php index aafa4024a..768a0bf27 100644 --- a/src/Core/Search.php +++ b/src/Core/Search.php @@ -26,7 +26,6 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\GContact; use Friendica\Network\HTTPException; -use Friendica\Network\HTTPRequest; use Friendica\Object\Search\ContactResult; use Friendica\Object\Search\ResultList; use Friendica\Util\Network; @@ -124,7 +123,7 @@ class Search $searchUrl .= '&page=' . $page; } - $resultJson = HTTPRequest::fetchUrl($searchUrl, false, 0, 'application/json'); + $resultJson = DI::httpRequest()->fetchUrl($searchUrl, false, 0, 'application/json'); $results = json_decode($resultJson, true); diff --git a/src/Core/Worker.php b/src/Core/Worker.php index a5c4226c4..83a24c38f 100644 --- a/src/Core/Worker.php +++ b/src/Core/Worker.php @@ -25,7 +25,6 @@ use Friendica\Core; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Process; -use Friendica\Network\HTTPRequest; use Friendica\Util\DateTimeFormat; /** @@ -997,7 +996,7 @@ class Worker } $url = DI::baseUrl() . '/worker'; - HTTPRequest::fetchUrl($url, false, 1); + DI::httpRequest()->fetchUrl($url, false, 1); } /** diff --git a/src/Model/GContact.php b/src/Model/GContact.php index 669bc60c5..dabc907f8 100644 --- a/src/Model/GContact.php +++ b/src/Model/GContact.php @@ -31,7 +31,6 @@ use Friendica\Core\System; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Network\HTTPRequest; use Friendica\Network\Probe; use Friendica\Protocol\ActivityPub; use Friendica\Protocol\PortableContact; @@ -537,7 +536,7 @@ class GContact $done[] = DI::baseUrl() . '/poco'; if (strlen(DI::config()->get('system', 'directory'))) { - $x = HTTPRequest::fetchUrl(Search::getGlobalDirectory() . '/pubsites'); + $x = DI::httpRequest()->fetchUrl(Search::getGlobalDirectory() . '/pubsites'); if (!empty($x)) { $j = json_decode($x); if (!empty($j->entries)) { diff --git a/src/Model/GServer.php b/src/Model/GServer.php index c868e19d4..76082fe1f 100644 --- a/src/Model/GServer.php +++ b/src/Model/GServer.php @@ -31,7 +31,6 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Module\Register; use Friendica\Network\CurlResult; -use Friendica\Network\HTTPRequest; use Friendica\Protocol\Diaspora; use Friendica\Protocol\PortableContact; use Friendica\Util\DateTimeFormat; @@ -1635,7 +1634,7 @@ class GServer $protocols = ['activitypub', 'diaspora', 'dfrn', 'ostatus']; foreach ($protocols as $protocol) { $query = '{nodes(protocol:"' . $protocol . '"){host}}'; - $curlResult = HTTPRequest::fetchUrl('https://the-federation.info/graphql?query=' . urlencode($query)); + $curlResult = DI::httpRequest()->fetchUrl('https://the-federation.info/graphql?query=' . urlencode($query)); if (!empty($curlResult)) { $data = json_decode($curlResult, true); if (!empty($data['data']['nodes'])) { diff --git a/src/Model/User.php b/src/Model/User.php index fda105687..9980c48d5 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -34,7 +34,6 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\TwoFactor\AppSpecificPassword; use Friendica\Network\HTTPException\InternalServerErrorException; -use Friendica\Network\HTTPRequest; use Friendica\Object\Image; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; @@ -824,7 +823,7 @@ class User $photo_failure = false; $filename = basename($photo); - $curlResult = HTTPRequest::curl($photo, true); + $curlResult = DI::httpRequest()->curl($photo, true); if ($curlResult->isSuccess()) { $img_str = $curlResult->getBody(); $type = $curlResult->getContentType(); diff --git a/src/Module/Debug/Feed.php b/src/Module/Debug/Feed.php index 6214b49dd..deeb8d7ec 100644 --- a/src/Module/Debug/Feed.php +++ b/src/Module/Debug/Feed.php @@ -25,7 +25,6 @@ use Friendica\BaseModule; use Friendica\Core\Renderer; use Friendica\DI; use Friendica\Model; -use Friendica\Network\HTTPRequest; use Friendica\Protocol; /** @@ -49,7 +48,7 @@ class Feed extends BaseModule $contact = Model\Contact::getByURLForUser($url, local_user(), false); - $xml = HTTPRequest::fetchUrl($contact['poll']); + $xml = DI::httpRequest()->fetchUrl($contact['poll']); $import_result = Protocol\Feed::import($xml); diff --git a/src/Network/HTTPRequest.php b/src/Network/HTTPRequest.php index 14395c37f..eaef6966d 100644 --- a/src/Network/HTTPRequest.php +++ b/src/Network/HTTPRequest.php @@ -341,9 +341,9 @@ class HTTPRequest * @return string The fetched content * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function fetchUrl(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = '', int &$redirects = 0) + public function fetchUrl(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = '', int &$redirects = 0) { - $ret = self::fetchUrlFull($url, $binary, $timeout, $accept_content, $cookiejar, $redirects); + $ret = $this->fetchUrlFull($url, $binary, $timeout, $accept_content, $cookiejar, $redirects); return $ret->getBody(); } diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index ed369a304..5e1f09677 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -41,7 +41,6 @@ use Friendica\Model\Mail; use Friendica\Model\Post; use Friendica\Model\Tag; use Friendica\Model\User; -use Friendica\Network\HTTPRequest; use Friendica\Network\Probe; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; @@ -1380,7 +1379,7 @@ class Diaspora Logger::log("Fetch post from ".$source_url, Logger::DEBUG); - $envelope = HTTPRequest::fetchUrl($source_url); + $envelope = DI::httpRequest()->fetchUrl($source_url); if ($envelope) { Logger::log("Envelope was fetched.", Logger::DEBUG); $x = self::verifyMagicEnvelope($envelope); diff --git a/src/Protocol/Salmon.php b/src/Protocol/Salmon.php index 35707b635..921c060f8 100644 --- a/src/Protocol/Salmon.php +++ b/src/Protocol/Salmon.php @@ -22,7 +22,6 @@ namespace Friendica\Protocol; use Friendica\Core\Logger; -use Friendica\Network\HTTPRequest; use Friendica\Network\Probe; use Friendica\Util\Crypto; use Friendica\Util\Strings; @@ -72,7 +71,7 @@ class Salmon $ret[$x] = substr($ret[$x], 5); } } elseif (Strings::normaliseLink($ret[$x]) == 'http://') { - $ret[$x] = HTTPRequest::fetchUrl($ret[$x]); + $ret[$x] = DI::httpRequest()->fetchUrl($ret[$x]); } } } diff --git a/src/Util/Images.php b/src/Util/Images.php index 9e3be4f4f..2d161a5c4 100644 --- a/src/Util/Images.php +++ b/src/Util/Images.php @@ -24,7 +24,6 @@ namespace Friendica\Util; use Friendica\Core\Logger; use Friendica\Core\System; use Friendica\DI; -use Friendica\Network\HTTPRequest; /** * Image utilities @@ -185,7 +184,7 @@ class Images return $data; } - $img_str = HTTPRequest::fetchUrl($url, true, 4); + $img_str = DI::httpRequest()->fetchUrl($url, true, 4); if (!$img_str) { return []; diff --git a/src/Worker/CheckVersion.php b/src/Worker/CheckVersion.php index f0369daab..260d6b16f 100644 --- a/src/Worker/CheckVersion.php +++ b/src/Worker/CheckVersion.php @@ -24,7 +24,6 @@ namespace Friendica\Worker; use Friendica\Core\Logger; use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Network\HTTPRequest; /** * Check the git repository VERSION file and save the version to the DB @@ -55,7 +54,7 @@ class CheckVersion Logger::log("Checking VERSION from: ".$checked_url, Logger::DEBUG); // fetch the VERSION file - $gitversion = DBA::escape(trim(HTTPRequest::fetchUrl($checked_url))); + $gitversion = DBA::escape(trim(DI::httpRequest()->fetchUrl($checked_url))); Logger::log("Upstream VERSION is: ".$gitversion, Logger::DEBUG); DI::config()->set('system', 'git_friendica_version', $gitversion); diff --git a/src/Worker/CronJobs.php b/src/Worker/CronJobs.php index 23434beb1..1e4505856 100644 --- a/src/Worker/CronJobs.php +++ b/src/Worker/CronJobs.php @@ -33,7 +33,6 @@ use Friendica\Model\GContact; use Friendica\Model\Nodeinfo; use Friendica\Model\Photo; use Friendica\Model\User; -use Friendica\Network\HTTPRequest; use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Strings; @@ -61,7 +60,7 @@ class CronJobs // Now trying to register $url = 'http://the-federation.info/register/' . DI::baseUrl()->getHostname(); Logger::debug('Check registering url', ['url' => $url]); - $ret = HTTPRequest::fetchUrl($url); + $ret = DI::httpRequest()->fetchUrl($url); Logger::debug('Check registering answer', ['answer' => $ret]); Logger::info('cron_end'); break; diff --git a/src/Worker/Directory.php b/src/Worker/Directory.php index 0dea9841f..2cab09f33 100644 --- a/src/Worker/Directory.php +++ b/src/Worker/Directory.php @@ -26,7 +26,6 @@ use Friendica\Core\Logger; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Network\HTTPRequest; /** * Sends updated profile data to the directory @@ -54,7 +53,7 @@ class Directory Logger::log('Updating directory: ' . $arr['url'], Logger::DEBUG); if (strlen($arr['url'])) { - HTTPRequest::fetchUrl($dir . '?url=' . bin2hex($arr['url'])); + DI::httpRequest()->fetchUrl($dir . '?url=' . bin2hex($arr['url'])); } return; diff --git a/src/Worker/SearchDirectory.php b/src/Worker/SearchDirectory.php index 1dcf0c8db..c3c344d93 100644 --- a/src/Worker/SearchDirectory.php +++ b/src/Worker/SearchDirectory.php @@ -30,7 +30,6 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\GContact; use Friendica\Model\GServer; -use Friendica\Network\HTTPRequest; use Friendica\Util\Strings; class SearchDirectory @@ -52,7 +51,7 @@ class SearchDirectory } } - $x = HTTPRequest::fetchUrl(Search::getGlobalDirectory() . '/lsearch?p=1&n=500&search=' . urlencode($search)); + $x = DI::httpRequest()->fetchUrl(Search::getGlobalDirectory() . '/lsearch?p=1&n=500&search=' . urlencode($search)); $j = json_decode($x); if (!empty($j->results)) { From cebdcb65991607c3a13ce691f60472e141e5d1af Mon Sep 17 00:00:00 2001 From: nupplaPhil Date: Wed, 4 Mar 2020 22:20:50 +0100 Subject: [PATCH 07/44] Fixing post() --- src/Network/HTTPRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Network/HTTPRequest.php b/src/Network/HTTPRequest.php index eaef6966d..ac2d70cad 100644 --- a/src/Network/HTTPRequest.php +++ b/src/Network/HTTPRequest.php @@ -248,7 +248,7 @@ class HTTPRequest curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $params); - curl_setopt($ch, CURLOPT_USERAGENT, $a->getUserAgent()); + curl_setopt($ch, CURLOPT_USERAGENT, $this->userAgent); if ($this->config->get('system', 'ipv4_resolve', false)) { curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); From 57587efe58e1ef9aed8c218f264696b10b331fea Mon Sep 17 00:00:00 2001 From: nupplaPhil Date: Wed, 4 Mar 2020 22:26:38 +0100 Subject: [PATCH 08/44] Move "getUserAgent()" to "HTTPRequest" class --- src/App.php | 16 ---------------- src/Content/Text/BBCode.php | 4 ++-- src/Network/HTTPRequest.php | 29 ++++++++++++++++++++++------- src/Util/Network.php | 4 ++-- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/App.php b/src/App.php index 9b6f6a5a2..65ae3fe2f 100644 --- a/src/App.php +++ b/src/App.php @@ -240,22 +240,6 @@ class App } } - /** - * Returns the current UserAgent as a String - * - * @return string the UserAgent as a String - * @throws HTTPException\InternalServerErrorException - */ - public function getUserAgent() - { - return - FRIENDICA_PLATFORM . " '" . - FRIENDICA_CODENAME . "' " . - FRIENDICA_VERSION . '-' . - DB_UPDATE_VERSION . '; ' . - $this->baseURL->get(); - } - /** * Returns the current theme name. May be overriden by the mobile theme name. * diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index 9d9679c49..036bc4a27 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -1094,7 +1094,7 @@ class BBCode $ch = @curl_init($match[1]); @curl_setopt($ch, CURLOPT_NOBODY, true); @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - @curl_setopt($ch, CURLOPT_USERAGENT, $a->getUserAgent()); + @curl_setopt($ch, CURLOPT_USERAGENT, DI::httpRequest()->getUserAgent()); @curl_exec($ch); $curl_info = @curl_getinfo($ch); @@ -1168,7 +1168,7 @@ class BBCode $ch = @curl_init($match[1]); @curl_setopt($ch, CURLOPT_NOBODY, true); @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - @curl_setopt($ch, CURLOPT_USERAGENT, $a->getUserAgent()); + @curl_setopt($ch, CURLOPT_USERAGENT, DI::httpRequest()->getUserAgent()); @curl_exec($ch); $curl_info = @curl_getinfo($ch); diff --git a/src/Network/HTTPRequest.php b/src/Network/HTTPRequest.php index ac2d70cad..1a0048b2a 100644 --- a/src/Network/HTTPRequest.php +++ b/src/Network/HTTPRequest.php @@ -41,14 +41,14 @@ class HTTPRequest /** @var IConfig */ private $config; /** @var string */ - private $userAgent; + private $baseUrl; - public function __construct(LoggerInterface $logger, Profiler $profiler, IConfig $config, App $a) + public function __construct(LoggerInterface $logger, Profiler $profiler, IConfig $config, App\BaseURL $baseUrl) { - $this->logger = $logger; - $this->profiler = $profiler; - $this->config = $config; - $this->userAgent = $a->getUserAgent(); + $this->logger = $logger; + $this->profiler = $profiler; + $this->config = $config; + $this->baseUrl = $baseUrl->get(); } /** @@ -232,7 +232,7 @@ class HTTPRequest $stamp1 = microtime(true); if (Network::isUrlBlocked($url)) { - $this->logger->info('Domain is blocked.'. ['url' => $url]); + $this->logger->info('Domain is blocked.' . ['url' => $url]); return CurlResult::createErrorCurl($url); } @@ -378,4 +378,19 @@ class HTTPRequest $redirects ); } + + /** + * Returns the current UserAgent as a String + * + * @return string the UserAgent as a String + */ + public function getUserAgent() + { + return + FRIENDICA_PLATFORM . " '" . + FRIENDICA_CODENAME . "' " . + FRIENDICA_VERSION . '-' . + DB_UPDATE_VERSION . '; ' . + $this->baseUrl; + } } diff --git a/src/Util/Network.php b/src/Util/Network.php index 888dc20a6..a8b216b34 100644 --- a/src/Util/Network.php +++ b/src/Util/Network.php @@ -350,7 +350,7 @@ class Network curl_setopt($ch, CURLOPT_NOBODY, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_USERAGENT, $a->getUserAgent()); + curl_setopt($ch, CURLOPT_USERAGENT, DI::httpRequest()->getUserAgent()); curl_exec($ch); $curl_info = @curl_getinfo($ch); @@ -394,7 +394,7 @@ class Network curl_setopt($ch, CURLOPT_NOBODY, 0); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_USERAGENT, $a->getUserAgent()); + curl_setopt($ch, CURLOPT_USERAGENT, DI::httpRequest()->getUserAgent()); $body = curl_exec($ch); curl_close($ch); From 60e18736b0555e336b0f06c9a0849f39cded4414 Mon Sep 17 00:00:00 2001 From: nupplaPhil Date: Wed, 4 Mar 2020 22:30:24 +0100 Subject: [PATCH 09/44] Move "Network::finalUrl" to "HTTPRequest" class --- src/Network/HTTPRequest.php | 121 +++++++++++++++++++++++++++++++++++ src/Protocol/Feed.php | 3 +- src/Util/Network.php | 123 ------------------------------------ 3 files changed, 123 insertions(+), 124 deletions(-) diff --git a/src/Network/HTTPRequest.php b/src/Network/HTTPRequest.php index 1a0048b2a..08ac203f5 100644 --- a/src/Network/HTTPRequest.php +++ b/src/Network/HTTPRequest.php @@ -21,10 +21,13 @@ namespace Friendica\Network; +use DOMDocument; +use DomXPath; use Friendica\App; use Friendica\Core\Config\IConfig; use Friendica\Core\Logger; use Friendica\Core\System; +use Friendica\DI; use Friendica\Util\Network; use Friendica\Util\Profiler; use Psr\Log\LoggerInterface; @@ -323,6 +326,124 @@ class HTTPRequest return $curlResponse; } + /** + * Returns the original URL of the provided URL + * + * This function strips tracking query params and follows redirections, either + * through HTTP code or meta refresh tags. Stops after 10 redirections. + * + * @todo Remove the $fetchbody parameter that generates an extraneous HEAD request + * + * @see ParseUrl::getSiteinfo + * + * @param string $url A user-submitted URL + * @param int $depth The current redirection recursion level (internal) + * @param bool $fetchbody Wether to fetch the body or not after the HEAD requests + * @return string A canonical URL + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public static function finalUrl(string $url, int $depth = 1, bool $fetchbody = false) + { + $url = Network::stripTrackingQueryParams($url); + + if ($depth > 10) { + return $url; + } + + $url = trim($url, "'"); + + $stamp1 = microtime(true); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_HEADER, 1); + curl_setopt($ch, CURLOPT_NOBODY, 1); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_USERAGENT, DI::httpRequest()->getUserAgent()); + + curl_exec($ch); + $curl_info = @curl_getinfo($ch); + $http_code = $curl_info['http_code']; + curl_close($ch); + + DI::profiler()->saveTimestamp($stamp1, "network", System::callstack()); + + if ($http_code == 0) { + return $url; + } + + if (in_array($http_code, ['301', '302'])) { + if (!empty($curl_info['redirect_url'])) { + return self::finalUrl($curl_info['redirect_url'], ++$depth, $fetchbody); + } elseif (!empty($curl_info['location'])) { + return self::finalUrl($curl_info['location'], ++$depth, $fetchbody); + } + } + + // Check for redirects in the meta elements of the body if there are no redirects in the header. + if (!$fetchbody) { + return self::finalUrl($url, ++$depth, true); + } + + // if the file is too large then exit + if ($curl_info["download_content_length"] > 1000000) { + return $url; + } + + // if it isn't a HTML file then exit + if (!empty($curl_info["content_type"]) && !strstr(strtolower($curl_info["content_type"]), "html")) { + return $url; + } + + $stamp1 = microtime(true); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_NOBODY, 0); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_USERAGENT, DI::httpRequest()->getUserAgent()); + + $body = curl_exec($ch); + curl_close($ch); + + DI::profiler()->saveTimestamp($stamp1, "network", System::callstack()); + + if (trim($body) == "") { + return $url; + } + + // Check for redirect in meta elements + $doc = new DOMDocument(); + @$doc->loadHTML($body); + + $xpath = new DomXPath($doc); + + $list = $xpath->query("//meta[@content]"); + foreach ($list as $node) { + $attr = []; + if ($node->attributes->length) { + foreach ($node->attributes as $attribute) { + $attr[$attribute->name] = $attribute->value; + } + } + + if (@$attr["http-equiv"] == 'refresh') { + $path = $attr["content"]; + $pathinfo = explode(";", $path); + foreach ($pathinfo as $value) { + if (substr(strtolower($value), 0, 4) == "url=") { + return self::finalUrl(substr($value, 4), ++$depth); + } + } + } + } + + return $url; + } + /** * Curl wrapper * diff --git a/src/Protocol/Feed.php b/src/Protocol/Feed.php index a665b7c85..a609ae296 100644 --- a/src/Protocol/Feed.php +++ b/src/Protocol/Feed.php @@ -35,6 +35,7 @@ use Friendica\Model\Contact; use Friendica\Model\Item; use Friendica\Model\Tag; use Friendica\Model\User; +use Friendica\Network\HTTPRequest; use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; use Friendica\Util\ParseUrl; @@ -350,7 +351,7 @@ class Feed $orig_plink = $item["plink"]; - $item["plink"] = Network::finalUrl($item["plink"]); + $item["plink"] = HTTPRequest::finalUrl($item["plink"]); $item["parent-uri"] = $item["uri"]; diff --git a/src/Util/Network.php b/src/Util/Network.php index a8b216b34..7795b0cd2 100644 --- a/src/Util/Network.php +++ b/src/Util/Network.php @@ -21,11 +21,8 @@ namespace Friendica\Util; -use DOMDocument; -use DomXPath; use Friendica\Core\Hook; use Friendica\Core\Logger; -use Friendica\Core\System; use Friendica\DI; class Network @@ -314,126 +311,6 @@ class Network return self::unparseURL($parts); } - /** - * Returns the original URL of the provided URL - * - * This function strips tracking query params and follows redirections, either - * through HTTP code or meta refresh tags. Stops after 10 redirections. - * - * @todo Remove the $fetchbody parameter that generates an extraneous HEAD request - * - * @see ParseUrl::getSiteinfo - * - * @param string $url A user-submitted URL - * @param int $depth The current redirection recursion level (internal) - * @param bool $fetchbody Wether to fetch the body or not after the HEAD requests - * @return string A canonical URL - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public static function finalUrl(string $url, int $depth = 1, bool $fetchbody = false) - { - $a = DI::app(); - - $url = self::stripTrackingQueryParams($url); - - if ($depth > 10) { - return $url; - } - - $url = trim($url, "'"); - - $stamp1 = microtime(true); - - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_HEADER, 1); - curl_setopt($ch, CURLOPT_NOBODY, 1); - curl_setopt($ch, CURLOPT_TIMEOUT, 10); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_USERAGENT, DI::httpRequest()->getUserAgent()); - - curl_exec($ch); - $curl_info = @curl_getinfo($ch); - $http_code = $curl_info['http_code']; - curl_close($ch); - - DI::profiler()->saveTimestamp($stamp1, "network", System::callstack()); - - if ($http_code == 0) { - return $url; - } - - if (in_array($http_code, ['301', '302'])) { - if (!empty($curl_info['redirect_url'])) { - return self::finalUrl($curl_info['redirect_url'], ++$depth, $fetchbody); - } elseif (!empty($curl_info['location'])) { - return self::finalUrl($curl_info['location'], ++$depth, $fetchbody); - } - } - - // Check for redirects in the meta elements of the body if there are no redirects in the header. - if (!$fetchbody) { - return(self::finalUrl($url, ++$depth, true)); - } - - // if the file is too large then exit - if ($curl_info["download_content_length"] > 1000000) { - return $url; - } - - // if it isn't a HTML file then exit - if (!empty($curl_info["content_type"]) && !strstr(strtolower($curl_info["content_type"]), "html")) { - return $url; - } - - $stamp1 = microtime(true); - - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_HEADER, 0); - curl_setopt($ch, CURLOPT_NOBODY, 0); - curl_setopt($ch, CURLOPT_TIMEOUT, 10); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_USERAGENT, DI::httpRequest()->getUserAgent()); - - $body = curl_exec($ch); - curl_close($ch); - - DI::profiler()->saveTimestamp($stamp1, "network", System::callstack()); - - if (trim($body) == "") { - return $url; - } - - // Check for redirect in meta elements - $doc = new DOMDocument(); - @$doc->loadHTML($body); - - $xpath = new DomXPath($doc); - - $list = $xpath->query("//meta[@content]"); - foreach ($list as $node) { - $attr = []; - if ($node->attributes->length) { - foreach ($node->attributes as $attribute) { - $attr[$attribute->name] = $attribute->value; - } - } - - if (@$attr["http-equiv"] == 'refresh') { - $path = $attr["content"]; - $pathinfo = explode(";", $path); - foreach ($pathinfo as $value) { - if (substr(strtolower($value), 0, 4) == "url=") { - return self::finalUrl(substr($value, 4), ++$depth); - } - } - } - } - - return $url; - } - /** * Find the matching part between two url * From e5649d6dbf32c2cad6cfb3b364161270b0fb8f03 Mon Sep 17 00:00:00 2001 From: nupplaPhil Date: Wed, 4 Mar 2020 22:33:31 +0100 Subject: [PATCH 10/44] Move "HTTPRequest::finalUrl" dynamic --- src/Network/HTTPRequest.php | 34 ++++++++++++++++------------------ src/Protocol/Feed.php | 2 +- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/Network/HTTPRequest.php b/src/Network/HTTPRequest.php index 08ac203f5..3e5091e07 100644 --- a/src/Network/HTTPRequest.php +++ b/src/Network/HTTPRequest.php @@ -25,9 +25,7 @@ use DOMDocument; use DomXPath; use Friendica\App; use Friendica\Core\Config\IConfig; -use Friendica\Core\Logger; use Friendica\Core\System; -use Friendica\DI; use Friendica\Util\Network; use Friendica\Util\Profiler; use Psr\Log\LoggerInterface; @@ -130,7 +128,7 @@ class HTTPRequest } @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - @curl_setopt($ch, CURLOPT_USERAGENT, $this->userAgent); + @curl_setopt($ch, CURLOPT_USERAGENT, $this->getUserAgent()); $range = intval($this->config->get('system', 'curl_range_bytes', 0)); @@ -208,7 +206,7 @@ class HTTPRequest $redirects++; $this->logger->notice('Curl redirect.', ['url' => $url, 'to' => $curlResponse->getRedirectUrl()]); @curl_close($ch); - return self::curl($curlResponse->getRedirectUrl(), $binary, $opts, $redirects); + return $this->curl($curlResponse->getRedirectUrl(), $binary, $opts, $redirects); } @curl_close($ch); @@ -251,7 +249,7 @@ class HTTPRequest curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $params); - curl_setopt($ch, CURLOPT_USERAGENT, $this->userAgent); + curl_setopt($ch, CURLOPT_USERAGENT, $this->getUserAgent()); if ($this->config->get('system', 'ipv4_resolve', false)) { curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); @@ -299,7 +297,7 @@ class HTTPRequest $redirects++; $this->logger->info('Post redirect.', ['url' => $url, 'to' => $curlResponse->getRedirectUrl()]); curl_close($ch); - return self::post($curlResponse->getRedirectUrl(), $params, $headers, $redirects, $timeout); + return $this->post($curlResponse->getRedirectUrl(), $params, $headers, $redirects, $timeout); } curl_close($ch); @@ -317,11 +315,11 @@ class HTTPRequest array_push($headers, 'Expect:'); } } - Logger::info('Server responds with 417, applying workaround', ['url' => $url]); - return self::post($url, $params, $headers, $redirects, $timeout); + $this->logger->info('Server responds with 417, applying workaround', ['url' => $url]); + return $this->post($url, $params, $headers, $redirects, $timeout); } - Logger::log('post_url: end ' . $url, Logger::DATA); + $this->logger->debug('Post_url: End.', ['url' => $url]); return $curlResponse; } @@ -342,7 +340,7 @@ class HTTPRequest * @return string A canonical URL * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function finalUrl(string $url, int $depth = 1, bool $fetchbody = false) + public function finalUrl(string $url, int $depth = 1, bool $fetchbody = false) { $url = Network::stripTrackingQueryParams($url); @@ -360,14 +358,14 @@ class HTTPRequest curl_setopt($ch, CURLOPT_NOBODY, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_USERAGENT, DI::httpRequest()->getUserAgent()); + curl_setopt($ch, CURLOPT_USERAGENT, $this->getUserAgent()); curl_exec($ch); $curl_info = @curl_getinfo($ch); $http_code = $curl_info['http_code']; curl_close($ch); - DI::profiler()->saveTimestamp($stamp1, "network", System::callstack()); + $this->profiler->saveTimestamp($stamp1, "network", System::callstack()); if ($http_code == 0) { return $url; @@ -375,15 +373,15 @@ class HTTPRequest if (in_array($http_code, ['301', '302'])) { if (!empty($curl_info['redirect_url'])) { - return self::finalUrl($curl_info['redirect_url'], ++$depth, $fetchbody); + return $this->finalUrl($curl_info['redirect_url'], ++$depth, $fetchbody); } elseif (!empty($curl_info['location'])) { - return self::finalUrl($curl_info['location'], ++$depth, $fetchbody); + return $this->finalUrl($curl_info['location'], ++$depth, $fetchbody); } } // Check for redirects in the meta elements of the body if there are no redirects in the header. if (!$fetchbody) { - return self::finalUrl($url, ++$depth, true); + return $this->finalUrl($url, ++$depth, true); } // if the file is too large then exit @@ -404,12 +402,12 @@ class HTTPRequest curl_setopt($ch, CURLOPT_NOBODY, 0); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_USERAGENT, DI::httpRequest()->getUserAgent()); + curl_setopt($ch, CURLOPT_USERAGENT, $this->getUserAgent()); $body = curl_exec($ch); curl_close($ch); - DI::profiler()->saveTimestamp($stamp1, "network", System::callstack()); + $this->profiler->saveTimestamp($stamp1, "network", System::callstack()); if (trim($body) == "") { return $url; @@ -435,7 +433,7 @@ class HTTPRequest $pathinfo = explode(";", $path); foreach ($pathinfo as $value) { if (substr(strtolower($value), 0, 4) == "url=") { - return self::finalUrl(substr($value, 4), ++$depth); + return $this->finalUrl(substr($value, 4), ++$depth); } } } diff --git a/src/Protocol/Feed.php b/src/Protocol/Feed.php index a609ae296..9aab4f52c 100644 --- a/src/Protocol/Feed.php +++ b/src/Protocol/Feed.php @@ -351,7 +351,7 @@ class Feed $orig_plink = $item["plink"]; - $item["plink"] = HTTPRequest::finalUrl($item["plink"]); + $item["plink"] = DI::httpRequest()->finalUrl($item["plink"]); $item["parent-uri"] = $item["uri"]; From 7029012f27e9f5199617ae1ec2c3aae10f0d1fc8 Mon Sep 17 00:00:00 2001 From: nupplaPhil Date: Wed, 4 Mar 2020 22:35:09 +0100 Subject: [PATCH 11/44] Rename "HTTPRequest::curl()" to HTTPRequest::get() --- mod/ostatus_subscribe.php | 2 +- mod/parse_url.php | 3 ++- mod/redir.php | 2 +- src/Content/Text/BBCode.php | 2 +- src/Core/Search.php | 2 +- src/Model/GContact.php | 6 +++--- src/Model/GServer.php | 36 ++++++++++++++++---------------- src/Model/Photo.php | 2 +- src/Model/Profile.php | 2 +- src/Model/User.php | 2 +- src/Module/Admin/Summary.php | 2 +- src/Module/Magic.php | 2 +- src/Network/HTTPRequest.php | 6 +++--- src/Network/Probe.php | 22 +++++++++---------- src/Protocol/ActivityPub.php | 3 ++- src/Protocol/DFRN.php | 2 +- src/Protocol/OStatus.php | 14 ++++++------- src/Protocol/PortableContact.php | 9 ++++---- src/Util/ExAuth.php | 2 +- src/Util/HTTPSignature.php | 2 +- src/Util/ParseUrl.php | 3 ++- src/Worker/OnePoll.php | 4 ++-- 22 files changed, 66 insertions(+), 64 deletions(-) diff --git a/mod/ostatus_subscribe.php b/mod/ostatus_subscribe.php index 5a3a625ce..459e9e2c9 100644 --- a/mod/ostatus_subscribe.php +++ b/mod/ostatus_subscribe.php @@ -54,7 +54,7 @@ function ostatus_subscribe_content(App $a) $api = $contact['baseurl'] . '/api/'; // Fetching friends - $curlResult = DI::httpRequest()->curl($api . 'statuses/friends.json?screen_name=' . $contact['nick']); + $curlResult = DI::httpRequest()->get($api . 'statuses/friends.json?screen_name=' . $contact['nick']); if (!$curlResult->isSuccess()) { DI::pConfig()->delete($uid, 'ostatus', 'legacy_contact'); diff --git a/mod/parse_url.php b/mod/parse_url.php index 0e80d971a..a1faab6ef 100644 --- a/mod/parse_url.php +++ b/mod/parse_url.php @@ -28,6 +28,7 @@ use Friendica\Content\PageInfo; use Friendica\Core\Hook; use Friendica\Core\Logger; use Friendica\Core\System; +use Friendica\DI; use Friendica\Util\ParseUrl; use Friendica\Util\Strings; @@ -84,7 +85,7 @@ function parse_url_content(App $a) // Check if the URL is an image, video or audio file. If so format // the URL with the corresponding BBCode media tag // Fetch the header of the URL - $curlResponse = DI::httpRequest()->curl($url, false, ['novalidate' => true, 'nobody' => true]); + $curlResponse = DI::httpRequest()->get($url, false, ['novalidate' => true, 'nobody' => true]); if ($curlResponse->isSuccess()) { // Convert the header fields into an array diff --git a/mod/redir.php b/mod/redir.php index 4069518cd..b2f76738b 100644 --- a/mod/redir.php +++ b/mod/redir.php @@ -170,7 +170,7 @@ function redir_magic($a, $cid, $url) } // Test for magic auth on the target system - $serverret = DI::httpRequest()->curl($basepath . '/magic'); + $serverret = DI::httpRequest()->get($basepath . '/magic'); if ($serverret->isSuccess()) { $separator = strpos($target_url, '?') ? '&' : '?'; $target_url .= $separator . 'zrl=' . urlencode($visitor) . '&addr=' . urlencode($contact_url); diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index 036bc4a27..bf208a103 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -485,7 +485,7 @@ class BBCode continue; } - $curlResult = DI::httpRequest()->curl($mtch[1], true); + $curlResult = DI::httpRequest()->get($mtch[1], true); if (!$curlResult->isSuccess()) { continue; } diff --git a/src/Core/Search.php b/src/Core/Search.php index 768a0bf27..c5c6ca08c 100644 --- a/src/Core/Search.php +++ b/src/Core/Search.php @@ -284,7 +284,7 @@ class Search $return = GContact::searchByName($search, $mode); } else { $p = $page > 1 ? 'p=' . $page : ''; - $curlResult = DI::httpRequest()->curl(self::getGlobalDirectory() . '/search/people?' . $p . '&q=' . urlencode($search), false, ['accept_content' => 'application/json']); + $curlResult = DI::httpRequest()->get(self::getGlobalDirectory() . '/search/people?' . $p . '&q=' . urlencode($search), false, ['accept_content' => 'application/json']); if ($curlResult->isSuccess()) { $searchResult = json_decode($curlResult->getBody(), true); if (!empty($searchResult['profiles'])) { diff --git a/src/Model/GContact.php b/src/Model/GContact.php index dabc907f8..109f5d54b 100644 --- a/src/Model/GContact.php +++ b/src/Model/GContact.php @@ -844,7 +844,7 @@ class GContact return false; } - $curlResult = DI::httpRequest()->curl($gserver['noscrape'] . '/' . $data['nick']); + $curlResult = DI::httpRequest()->get($gserver['noscrape'] . '/' . $data['nick']); if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { $noscrape = json_decode($curlResult->getBody(), true); @@ -926,7 +926,7 @@ class GContact private static function updateFromFeed(array $data) { // Search for the newest entry in the feed - $curlResult = DI::httpRequest()->curl($data['poll']); + $curlResult = DI::httpRequest()->get($data['poll']); if (!$curlResult->isSuccess()) { $fields = ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()]; DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); @@ -1204,7 +1204,7 @@ class GContact $url = $server . '/main/statistics'; - $curlResult = DI::httpRequest()->curl($url); + $curlResult = DI::httpRequest()->get($url); if (!$curlResult->isSuccess()) { return false; } diff --git a/src/Model/GServer.php b/src/Model/GServer.php index 76082fe1f..ae4332511 100644 --- a/src/Model/GServer.php +++ b/src/Model/GServer.php @@ -309,7 +309,7 @@ class GServer // When a nodeinfo is present, we don't need to dig further $xrd_timeout = DI::config()->get('system', 'xrd_timeout'); - $curlResult = DI::httpRequest()->curl($url . '/.well-known/nodeinfo', false, ['timeout' => $xrd_timeout]); + $curlResult = DI::httpRequest()->get($url . '/.well-known/nodeinfo', false, ['timeout' => $xrd_timeout]); if ($curlResult->isTimeout()) { self::setFailure($url); return false; @@ -342,7 +342,7 @@ class GServer $basedata = ['detection-method' => self::DETECT_MANUAL]; } - $curlResult = DI::httpRequest()->curl($baseurl, false, ['timeout' => $xrd_timeout]); + $curlResult = DI::httpRequest()->get($baseurl, false, ['timeout' => $xrd_timeout]); if ($curlResult->isSuccess()) { $basedata = self::analyseRootHeader($curlResult, $basedata); $basedata = self::analyseRootBody($curlResult, $basedata, $baseurl); @@ -498,7 +498,7 @@ class GServer { Logger::info('Discover relay data', ['server' => $server_url]); - $curlResult = DI::httpRequest()->curl($server_url . '/.well-known/x-social-relay'); + $curlResult = DI::httpRequest()->get($server_url . '/.well-known/x-social-relay'); if (!$curlResult->isSuccess()) { return; } @@ -579,7 +579,7 @@ class GServer */ private static function fetchStatistics(string $url) { - $curlResult = DI::httpRequest()->curl($url . '/statistics.json'); + $curlResult = DI::httpRequest()->get($url . '/statistics.json'); if (!$curlResult->isSuccess()) { return []; } @@ -689,7 +689,7 @@ class GServer */ private static function parseNodeinfo1(string $nodeinfo_url) { - $curlResult = DI::httpRequest()->curl($nodeinfo_url); + $curlResult = DI::httpRequest()->get($nodeinfo_url); if (!$curlResult->isSuccess()) { return []; @@ -766,7 +766,7 @@ class GServer */ private static function parseNodeinfo2(string $nodeinfo_url) { - $curlResult = DI::httpRequest()->curl($nodeinfo_url); + $curlResult = DI::httpRequest()->get($nodeinfo_url); if (!$curlResult->isSuccess()) { return []; } @@ -843,7 +843,7 @@ class GServer */ private static function fetchSiteinfo(string $url, array $serverdata) { - $curlResult = DI::httpRequest()->curl($url . '/siteinfo.json'); + $curlResult = DI::httpRequest()->get($url . '/siteinfo.json'); if (!$curlResult->isSuccess()) { return $serverdata; } @@ -912,7 +912,7 @@ class GServer private static function validHostMeta(string $url) { $xrd_timeout = DI::config()->get('system', 'xrd_timeout'); - $curlResult = DI::httpRequest()->curl($url . '/.well-known/host-meta', false, ['timeout' => $xrd_timeout]); + $curlResult = DI::httpRequest()->get($url . '/.well-known/host-meta', false, ['timeout' => $xrd_timeout]); if (!$curlResult->isSuccess()) { return false; } @@ -1008,7 +1008,7 @@ class GServer { $serverdata['poco'] = ''; - $curlResult = DI::httpRequest()->curl($url . '/poco'); + $curlResult = DI::httpRequest()->get($url . '/poco'); if (!$curlResult->isSuccess()) { return $serverdata; } @@ -1038,7 +1038,7 @@ class GServer */ public static function checkMastodonDirectory(string $url, array $serverdata) { - $curlResult = DI::httpRequest()->curl($url . '/api/v1/directory?limit=1'); + $curlResult = DI::httpRequest()->get($url . '/api/v1/directory?limit=1'); if (!$curlResult->isSuccess()) { return $serverdata; } @@ -1065,7 +1065,7 @@ class GServer */ private static function detectNextcloud(string $url, array $serverdata) { - $curlResult = DI::httpRequest()->curl($url . '/status.php'); + $curlResult = DI::httpRequest()->get($url . '/status.php'); if (!$curlResult->isSuccess() || ($curlResult->getBody() == '')) { return $serverdata; @@ -1099,7 +1099,7 @@ class GServer */ private static function detectMastodonAlikes(string $url, array $serverdata) { - $curlResult = DI::httpRequest()->curl($url . '/api/v1/instance'); + $curlResult = DI::httpRequest()->get($url . '/api/v1/instance'); if (!$curlResult->isSuccess() || ($curlResult->getBody() == '')) { return $serverdata; @@ -1165,7 +1165,7 @@ class GServer */ private static function detectHubzilla(string $url, array $serverdata) { - $curlResult = DI::httpRequest()->curl($url . '/api/statusnet/config.json'); + $curlResult = DI::httpRequest()->get($url . '/api/statusnet/config.json'); if (!$curlResult->isSuccess() || ($curlResult->getBody() == '')) { return $serverdata; } @@ -1263,7 +1263,7 @@ class GServer private static function detectGNUSocial(string $url, array $serverdata) { // Test for GNU Social - $curlResult = DI::httpRequest()->curl($url . '/api/gnusocial/version.json'); + $curlResult = DI::httpRequest()->get($url . '/api/gnusocial/version.json'); if ($curlResult->isSuccess() && ($curlResult->getBody() != '{"error":"not implemented"}') && ($curlResult->getBody() != '') && (strlen($curlResult->getBody()) < 30)) { $serverdata['platform'] = 'gnusocial'; @@ -1281,7 +1281,7 @@ class GServer } // Test for Statusnet - $curlResult = DI::httpRequest()->curl($url . '/api/statusnet/version.json'); + $curlResult = DI::httpRequest()->get($url . '/api/statusnet/version.json'); if ($curlResult->isSuccess() && ($curlResult->getBody() != '{"error":"not implemented"}') && ($curlResult->getBody() != '') && (strlen($curlResult->getBody()) < 30)) { @@ -1317,9 +1317,9 @@ class GServer */ private static function detectFriendica(string $url, array $serverdata) { - $curlResult = DI::httpRequest()->curl($url . '/friendica/json'); + $curlResult = DI::httpRequest()->get($url . '/friendica/json'); if (!$curlResult->isSuccess()) { - $curlResult = DI::httpRequest()->curl($url . '/friendika/json'); + $curlResult = DI::httpRequest()->get($url . '/friendika/json'); $friendika = true; $platform = 'Friendika'; } else { @@ -1652,7 +1652,7 @@ class GServer if (!empty($accesstoken)) { $api = 'https://instances.social/api/1.0/instances/list?count=0'; $header = ['Authorization: Bearer '.$accesstoken]; - $curlResult = DI::httpRequest()->curl($api, false, ['headers' => $header]); + $curlResult = DI::httpRequest()->get($api, false, ['headers' => $header]); if ($curlResult->isSuccess()) { $servers = json_decode($curlResult->getBody(), true); diff --git a/src/Model/Photo.php b/src/Model/Photo.php index a10711db2..7d984a8ce 100644 --- a/src/Model/Photo.php +++ b/src/Model/Photo.php @@ -420,7 +420,7 @@ class Photo $filename = basename($image_url); if (!empty($image_url)) { - $ret = DI::httpRequest()->curl($image_url, true); + $ret = DI::httpRequest()->get($image_url, true); $img_str = $ret->getBody(); $type = $ret->getContentType(); } else { diff --git a/src/Model/Profile.php b/src/Model/Profile.php index d32940ae6..0cff8ba28 100644 --- a/src/Model/Profile.php +++ b/src/Model/Profile.php @@ -737,7 +737,7 @@ class Profile $magic_path = $basepath . '/magic' . '?owa=1&dest=' . $dest . '&' . $addr_request; // We have to check if the remote server does understand /magic without invoking something - $serverret = DI::httpRequest()->curl($basepath . '/magic'); + $serverret = DI::httpRequest()->get($basepath . '/magic'); if ($serverret->isSuccess()) { Logger::log('Doing magic auth for visitor ' . $my_url . ' to ' . $magic_path, Logger::DEBUG); System::externalRedirect($magic_path); diff --git a/src/Model/User.php b/src/Model/User.php index 9980c48d5..78ae95804 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -823,7 +823,7 @@ class User $photo_failure = false; $filename = basename($photo); - $curlResult = DI::httpRequest()->curl($photo, true); + $curlResult = DI::httpRequest()->get($photo, true); if ($curlResult->isSuccess()) { $img_str = $curlResult->getBody(); $type = $curlResult->getContentType(); diff --git a/src/Module/Admin/Summary.php b/src/Module/Admin/Summary.php index ad84bb6ab..a130c4839 100644 --- a/src/Module/Admin/Summary.php +++ b/src/Module/Admin/Summary.php @@ -246,7 +246,7 @@ class Summary extends BaseAdmin private static function checkSelfHostMeta() { // Fetch the host-meta to check if this really is a vital server - return DI::httpRequest()->curl(DI::baseUrl()->get() . '/.well-known/host-meta')->isSuccess(); + return DI::httpRequest()->get(DI::baseUrl()->get() . '/.well-known/host-meta')->isSuccess(); } } diff --git a/src/Module/Magic.php b/src/Module/Magic.php index a025617d8..95b742bb3 100644 --- a/src/Module/Magic.php +++ b/src/Module/Magic.php @@ -100,7 +100,7 @@ class Magic extends BaseModule ); // Try to get an authentication token from the other instance. - $curlResult = DI::httpRequest()->curl($basepath . '/owa', false, ['headers' => $headers]); + $curlResult = DI::httpRequest()->get($basepath . '/owa', false, ['headers' => $headers]); if ($curlResult->isSuccess()) { $j = json_decode($curlResult->getBody(), true); diff --git a/src/Network/HTTPRequest.php b/src/Network/HTTPRequest.php index 3e5091e07..f9279fa60 100644 --- a/src/Network/HTTPRequest.php +++ b/src/Network/HTTPRequest.php @@ -71,7 +71,7 @@ class HTTPRequest * @return CurlResult * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public function curl(string $url, bool $binary = false, array $opts = [], int &$redirects = 0) + public function get(string $url, bool $binary = false, array $opts = [], int &$redirects = 0) { $stamp1 = microtime(true); @@ -206,7 +206,7 @@ class HTTPRequest $redirects++; $this->logger->notice('Curl redirect.', ['url' => $url, 'to' => $curlResponse->getRedirectUrl()]); @curl_close($ch); - return $this->curl($curlResponse->getRedirectUrl(), $binary, $opts, $redirects); + return $this->get($curlResponse->getRedirectUrl(), $binary, $opts, $redirects); } @curl_close($ch); @@ -486,7 +486,7 @@ class HTTPRequest */ public function fetchUrlFull(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = '', int &$redirects = 0) { - return $this->curl( + return $this->get( $url, $binary, [ diff --git a/src/Network/Probe.php b/src/Network/Probe.php index 01dc28408..c41006b12 100644 --- a/src/Network/Probe.php +++ b/src/Network/Probe.php @@ -166,7 +166,7 @@ class Probe Logger::info('Probing', ['host' => $host, 'ssl_url' => $ssl_url, 'url' => $url, 'callstack' => System::callstack(20)]); $xrd = null; - $curlResult = DI::httpRequest()->curl($ssl_url, false, ['timeout' => $xrd_timeout, 'accept_content' => 'application/xrd+xml']); + $curlResult = DI::httpRequest()->get($ssl_url, false, ['timeout' => $xrd_timeout, 'accept_content' => 'application/xrd+xml']); $ssl_connection_error = ($curlResult->getErrorNumber() == CURLE_COULDNT_CONNECT) || ($curlResult->getReturnCode() == 0); if ($curlResult->isSuccess()) { $xml = $curlResult->getBody(); @@ -183,7 +183,7 @@ class Probe } if (!is_object($xrd) && !empty($url)) { - $curlResult = DI::httpRequest()->curl($url, false, ['timeout' => $xrd_timeout, 'accept_content' => 'application/xrd+xml']); + $curlResult = DI::httpRequest()->get($url, false, ['timeout' => $xrd_timeout, 'accept_content' => 'application/xrd+xml']); $connection_error = ($curlResult->getErrorNumber() == CURLE_COULDNT_CONNECT) || ($curlResult->getReturnCode() == 0); if ($curlResult->isTimeout()) { Logger::info('Probing timeout', ['url' => $url]); @@ -427,7 +427,7 @@ class Probe */ private static function getHideStatus($url) { - $curlResult = DI::httpRequest()->curl($url); + $curlResult = DI::httpRequest()->get($url); if (!$curlResult->isSuccess()) { return false; } @@ -841,7 +841,7 @@ class Probe public static function pollZot($url, $data) { - $curlResult = DI::httpRequest()->curl($url); + $curlResult = DI::httpRequest()->get($url); if ($curlResult->isTimeout()) { return $data; } @@ -938,7 +938,7 @@ class Probe { $xrd_timeout = DI::config()->get('system', 'xrd_timeout', 20); - $curlResult = DI::httpRequest()->curl($url, false, ['timeout' => $xrd_timeout, 'accept_content' => $type]); + $curlResult = DI::httpRequest()->get($url, false, ['timeout' => $xrd_timeout, 'accept_content' => $type]); if ($curlResult->isTimeout()) { self::$istimeout = true; return []; @@ -1007,7 +1007,7 @@ class Probe */ private static function pollNoscrape($noscrape_url, $data) { - $curlResult = DI::httpRequest()->curl($noscrape_url); + $curlResult = DI::httpRequest()->get($noscrape_url); if ($curlResult->isTimeout()) { self::$istimeout = true; return []; @@ -1265,7 +1265,7 @@ class Probe */ private static function pollHcard($hcard_url, $data, $dfrn = false) { - $curlResult = DI::httpRequest()->curl($hcard_url); + $curlResult = DI::httpRequest()->get($hcard_url); if ($curlResult->isTimeout()) { self::$istimeout = true; return []; @@ -1519,7 +1519,7 @@ class Probe $pubkey = substr($pubkey, 5); } } elseif (Strings::normaliseLink($pubkey) == 'http://') { - $curlResult = DI::httpRequest()->curl($pubkey); + $curlResult = DI::httpRequest()->get($pubkey); if ($curlResult->isTimeout()) { self::$istimeout = true; return $short ? false : []; @@ -1552,7 +1552,7 @@ class Probe } // Fetch all additional data from the feed - $curlResult = DI::httpRequest()->curl($data["poll"]); + $curlResult = DI::httpRequest()->get($data["poll"]); if ($curlResult->isTimeout()) { self::$istimeout = true; return []; @@ -1604,7 +1604,7 @@ class Probe */ private static function pumpioProfileData($profile_link) { - $curlResult = DI::httpRequest()->curl($profile_link); + $curlResult = DI::httpRequest()->get($profile_link); if (!$curlResult->isSuccess()) { return []; } @@ -1835,7 +1835,7 @@ class Probe */ private static function feed($url, $probe = true) { - $curlResult = DI::httpRequest()->curl($url); + $curlResult = DI::httpRequest()->get($url); if ($curlResult->isTimeout()) { self::$istimeout = true; return []; diff --git a/src/Protocol/ActivityPub.php b/src/Protocol/ActivityPub.php index f41708011..6b29eabce 100644 --- a/src/Protocol/ActivityPub.php +++ b/src/Protocol/ActivityPub.php @@ -22,6 +22,7 @@ namespace Friendica\Protocol; use Friendica\Core\Protocol; +use Friendica\DI; use Friendica\Model\APContact; use Friendica\Model\User; use Friendica\Util\HTTPSignature; @@ -92,7 +93,7 @@ class ActivityPub return HTTPSignature::fetch($url, $uid); } - $curlResult = DI::httpRequest()->curl($url, false, ['accept_content' => 'application/activity+json, application/ld+json']); + $curlResult = DI::httpRequest()->get($url, false, ['accept_content' => 'application/activity+json, application/ld+json']); if (!$curlResult->isSuccess() || empty($curlResult->getBody())) { return false; } diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index 7c87ee0b2..8190806a0 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -1194,7 +1194,7 @@ class DFRN Logger::log('dfrn_deliver: ' . $url); - $curlResult = DI::httpRequest()->curl($url); + $curlResult = DI::httpRequest()->get($url); if ($curlResult->isTimeout()) { return -2; // timed out diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index a9ee2277e..9c87f367a 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -755,7 +755,7 @@ class OStatus self::$conv_list[$conversation] = true; - $curlResult = DI::httpRequest()->curl($conversation, false, ['accept_content' => 'application/atom+xml, text/html']); + $curlResult = DI::httpRequest()->get($conversation, false, ['accept_content' => 'application/atom+xml, text/html']); if (!$curlResult->isSuccess()) { return; @@ -784,7 +784,7 @@ class OStatus } } if ($file != '') { - $conversation_atom = DI::httpRequest()->curl($attribute['href']); + $conversation_atom = DI::httpRequest()->get($attribute['href']); if ($conversation_atom->isSuccess()) { $xml = $conversation_atom->getBody(); @@ -901,7 +901,7 @@ class OStatus return; } - $curlResult = DI::httpRequest()->curl($self); + $curlResult = DI::httpRequest()->get($self); if (!$curlResult->isSuccess()) { return; @@ -948,7 +948,7 @@ class OStatus } $stored = false; - $curlResult = DI::httpRequest()->curl($related, false, ['accept_content' => 'application/atom+xml, text/html']); + $curlResult = DI::httpRequest()->get($related, false, ['accept_content' => 'application/atom+xml, text/html']); if (!$curlResult->isSuccess()) { return; @@ -979,7 +979,7 @@ class OStatus } } if ($atom_file != '') { - $curlResult = DI::httpRequest()->curl($atom_file); + $curlResult = DI::httpRequest()->get($atom_file); if ($curlResult->isSuccess()) { Logger::log('Fetched XML for URI ' . $related_uri, Logger::DEBUG); @@ -991,7 +991,7 @@ class OStatus // Workaround for older GNU Social servers if (($xml == '') && strstr($related, '/notice/')) { - $curlResult = DI::httpRequest()->curl(str_replace('/notice/', '/api/statuses/show/', $related) . '.atom'); + $curlResult = DI::httpRequest()->get(str_replace('/notice/', '/api/statuses/show/', $related) . '.atom'); if ($curlResult->isSuccess()) { Logger::log('GNU Social workaround to fetch XML for URI ' . $related_uri, Logger::DEBUG); @@ -1002,7 +1002,7 @@ class OStatus // Even more worse workaround for GNU Social ;-) if ($xml == '') { $related_guess = self::convertHref($related_uri); - $curlResult = DI::httpRequest()->curl(str_replace('/notice/', '/api/statuses/show/', $related_guess) . '.atom'); + $curlResult = DI::httpRequest()->get(str_replace('/notice/', '/api/statuses/show/', $related_guess) . '.atom'); if ($curlResult->isSuccess()) { Logger::log('GNU Social workaround 2 to fetch XML for URI ' . $related_uri, Logger::DEBUG); diff --git a/src/Protocol/PortableContact.php b/src/Protocol/PortableContact.php index f118ffa94..5216e3937 100644 --- a/src/Protocol/PortableContact.php +++ b/src/Protocol/PortableContact.php @@ -30,7 +30,6 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\GContact; use Friendica\Model\GServer; -use Friendica\Network\HTTPRequest; use Friendica\Util\DateTimeFormat; use Friendica\Util\Strings; @@ -251,7 +250,7 @@ class PortableContact */ private static function fetchServerlist($poco) { - $curlResult = DI::httpRequest()->curl($poco . "/@server"); + $curlResult = DI::httpRequest()->get($poco . "/@server"); if (!$curlResult->isSuccess()) { return; @@ -291,7 +290,7 @@ class PortableContact Logger::info("Fetch all users from the server " . $server["url"]); - $curlResult = DI::httpRequest()->curl($url); + $curlResult = DI::httpRequest()->get($url); if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { $data = json_decode($curlResult->getBody(), true); @@ -314,7 +313,7 @@ class PortableContact $success = false; - $curlResult = DI::httpRequest()->curl($url); + $curlResult = DI::httpRequest()->get($url); if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { Logger::info("Fetch all global contacts from the server " . $server["nurl"]); @@ -372,7 +371,7 @@ class PortableContact // Fetch all contacts from a given user from the other server $url = $server['poco'] . '/' . $username . '/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,contactType,generation'; - $curlResult = DI::httpRequest()->curl($url); + $curlResult = DI::httpRequest()->get($url); if ($curlResult->isSuccess()) { $data = json_decode($curlResult->getBody(), true); diff --git a/src/Util/ExAuth.php b/src/Util/ExAuth.php index 593386082..25eb3cc62 100644 --- a/src/Util/ExAuth.php +++ b/src/Util/ExAuth.php @@ -181,7 +181,7 @@ class ExAuth $url = ($ssl ? 'https' : 'http') . '://' . $host . '/noscrape/' . $user; - $curlResult = DI::httpRequest()->curl($url); + $curlResult = DI::httpRequest()->get($url); if (!$curlResult->isSuccess()) { return false; diff --git a/src/Util/HTTPSignature.php b/src/Util/HTTPSignature.php index 84a11b8ec..89da59ba2 100644 --- a/src/Util/HTTPSignature.php +++ b/src/Util/HTTPSignature.php @@ -442,7 +442,7 @@ class HTTPSignature $curl_opts = $opts; $curl_opts['header'] = $headers; - $curlResult = DI::httpRequest()->curl($request, false, $curl_opts); + $curlResult = DI::httpRequest()->get($request, false, $curl_opts); $return_code = $curlResult->getReturnCode(); Logger::log('Fetched for user ' . $uid . ' from ' . $request . ' returned ' . $return_code, Logger::DEBUG); diff --git a/src/Util/ParseUrl.php b/src/Util/ParseUrl.php index cf38ffd7b..01ad79d4f 100644 --- a/src/Util/ParseUrl.php +++ b/src/Util/ParseUrl.php @@ -27,6 +27,7 @@ use Friendica\Content\OEmbed; use Friendica\Core\Hook; use Friendica\Core\Logger; use Friendica\Database\DBA; +use Friendica\DI; /** * Get information about a given URL @@ -159,7 +160,7 @@ class ParseUrl return $siteinfo; } - $curlResult = DI::httpRequest()->curl($url); + $curlResult = DI::httpRequest()->get($url); if (!$curlResult->isSuccess()) { return $siteinfo; } diff --git a/src/Worker/OnePoll.php b/src/Worker/OnePoll.php index ed6bfacb3..fbd1ab4e5 100644 --- a/src/Worker/OnePoll.php +++ b/src/Worker/OnePoll.php @@ -290,7 +290,7 @@ class OnePoll . '&type=data&last_update=' . $last_update . '&perm=' . $perm; - $curlResult = DI::httpRequest()->curl($url); + $curlResult = DI::httpRequest()->get($url); if (!$curlResult->isSuccess() && ($curlResult->getErrorNumber() == CURLE_OPERATION_TIMEDOUT)) { // set the last-update so we don't keep polling @@ -443,7 +443,7 @@ class OnePoll } $cookiejar = tempnam(get_temppath(), 'cookiejar-onepoll-'); - $curlResult = DI::httpRequest()->curl($contact['poll'], false, ['cookiejar' => $cookiejar]); + $curlResult = DI::httpRequest()->get($contact['poll'], false, ['cookiejar' => $cookiejar]); unlink($cookiejar); if ($curlResult->isTimeout()) { From 657d08f09f94f53f2fb7515de73c3687ad71c0d2 Mon Sep 17 00:00:00 2001 From: nupplaPhil Date: Wed, 4 Mar 2020 22:35:40 +0100 Subject: [PATCH 12/44] Rename "fetchUrl" and "fetchUrlFull" to "fetch" and "fetchFull" --- mod/dfrn_poll.php | 14 +++++++------- mod/dfrn_request.php | 2 +- mod/oexchange.php | 2 +- mod/pubsubhubbub.php | 3 +-- src/Content/OEmbed.php | 6 +++--- src/Content/Text/BBCode.php | 4 ++-- src/Core/Installer.php | 5 ++--- src/Core/Search.php | 2 +- src/Core/Worker.php | 2 +- src/Model/GContact.php | 2 +- src/Model/GServer.php | 2 +- src/Module/Debug/Feed.php | 2 +- src/Network/HTTPRequest.php | 6 +++--- src/Protocol/Diaspora.php | 2 +- src/Protocol/PortableContact.php | 2 +- src/Util/Images.php | 2 +- src/Worker/CheckVersion.php | 2 +- src/Worker/CronJobs.php | 2 +- src/Worker/Directory.php | 2 +- src/Worker/SearchDirectory.php | 2 +- 20 files changed, 32 insertions(+), 34 deletions(-) diff --git a/mod/dfrn_poll.php b/mod/dfrn_poll.php index 7f7fbe498..183f6022e 100644 --- a/mod/dfrn_poll.php +++ b/mod/dfrn_poll.php @@ -114,7 +114,7 @@ function dfrn_poll_init(App $a) ); if (DBA::isResult($r)) { - $s = DI::httpRequest()->fetchUrl($r[0]['poll'] . '?dfrn_id=' . $my_id . '&type=profile-check'); + $s = DI::httpRequest()->fetch($r[0]['poll'] . '?dfrn_id=' . $my_id . '&type=profile-check'); Logger::log("dfrn_poll: old profile returns " . $s, Logger::DATA); @@ -498,12 +498,12 @@ function dfrn_poll_content(App $a) // URL reply if ($dfrn_version < 2.2) { - $s = DI::httpRequest()->fetchUrl($r[0]['poll'] - . '?dfrn_id=' . $encrypted_id - . '&type=profile-check' - . '&dfrn_version=' . DFRN_PROTOCOL_VERSION - . '&challenge=' . $challenge - . '&sec=' . $sec + $s = DI::httpRequest()->fetch($r[0]['poll'] + . '?dfrn_id=' . $encrypted_id + . '&type=profile-check' + . '&dfrn_version=' . DFRN_PROTOCOL_VERSION + . '&challenge=' . $challenge + . '&sec=' . $sec ); } else { $s = DI::httpRequest()->post($r[0]['poll'], [ diff --git a/mod/dfrn_request.php b/mod/dfrn_request.php index 8c8557650..cb21a211f 100644 --- a/mod/dfrn_request.php +++ b/mod/dfrn_request.php @@ -203,7 +203,7 @@ function dfrn_request_post(App $a) } if (!empty($dfrn_request) && strlen($confirm_key)) { - DI::httpRequest()->fetchUrl($dfrn_request . '?confirm_key=' . $confirm_key); + DI::httpRequest()->fetch($dfrn_request . '?confirm_key=' . $confirm_key); } // (ignore reply, nothing we can do it failed) diff --git a/mod/oexchange.php b/mod/oexchange.php index 4746651c1..b8da9df7e 100644 --- a/mod/oexchange.php +++ b/mod/oexchange.php @@ -57,7 +57,7 @@ function oexchange_content(App $a) { $tags = ((!empty($_REQUEST['tags'])) ? '&tags=' . urlencode(Strings::escapeTags(trim($_REQUEST['tags']))) : ''); - $s = DI::httpRequest()->fetchUrl(DI::baseUrl() . '/parse_url?url=' . $url . $title . $description . $tags); + $s = DI::httpRequest()->fetch(DI::baseUrl() . '/parse_url?url=' . $url . $title . $description . $tags); if (!strlen($s)) { return; diff --git a/mod/pubsubhubbub.php b/mod/pubsubhubbub.php index 96f26838e..344543618 100644 --- a/mod/pubsubhubbub.php +++ b/mod/pubsubhubbub.php @@ -24,7 +24,6 @@ use Friendica\Core\Logger; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\PushSubscriber; -use Friendica\Network\HTTPRequest; use Friendica\Util\Strings; function post_var($name) { @@ -126,7 +125,7 @@ function pubsubhubbub_init(App $a) { $hub_callback = rtrim($hub_callback, ' ?&#'); $separator = parse_url($hub_callback, PHP_URL_QUERY) === null ? '?' : '&'; - $fetchResult = DI::httpRequest()->fetchUrlFull($hub_callback . $separator . $params); + $fetchResult = DI::httpRequest()->fetchFull($hub_callback . $separator . $params); $body = $fetchResult->getBody(); $ret = $fetchResult->getReturnCode(); diff --git a/src/Content/OEmbed.php b/src/Content/OEmbed.php index 592d25c10..30a113f46 100644 --- a/src/Content/OEmbed.php +++ b/src/Content/OEmbed.php @@ -95,7 +95,7 @@ class OEmbed if (!in_array($ext, $noexts)) { // try oembed autodiscovery - $html_text = DI::httpRequest()->fetchUrl($embedurl, false, 15, 'text/*'); + $html_text = DI::httpRequest()->fetch($embedurl, false, 15, 'text/*'); if ($html_text) { $dom = @DOMDocument::loadHTML($html_text); if ($dom) { @@ -103,14 +103,14 @@ class OEmbed $entries = $xpath->query("//link[@type='application/json+oembed']"); foreach ($entries as $e) { $href = $e->getAttributeNode('href')->nodeValue; - $json_string = DI::httpRequest()->fetchUrl($href . '&maxwidth=' . $a->videowidth); + $json_string = DI::httpRequest()->fetch($href . '&maxwidth=' . $a->videowidth); break; } $entries = $xpath->query("//link[@type='text/json+oembed']"); foreach ($entries as $e) { $href = $e->getAttributeNode('href')->nodeValue; - $json_string = DI::httpRequest()->fetchUrl($href . '&maxwidth=' . $a->videowidth); + $json_string = DI::httpRequest()->fetch($href . '&maxwidth=' . $a->videowidth); break; } } diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index bf208a103..1181c8f47 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -1106,7 +1106,7 @@ class BBCode $text = "[url=" . $match[2] . ']' . $match[2] . "[/url]"; // if its not a picture then look if its a page that contains a picture link - $body = DI::httpRequest()->fetchUrl($match[1]); + $body = DI::httpRequest()->fetch($match[1]); $doc = new DOMDocument(); @$doc->loadHTML($body); @@ -1185,7 +1185,7 @@ class BBCode } // if its not a picture then look if its a page that contains a picture link - $body = DI::httpRequest()->fetchUrl($match[1]); + $body = DI::httpRequest()->fetch($match[1]); $doc = new DOMDocument(); @$doc->loadHTML($body); diff --git a/src/Core/Installer.php b/src/Core/Installer.php index af7c7aa49..28db93d29 100644 --- a/src/Core/Installer.php +++ b/src/Core/Installer.php @@ -27,7 +27,6 @@ use Friendica\Core\Config\Cache; use Friendica\Database\Database; use Friendica\Database\DBStructure; use Friendica\DI; -use Friendica\Network\HTTPRequest; use Friendica\Util\Images; use Friendica\Util\Strings; @@ -548,11 +547,11 @@ class Installer $help = ""; $error_msg = ""; if (function_exists('curl_init')) { - $fetchResult = DI::httpRequest()->fetchUrlFull($baseurl . "/install/testrewrite"); + $fetchResult = DI::httpRequest()->fetchFull($baseurl . "/install/testrewrite"); $url = Strings::normaliseLink($baseurl . "/install/testrewrite"); if ($fetchResult->getReturnCode() != 204) { - $fetchResult = DI::httpRequest()->fetchUrlFull($url); + $fetchResult = DI::httpRequest()->fetchFull($url); } if ($fetchResult->getReturnCode() != 204) { diff --git a/src/Core/Search.php b/src/Core/Search.php index c5c6ca08c..577b11266 100644 --- a/src/Core/Search.php +++ b/src/Core/Search.php @@ -123,7 +123,7 @@ class Search $searchUrl .= '&page=' . $page; } - $resultJson = DI::httpRequest()->fetchUrl($searchUrl, false, 0, 'application/json'); + $resultJson = DI::httpRequest()->fetch($searchUrl, false, 0, 'application/json'); $results = json_decode($resultJson, true); diff --git a/src/Core/Worker.php b/src/Core/Worker.php index 83a24c38f..937dd0a56 100644 --- a/src/Core/Worker.php +++ b/src/Core/Worker.php @@ -996,7 +996,7 @@ class Worker } $url = DI::baseUrl() . '/worker'; - DI::httpRequest()->fetchUrl($url, false, 1); + DI::httpRequest()->fetch($url, false, 1); } /** diff --git a/src/Model/GContact.php b/src/Model/GContact.php index 109f5d54b..ab0a4fdd8 100644 --- a/src/Model/GContact.php +++ b/src/Model/GContact.php @@ -536,7 +536,7 @@ class GContact $done[] = DI::baseUrl() . '/poco'; if (strlen(DI::config()->get('system', 'directory'))) { - $x = DI::httpRequest()->fetchUrl(Search::getGlobalDirectory() . '/pubsites'); + $x = DI::httpRequest()->fetch(Search::getGlobalDirectory() . '/pubsites'); if (!empty($x)) { $j = json_decode($x); if (!empty($j->entries)) { diff --git a/src/Model/GServer.php b/src/Model/GServer.php index ae4332511..7643c9590 100644 --- a/src/Model/GServer.php +++ b/src/Model/GServer.php @@ -1634,7 +1634,7 @@ class GServer $protocols = ['activitypub', 'diaspora', 'dfrn', 'ostatus']; foreach ($protocols as $protocol) { $query = '{nodes(protocol:"' . $protocol . '"){host}}'; - $curlResult = DI::httpRequest()->fetchUrl('https://the-federation.info/graphql?query=' . urlencode($query)); + $curlResult = DI::httpRequest()->fetch('https://the-federation.info/graphql?query=' . urlencode($query)); if (!empty($curlResult)) { $data = json_decode($curlResult, true); if (!empty($data['data']['nodes'])) { diff --git a/src/Module/Debug/Feed.php b/src/Module/Debug/Feed.php index deeb8d7ec..1da0457c4 100644 --- a/src/Module/Debug/Feed.php +++ b/src/Module/Debug/Feed.php @@ -48,7 +48,7 @@ class Feed extends BaseModule $contact = Model\Contact::getByURLForUser($url, local_user(), false); - $xml = DI::httpRequest()->fetchUrl($contact['poll']); + $xml = DI::httpRequest()->fetch($contact['poll']); $import_result = Protocol\Feed::import($xml); diff --git a/src/Network/HTTPRequest.php b/src/Network/HTTPRequest.php index f9279fa60..c751406c1 100644 --- a/src/Network/HTTPRequest.php +++ b/src/Network/HTTPRequest.php @@ -460,9 +460,9 @@ class HTTPRequest * @return string The fetched content * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public function fetchUrl(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = '', int &$redirects = 0) + public function fetch(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = '', int &$redirects = 0) { - $ret = $this->fetchUrlFull($url, $binary, $timeout, $accept_content, $cookiejar, $redirects); + $ret = $this->fetchFull($url, $binary, $timeout, $accept_content, $cookiejar, $redirects); return $ret->getBody(); } @@ -484,7 +484,7 @@ class HTTPRequest * @return CurlResult With all relevant information, 'body' contains the actual fetched content. * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public function fetchUrlFull(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = '', int &$redirects = 0) + public function fetchFull(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = '', int &$redirects = 0) { return $this->get( $url, diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index 5e1f09677..98a315ce2 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -1379,7 +1379,7 @@ class Diaspora Logger::log("Fetch post from ".$source_url, Logger::DEBUG); - $envelope = DI::httpRequest()->fetchUrl($source_url); + $envelope = DI::httpRequest()->fetch($source_url); if ($envelope) { Logger::log("Envelope was fetched.", Logger::DEBUG); $x = self::verifyMagicEnvelope($envelope); diff --git a/src/Protocol/PortableContact.php b/src/Protocol/PortableContact.php index 5216e3937..cfc140d66 100644 --- a/src/Protocol/PortableContact.php +++ b/src/Protocol/PortableContact.php @@ -102,7 +102,7 @@ class PortableContact Logger::log('load: ' . $url, Logger::DEBUG); - $fetchresult = DI::httpRequest()->fetchUrlFull($url); + $fetchresult = DI::httpRequest()->fetchFull($url); $s = $fetchresult->getBody(); Logger::log('load: returns ' . $s, Logger::DATA); diff --git a/src/Util/Images.php b/src/Util/Images.php index 2d161a5c4..ef171873f 100644 --- a/src/Util/Images.php +++ b/src/Util/Images.php @@ -184,7 +184,7 @@ class Images return $data; } - $img_str = DI::httpRequest()->fetchUrl($url, true, 4); + $img_str = DI::httpRequest()->fetch($url, true, 4); if (!$img_str) { return []; diff --git a/src/Worker/CheckVersion.php b/src/Worker/CheckVersion.php index 260d6b16f..be325461b 100644 --- a/src/Worker/CheckVersion.php +++ b/src/Worker/CheckVersion.php @@ -54,7 +54,7 @@ class CheckVersion Logger::log("Checking VERSION from: ".$checked_url, Logger::DEBUG); // fetch the VERSION file - $gitversion = DBA::escape(trim(DI::httpRequest()->fetchUrl($checked_url))); + $gitversion = DBA::escape(trim(DI::httpRequest()->fetch($checked_url))); Logger::log("Upstream VERSION is: ".$gitversion, Logger::DEBUG); DI::config()->set('system', 'git_friendica_version', $gitversion); diff --git a/src/Worker/CronJobs.php b/src/Worker/CronJobs.php index 1e4505856..4f988b6e1 100644 --- a/src/Worker/CronJobs.php +++ b/src/Worker/CronJobs.php @@ -60,7 +60,7 @@ class CronJobs // Now trying to register $url = 'http://the-federation.info/register/' . DI::baseUrl()->getHostname(); Logger::debug('Check registering url', ['url' => $url]); - $ret = DI::httpRequest()->fetchUrl($url); + $ret = DI::httpRequest()->fetch($url); Logger::debug('Check registering answer', ['answer' => $ret]); Logger::info('cron_end'); break; diff --git a/src/Worker/Directory.php b/src/Worker/Directory.php index 2cab09f33..d71e593dc 100644 --- a/src/Worker/Directory.php +++ b/src/Worker/Directory.php @@ -53,7 +53,7 @@ class Directory Logger::log('Updating directory: ' . $arr['url'], Logger::DEBUG); if (strlen($arr['url'])) { - DI::httpRequest()->fetchUrl($dir . '?url=' . bin2hex($arr['url'])); + DI::httpRequest()->fetch($dir . '?url=' . bin2hex($arr['url'])); } return; diff --git a/src/Worker/SearchDirectory.php b/src/Worker/SearchDirectory.php index c3c344d93..546c369b2 100644 --- a/src/Worker/SearchDirectory.php +++ b/src/Worker/SearchDirectory.php @@ -51,7 +51,7 @@ class SearchDirectory } } - $x = DI::httpRequest()->fetchUrl(Search::getGlobalDirectory() . '/lsearch?p=1&n=500&search=' . urlencode($search)); + $x = DI::httpRequest()->fetch(Search::getGlobalDirectory() . '/lsearch?p=1&n=500&search=' . urlencode($search)); $j = json_decode($x); if (!empty($j->results)) { From 0a421064a56492e15b5ed647ee19c26960d046f3 Mon Sep 17 00:00:00 2001 From: nupplaPhil Date: Wed, 4 Mar 2020 22:56:16 +0100 Subject: [PATCH 13/44] Introduce interface "IHTTPRequest" (rely on abstractions, not on concrete implementations) --- src/DI.php | 4 +- src/Network/HTTPRequest.php | 75 +++------------------ src/Network/IHTTPRequest.php | 119 +++++++++++++++++++++++++++++++++ static/dependencies.config.php | 4 ++ 4 files changed, 136 insertions(+), 66 deletions(-) create mode 100644 src/Network/IHTTPRequest.php diff --git a/src/DI.php b/src/DI.php index 5986ca961..39e892adc 100644 --- a/src/DI.php +++ b/src/DI.php @@ -328,11 +328,11 @@ abstract class DI // /** - * @return Network\HTTPRequest + * @return Network\IHTTPRequest */ public static function httpRequest() { - return self::$dice->create(Network\HTTPRequest::class); + return self::$dice->create(Network\IHTTPRequest::class); } // diff --git a/src/Network/HTTPRequest.php b/src/Network/HTTPRequest.php index c751406c1..839586880 100644 --- a/src/Network/HTTPRequest.php +++ b/src/Network/HTTPRequest.php @@ -33,7 +33,7 @@ use Psr\Log\LoggerInterface; /** * Performs HTTP requests to a given URL */ -class HTTPRequest +class HTTPRequest implements IHTTPRequest { /** @var LoggerInterface */ private $logger; @@ -53,22 +53,10 @@ class HTTPRequest } /** - * fetches an URL. + * {@inheritDoc} * - * @param string $url URL to fetch - * @param bool $binary default false - * TRUE if asked to return binary results (file download) - * @param array $opts (optional parameters) assoziative array with: - * 'accept_content' => supply Accept: header with 'accept_content' as the value - * 'timeout' => int Timeout in seconds, default system config value or 60 seconds - * 'http_auth' => username:password - * 'novalidate' => do not validate SSL certs, default is to validate using our CA list - * 'nobody' => only return the header - * 'cookiejar' => path to cookie jar file - * 'header' => header array - * @param int $redirects The recursion counter for internal use - default 0 + * @param int $redirects The recursion counter for internal use - default 0 * - * @return CurlResult * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ public function get(string $url, bool $binary = false, array $opts = [], int &$redirects = 0) @@ -217,15 +205,10 @@ class HTTPRequest } /** - * Send POST request to $url + * {@inheritDoc} * - * @param string $url URL to post - * @param mixed $params array of POST variables - * @param array $headers HTTP headers - * @param int $redirects Recursion counter for internal use - default = 0 - * @param int $timeout The timeout in seconds, default system config value or 60 seconds + * @param int $redirects The recursion counter for internal use - default 0 * - * @return CurlResult The content * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ public function post(string $url, $params, array $headers = [], int $timeout = 0, int &$redirects = 0) @@ -325,20 +308,7 @@ class HTTPRequest } /** - * Returns the original URL of the provided URL - * - * This function strips tracking query params and follows redirections, either - * through HTTP code or meta refresh tags. Stops after 10 redirections. - * - * @todo Remove the $fetchbody parameter that generates an extraneous HEAD request - * - * @see ParseUrl::getSiteinfo - * - * @param string $url A user-submitted URL - * @param int $depth The current redirection recursion level (internal) - * @param bool $fetchbody Wether to fetch the body or not after the HEAD requests - * @return string A canonical URL - * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * {@inheritDoc} */ public function finalUrl(string $url, int $depth = 1, bool $fetchbody = false) { @@ -443,21 +413,10 @@ class HTTPRequest } /** - * Curl wrapper + * {@inheritDoc} * - * If binary flag is true, return binary results. - * Set the cookiejar argument to a string (e.g. "/tmp/friendica-cookies.txt") - * to preserve cookies from one request to the next. + * @param int $redirects The recursion counter for internal use - default 0 * - * @param string $url URL to fetch - * @param bool $binary default false - * TRUE if asked to return binary results (file download) - * @param int $timeout Timeout in seconds, default system config value or 60 seconds - * @param string $accept_content supply Accept: header with 'accept_content' as the value - * @param string $cookiejar Path to cookie jar file - * @param int $redirects The recursion counter for internal use - default 0 - * - * @return string The fetched content * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ public function fetch(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = '', int &$redirects = 0) @@ -468,20 +427,10 @@ class HTTPRequest } /** - * Curl wrapper with array of return values. + * {@inheritDoc} * - * Inner workings and parameters are the same as @ref fetchUrl but returns an array with - * all the information collected during the fetch. + * @param int $redirects The recursion counter for internal use - default 0 * - * @param string $url URL to fetch - * @param bool $binary default false - * TRUE if asked to return binary results (file download) - * @param int $timeout Timeout in seconds, default system config value or 60 seconds - * @param string $accept_content supply Accept: header with 'accept_content' as the value - * @param string $cookiejar Path to cookie jar file - * @param int $redirects The recursion counter for internal use - default 0 - * - * @return CurlResult With all relevant information, 'body' contains the actual fetched content. * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ public function fetchFull(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = '', int &$redirects = 0) @@ -499,9 +448,7 @@ class HTTPRequest } /** - * Returns the current UserAgent as a String - * - * @return string the UserAgent as a String + * {@inheritDoc} */ public function getUserAgent() { diff --git a/src/Network/IHTTPRequest.php b/src/Network/IHTTPRequest.php new file mode 100644 index 000000000..3ebcc5dc1 --- /dev/null +++ b/src/Network/IHTTPRequest.php @@ -0,0 +1,119 @@ +. + * + */ + +namespace Friendica\Network; + +/** + * Interface for calling HTTP requests and returning their responses + */ +interface IHTTPRequest +{ + /** + * Fetches the content of an URL + * + * If binary flag is true, return binary results. + * Set the cookiejar argument to a string (e.g. "/tmp/friendica-cookies.txt") + * to preserve cookies from one request to the next. + * + * @param string $url URL to fetch + * @param bool $binary default false + * TRUE if asked to return binary results (file download) + * @param int $timeout Timeout in seconds, default system config value or 60 seconds + * @param string $accept_content supply Accept: header with 'accept_content' as the value + * @param string $cookiejar Path to cookie jar file + * + * @return string The fetched content + */ + public function fetch(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = ''); + + /** + * Fetches the whole response of an URL. + * + * Inner workings and parameters are the same as @ref fetchUrl but returns an array with + * all the information collected during the fetch. + * + * @param string $url URL to fetch + * @param bool $binary default false + * TRUE if asked to return binary results (file download) + * @param int $timeout Timeout in seconds, default system config value or 60 seconds + * @param string $accept_content supply Accept: header with 'accept_content' as the value + * @param string $cookiejar Path to cookie jar file + * + * @return CurlResult With all relevant information, 'body' contains the actual fetched content. + */ + public function fetchFull(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = ''); + + /** + * Send a GET to an URL. + * + * @param string $url URL to fetch + * @param bool $binary default false + * TRUE if asked to return binary results (file download) + * @param array $opts (optional parameters) assoziative array with: + * 'accept_content' => supply Accept: header with 'accept_content' as the value + * 'timeout' => int Timeout in seconds, default system config value or 60 seconds + * 'http_auth' => username:password + * 'novalidate' => do not validate SSL certs, default is to validate using our CA list + * 'nobody' => only return the header + * 'cookiejar' => path to cookie jar file + * 'header' => header array + * + * @return CurlResult + */ + public function get(string $url, bool $binary = false, array $opts = []); + + /** + * Send POST request to an URL + * + * @param string $url URL to post + * @param mixed $params array of POST variables + * @param array $headers HTTP headers + * @param int $timeout The timeout in seconds, default system config value or 60 seconds + * + * @return CurlResult The content + */ + public function post(string $url, $params, array $headers = [], int $timeout = 0); + + /** + * Returns the original URL of the provided URL + * + * This function strips tracking query params and follows redirections, either + * through HTTP code or meta refresh tags. Stops after 10 redirections. + * + * @param string $url A user-submitted URL + * @param int $depth The current redirection recursion level (internal) + * @param bool $fetchbody Wether to fetch the body or not after the HEAD requests + * + * @return string A canonical URL + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @see ParseUrl::getSiteinfo + * + * @todo Remove the $fetchbody parameter that generates an extraneous HEAD request + */ + public function finalUrl(string $url, int $depth = 1, bool $fetchbody = false); + + /** + * Returns the current UserAgent as a String + * + * @return string the UserAgent as a String + */ + public function getUserAgent(); +} diff --git a/static/dependencies.config.php b/static/dependencies.config.php index 84344a60e..fe8a8caee 100644 --- a/static/dependencies.config.php +++ b/static/dependencies.config.php @@ -46,6 +46,7 @@ use Friendica\Database\Database; use Friendica\Factory; use Friendica\Model\Storage\IStorage; use Friendica\Model\User\Cookie; +use Friendica\Network; use Friendica\Util; use Psr\Log\LoggerInterface; @@ -219,4 +220,7 @@ return [ ['getBackend', [], Dice::CHAIN_CALL], ], ], + Network\IHTTPRequest::class => [ + 'instanceOf' => Network\HTTPRequest::class, + ] ]; From 1124090dbc5c9ff54b0f683424f16fb3123505d5 Mon Sep 17 00:00:00 2001 From: nupplaPhil Date: Wed, 4 Mar 2020 23:28:41 +0100 Subject: [PATCH 14/44] Cleanup dependencies --- src/Core/Protocol.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Core/Protocol.php b/src/Core/Protocol.php index e6133240c..7b9789752 100644 --- a/src/Core/Protocol.php +++ b/src/Core/Protocol.php @@ -21,6 +21,8 @@ namespace Friendica\Core; +use Friendica\DI; + /** * Manage compatibility with federated networks */ @@ -89,7 +91,6 @@ class Protocol * @param string $profile_url * @param array $matches preg_match return array: [0] => Full match [1] => hostname [2] => username * @return string - * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ public static function matchByProfileUrl($profile_url, &$matches = []) { @@ -121,7 +122,7 @@ class Protocol if (preg_match('=https?://(.*)/user/(.*)=ism', $profile_url, $matches)) { $statusnet_host = $matches[1]; $statusnet_user = $matches[2]; - $UserData = DI::httpRequest()->fetchUrl('http://' . $statusnet_host . '/api/users/show.json?user_id=' . $statusnet_user); + $UserData = DI::httpRequest()->fetch('http://' . $statusnet_host . '/api/users/show.json?user_id=' . $statusnet_user); $user = json_decode($UserData); if ($user) { $matches[2] = $user->screen_name; From c51128ad201b92e070b12b24a346d0e5266ca5ad Mon Sep 17 00:00:00 2001 From: nupplaPhil Date: Wed, 4 Mar 2020 23:36:46 +0100 Subject: [PATCH 15/44] Fix tests --- tests/src/Core/InstallerTest.php | 38 +++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/tests/src/Core/InstallerTest.php b/tests/src/Core/InstallerTest.php index f512bf17b..6ec2f1bc7 100644 --- a/tests/src/Core/InstallerTest.php +++ b/tests/src/Core/InstallerTest.php @@ -26,9 +26,9 @@ use Dice\Dice; use Friendica\Core\Config\Cache; use Friendica\DI; use Friendica\Network\CurlResult; +use Friendica\Network\IHTTPRequest; use Friendica\Test\MockedTest; use Friendica\Test\Util\VFSTrait; -use Friendica\Util\Network; use Mockery\MockInterface; class InstallerTest extends MockedTest @@ -39,6 +39,10 @@ class InstallerTest extends MockedTest * @var \Friendica\Core\L10n|MockInterface */ private $l10nMock; + /** + * @var Dice|MockInterface + */ + private $dice; public function setUp() { @@ -49,14 +53,14 @@ class InstallerTest extends MockedTest $this->l10nMock = \Mockery::mock(\Friendica\Core\L10n::class); /** @var Dice|MockInterface $dice */ - $dice = \Mockery::mock(Dice::class)->makePartial(); - $dice = $dice->addRules(include __DIR__ . '/../../../static/dependencies.config.php'); + $this->dice = \Mockery::mock(Dice::class)->makePartial(); + $this->dice = $this->dice->addRules(include __DIR__ . '/../../../static/dependencies.config.php'); - $dice->shouldReceive('create') + $this->dice->shouldReceive('create') ->with(\Friendica\Core\L10n::class) ->andReturn($this->l10nMock); - DI::init($dice); + DI::init($this->dice); } private function mockL10nT(string $text, $times = null) @@ -305,16 +309,22 @@ class InstallerTest extends MockedTest ->andReturn('test Error'); // Mocking the CURL Request - $networkMock = \Mockery::mock('alias:' . Network::class); + $networkMock = \Mockery::mock(IHTTPRequest::class); $networkMock - ->shouldReceive('fetchUrlFull') + ->shouldReceive('fetchFull') ->with('https://test/install/testrewrite') ->andReturn($curlResult); $networkMock - ->shouldReceive('fetchUrlFull') + ->shouldReceive('fetchFull') ->with('http://test/install/testrewrite') ->andReturn($curlResult); + $this->dice->shouldReceive('create') + ->with(IHTTPRequest::class) + ->andReturn($networkMock); + + DI::init($this->dice); + // Mocking that we can use CURL $this->setFunctions(['curl_init' => true]); @@ -346,16 +356,22 @@ class InstallerTest extends MockedTest ->andReturn('204'); // Mocking the CURL Request - $networkMock = \Mockery::mock('alias:' . Network::class); + $networkMock = \Mockery::mock(IHTTPRequest::class); $networkMock - ->shouldReceive('fetchUrlFull') + ->shouldReceive('fetchFull') ->with('https://test/install/testrewrite') ->andReturn($curlResultF); $networkMock - ->shouldReceive('fetchUrlFull') + ->shouldReceive('fetchFull') ->with('http://test/install/testrewrite') ->andReturn($curlResultW); + $this->dice->shouldReceive('create') + ->with(IHTTPRequest::class) + ->andReturn($networkMock); + + DI::init($this->dice); + // Mocking that we can use CURL $this->setFunctions(['curl_init' => true]); From 1998b7811ba7fc03367eb335f8f0e6b2f9f17e0c Mon Sep 17 00:00:00 2001 From: nupplaPhil Date: Sat, 7 Mar 2020 13:39:09 +0100 Subject: [PATCH 16/44] Fix fatal execution path for found Network::get() parameter execption --- src/Model/Item.php | 8 ++++---- src/Protocol/ActivityPub.php | 2 +- src/Protocol/ActivityPub/Processor.php | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Model/Item.php b/src/Model/Item.php index e31097f53..46f2fb09a 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -3639,7 +3639,7 @@ class Item * * @return integer item id */ - public static function fetchByLink($uri, $uid = 0) + public static function fetchByLink(string $uri, int $uid = 0) { $item_id = self::searchByLink($uri, $uid); if (!empty($item_id)) { @@ -3692,7 +3692,7 @@ class Item * * @return array item array with data from the original item */ - public static function addShareDataFromOriginal($item) + public static function addShareDataFromOriginal(array $item) { $shared = self::getShareArray($item); if (empty($shared)) { @@ -3714,9 +3714,9 @@ class Item } // Otherwhise try to find (and possibly fetch) the item via the link. This should work for Diaspora and ActivityPub posts - $id = self::fetchByLink($shared['link'], $uid); + $id = self::fetchByLink($shared['link'] ?? '', $uid); if (empty($id)) { - Logger::info('Original item not found', ['url' => $shared['link'], 'callstack' => System::callstack()]); + Logger::info('Original item not found', ['url' => $shared['link'] ?? '', 'callstack' => System::callstack()]); return $item; } diff --git a/src/Protocol/ActivityPub.php b/src/Protocol/ActivityPub.php index 6b29eabce..3c4f4f2e6 100644 --- a/src/Protocol/ActivityPub.php +++ b/src/Protocol/ActivityPub.php @@ -87,7 +87,7 @@ class ActivityPub * @return array * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function fetchContent($url, $uid = 0) + public static function fetchContent(string $url, int $uid = 0) { if (!empty($uid)) { return HTTPSignature::fetch($url, $uid); diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index e4cef1704..04d8a7467 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -681,7 +681,7 @@ class Processor * @return string fetched message URL * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function fetchMissingActivity($url, $child = []) + public static function fetchMissingActivity(string $url, array $child = []) { if (!empty($child['receiver'])) { $uid = ActivityPub\Receiver::getFirstUserFromReceivers($child['receiver']); From ec3ec3b78a2a431b82ff16136dddd5e6900c3e1e Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Tue, 21 Jul 2020 21:39:59 -0400 Subject: [PATCH 17/44] Replace remaining occurrences of Network::curl --- src/Model/GContact.php | 3 ++- src/Model/GServer.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Model/GContact.php b/src/Model/GContact.php index ab0a4fdd8..41ca763fc 100644 --- a/src/Model/GContact.php +++ b/src/Model/GContact.php @@ -35,6 +35,7 @@ use Friendica\Network\Probe; use Friendica\Protocol\ActivityPub; use Friendica\Protocol\PortableContact; use Friendica\Util\DateTimeFormat; +use Friendica\Util\Network; use Friendica\Util\Strings; /** @@ -1377,7 +1378,7 @@ class GContact return; } - $curlResult = Network::curl($data['poco']); + $curlResult = DI::httpRequest()->get($data['poco']); if (!$curlResult->isSuccess()) { return; } diff --git a/src/Model/GServer.php b/src/Model/GServer.php index 7643c9590..0f47146eb 100644 --- a/src/Model/GServer.php +++ b/src/Model/GServer.php @@ -359,7 +359,7 @@ class GServer // When the base path doesn't seem to contain a social network we try the complete path. // Most detectable system have to be installed in the root directory. // We checked the base to avoid false positives. - $curlResult = Network::curl($url, false, ['timeout' => $xrd_timeout]); + $curlResult = DI::httpRequest()->get($url, false, ['timeout' => $xrd_timeout]); if ($curlResult->isSuccess()) { $urldata = self::analyseRootHeader($curlResult, $serverdata); $urldata = self::analyseRootBody($curlResult, $urldata, $url); From 8572cec0cb40aced0a7a25a43087b0767a966565 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 22 Jul 2020 10:42:53 -0400 Subject: [PATCH 18/44] [Composer] Add new dependency npm-asset/textcomplete --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index b3dd0ec90..2d6b46574 100644 --- a/composer.json +++ b/composer.json @@ -62,6 +62,7 @@ "npm-asset/jgrowl": "^1.4", "npm-asset/moment": "^2.24", "npm-asset/perfect-scrollbar": "0.6.16", + "npm-asset/textcomplete": "^0.18.2", "npm-asset/typeahead.js": "^0.11.1" }, "repositories": [ From 0bfe5966ae66dda064c68a2b1771fc51f49aa395 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 22 Jul 2020 10:43:05 -0400 Subject: [PATCH 19/44] [Composer] Update Composer lock file --- composer.lock | 270 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 210 insertions(+), 60 deletions(-) diff --git a/composer.lock b/composer.lock index 45c6137a4..9f6f78d00 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ded67f7e680a122d0cd3512c2738be97", + "content-hash": "7d1fe40c28d815b56d0b5cb323860b26", "packages": [ { "name": "asika/simple-console", @@ -1276,6 +1276,63 @@ ], "time": "2017-07-06T13:46:38+00:00" }, + { + "name": "npm-asset/eventemitter3", + "version": "2.0.3", + "dist": { + "type": "tar", + "url": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "shasum": "b5e1079b59fb5e1ba2771c0a993be060a58c99ba" + }, + "type": "npm-asset-library", + "extra": { + "npm-asset-bugs": { + "url": "https://github.com/primus/eventemitter3/issues" + }, + "npm-asset-main": "index.js", + "npm-asset-directories": [], + "npm-asset-repository": { + "type": "git", + "url": "git://github.com/primus/eventemitter3.git" + }, + "npm-asset-scripts": { + "build": "mkdir -p umd && browserify index.js -s EventEmitter3 | uglifyjs -m -o umd/eventemitter3.min.js", + "benchmark": "find benchmarks/run -name '*.js' -exec benchmarks/start.sh {} \\;", + "test": "nyc --reporter=html --reporter=text mocha", + "test-browser": "zuul -- test.js", + "prepublish": "npm run build", + "sync": "node versions.js" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Arnout Kazemier" + } + ], + "description": "EventEmitter3 focuses on performance while maintaining a Node.js AND browser compatible interface.", + "homepage": "https://github.com/primus/eventemitter3#readme", + "keywords": [ + "EventEmitter", + "EventEmitter2", + "EventEmitter3", + "Events", + "addEventListener", + "addListener", + "emit", + "emits", + "emitter", + "event", + "once", + "pub/sub", + "publish", + "reactor", + "subscribe" + ], + "time": "2017-03-31T14:51:09+00:00" + }, { "name": "npm-asset/fullcalendar", "version": "3.10.2", @@ -1792,64 +1849,6 @@ ], "time": "2017-01-10T01:03:05+00:00" }, - { - "name": "npm-asset/perfect-scrollbar", - "version": "0.6.16", - "dist": { - "type": "tar", - "url": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-0.6.16.tgz", - "shasum": "b1d61a5245cf3962bb9a8407a3fc669d923212fc" - }, - "type": "npm-asset-library", - "extra": { - "npm-asset-bugs": { - "url": "https://github.com/noraesae/perfect-scrollbar/issues" - }, - "npm-asset-files": [ - "dist", - "src", - "index.js", - "jquery.js", - "perfect-scrollbar.d.ts" - ], - "npm-asset-main": "./index.js", - "npm-asset-directories": [], - "npm-asset-repository": { - "type": "git", - "url": "git+https://github.com/noraesae/perfect-scrollbar.git" - }, - "npm-asset-scripts": { - "test": "gulp", - "before-deploy": "gulp && gulp compress", - "release": "rm -rf dist && gulp && npm publish" - }, - "npm-asset-engines": { - "node": ">= 0.12.0" - } - }, - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Hyunje Jun", - "email": "me@noraesae.net" - }, - { - "name": "Hyunje Jun", - "email": "me@noraesae.net" - } - ], - "description": "Minimalistic but perfect custom scrollbar plugin", - "homepage": "https://github.com/noraesae/perfect-scrollbar#readme", - "keywords": [ - "frontend", - "jquery-plugin", - "scroll", - "scrollbar" - ], - "time": "2017-01-10T01:03:05+00:00" - }, { "name": "npm-asset/php-date-formatter", "version": "v1.3.6", @@ -1888,6 +1887,100 @@ "homepage": "https://github.com/kartik-v/php-date-formatter", "time": "2020-04-14T10:16:32+00:00" }, + { + "name": "npm-asset/textarea-caret", + "version": "3.1.0", + "dist": { + "type": "tar", + "url": "https://registry.npmjs.org/textarea-caret/-/textarea-caret-3.1.0.tgz", + "shasum": "5d5a35bb035fd06b2ff0e25d5359e97f2655087f" + }, + "type": "npm-asset-library", + "extra": { + "npm-asset-bugs": { + "url": "https://github.com/component/textarea-caret-position/issues" + }, + "npm-asset-files": [ + "index.js" + ], + "npm-asset-main": "index.js", + "npm-asset-directories": [], + "npm-asset-repository": { + "type": "git", + "url": "git+https://github.com/component/textarea-caret-position.git" + } + }, + "license": [ + "MIT" + ], + "description": "(x, y) coordinates of the caret in a textarea or input type='text'", + "homepage": "https://github.com/component/textarea-caret-position#readme", + "keywords": [ + "caret", + "position", + "textarea" + ], + "time": "2018-02-20T06:11:03+00:00" + }, + { + "name": "npm-asset/textcomplete", + "version": "0.18.2", + "dist": { + "type": "tar", + "url": "https://registry.npmjs.org/textcomplete/-/textcomplete-0.18.2.tgz", + "shasum": "de0d806567102f7e32daffcbcc3db05af1515eb5" + }, + "require": { + "npm-asset/eventemitter3": ">=2.0.3,<3.0.0", + "npm-asset/textarea-caret": ">=3.0.1,<4.0.0", + "npm-asset/undate": ">=0.2.3,<0.3.0" + }, + "type": "npm-asset-library", + "extra": { + "npm-asset-bugs": { + "url": "https://github.com/yuku-t/textcomplete/issues" + }, + "npm-asset-main": "lib/index.js", + "npm-asset-directories": [], + "npm-asset-repository": { + "type": "git", + "url": "git+ssh://git@github.com/yuku-t/textcomplete.git" + }, + "npm-asset-scripts": { + "build": "yarn run clean && run-p build:*", + "build:dist": "webpack && webpack --env=min && run-p print-dist-gz-size", + "build:docs": "run-p build:docs:*", + "build:docs:html": "webpack --config webpack.doc.config.js && pug -o docs src/doc/index.pug", + "build:docs:md": "documentation build src/*.js -f md -o doc/api.md", + "build:lib": "babel src -d lib -s && for js in src/*.js; do cp $js lib/${js##*/}.flow; done", + "clean": "rm -fr dist docs lib", + "format": "prettier --no-semi --trailing-comma all --write 'src/*.js' 'test/**/*.js'", + "gh-release": "npm pack textcomplete && gh-release -a textcomplete-$(cat package.json|jq -r .version).tgz", + "opener": "wait-on http://localhost:8082 && opener http://localhost:8082", + "print-dist-gz-size": "printf 'dist/textcomplete.min.js.gz: %d bytes\\n' \"$(gzip -9kc dist/textcomplete.min.js | wc -c)\"", + "start": "run-p watch opener", + "test": "run-p test:*", + "test:bundlesize": "yarn run build:dist && bundlesize", + "test:e2e": "NODE_ENV=test karma start --single-run", + "test:lint": "eslint src/*.js test/**/*.js", + "test:typecheck": "flow check", + "watch": "run-p watch:*", + "watch:webpack": "webpack-dev-server --config webpack.doc.config.js", + "watch:pug": "pug -o docs --watch src/doc/index.pug" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Yuku Takahashi" + } + ], + "description": "Autocomplete for textarea elements", + "homepage": "https://github.com/yuku-t/textcomplete#readme", + "time": "2020-06-10T06:11:00+00:00" + }, { "name": "npm-asset/typeahead.js", "version": "0.11.1", @@ -1940,6 +2033,48 @@ ], "time": "2015-04-27T04:03:42+00:00" }, + { + "name": "npm-asset/undate", + "version": "0.2.4", + "dist": { + "type": "tar", + "url": "https://registry.npmjs.org/undate/-/undate-0.2.4.tgz", + "shasum": "ccb2a8cf38edc035d1006fcb2909c4c6024a8400" + }, + "type": "npm-asset-library", + "extra": { + "npm-asset-bugs": { + "url": "https://github.com/yuku-t/undate/issues" + }, + "npm-asset-main": "lib/index.js", + "npm-asset-directories": [], + "npm-asset-repository": { + "type": "git", + "url": "git+https://github.com/yuku-t/undate.git" + }, + "npm-asset-scripts": { + "build": "babel src -d lib && for js in src/*.js; do cp $js lib/${js##*/}.flow; done", + "test": "run-p test:*", + "test:eslint": "eslint src/*.js test/*.js", + "test:flow": "flow check", + "test:karma": "karma start --single-run" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Yuku Takahashi" + } + ], + "description": "Undoable update for HTMLTextAreaElement", + "homepage": "https://github.com/yuku-t/undate#readme", + "keywords": [ + "textarea" + ], + "time": "2018-01-24T10:49:39+00:00" + }, { "name": "paragonie/certainty", "version": "v2.6.1", @@ -4669,6 +4804,20 @@ "polyfill", "portable" ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-05-12T16:14:59+00:00" }, { @@ -4801,5 +4950,6 @@ "platform-dev": [], "platform-overrides": { "php": "7.0" - } + }, + "plugin-api-version": "1.1.0" } From f4afd56fa5c87b8f614cadee0f897f4b8047709f Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 22 Jul 2020 10:48:02 -0400 Subject: [PATCH 20/44] Replace jquery-textcomplete with yuku/old-textcomplete - Add a jQuery wrapper to minimize code changes - Improve local autocomplete jQuery plugin to allow chaining --- view/js/autocomplete.js | 40 +++++++++++++++++++++--------- view/templates/head.tpl | 2 +- view/theme/frio/templates/head.tpl | 2 +- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/view/js/autocomplete.js b/view/js/autocomplete.js index 7f5f36cfd..c3993603b 100644 --- a/view/js/autocomplete.js +++ b/view/js/autocomplete.js @@ -197,6 +197,23 @@ function string2bb(element) { * jQuery plugin 'editor_autocomplete' */ (function( $ ) { + let textcompleteObjects = []; + + // jQuery wrapper for yuku/old-textcomplete + // uses a local object directory to avoid recreating Textcomplete objects + $.fn.textcomplete = function (strategies, options) { + if (!(this.data('textcompleteId') in textcompleteObjects)) { + let editor = new Textcomplete.editors.Textarea(this.get(0)); + + this.data('textcompleteId', textcompleteObjects.length); + textcompleteObjects.push(new Textcomplete(editor, options)); + } + + textcompleteObjects[this.data('textcompleteId')].register(strategies); + + return this; + }; + /** * This function should be called immediately after $.textcomplete() to prevent the escape key press to propagate * after the autocompletion dropdown has closed. @@ -278,13 +295,10 @@ function string2bb(element) { this.attr('autocomplete','off'); this.textcomplete([contacts, forums, smilies, tags], {className:'acpopup', zIndex:10000}); this.fixTextcompleteEscape(); - }; -})( jQuery ); -/** - * jQuery plugin 'search_autocomplete' - */ -(function( $ ) { + return this; + }; + $.fn.search_autocomplete = function(backend_url) { // Autocomplete contacts contacts = { @@ -317,10 +331,10 @@ function string2bb(element) { this.textcomplete([contacts, community, tags], {className:'acpopup', maxCount:100, zIndex: 10000, appendTo:'nav'}); this.fixTextcompleteEscape(); this.on('textComplete:select', function(e, value, strategy) { submit_form(this); }); - }; -})( jQuery ); -(function( $ ) { + return this; + }; + $.fn.name_autocomplete = function(backend_url, typ, autosubmit, onselect) { if(typeof typ === 'undefined') typ = ''; if(typeof autosubmit === 'undefined') autosubmit = false; @@ -345,10 +359,10 @@ function string2bb(element) { if(typeof onselect !== 'undefined') { this.on('textComplete:select', function(e, value, strategy) { onselect(value); }); } - }; -})( jQuery ); -(function( $ ) { + return this; + }; + $.fn.bbco_autocomplete = function(type) { if (type === 'bbcode') { var open_close_elements = ['bold', 'italic', 'underline', 'overline', 'strike', 'quote', 'code', 'spoiler', 'map', 'img', 'url', 'audio', 'video', 'embed', 'youtube', 'vimeo', 'list', 'ul', 'ol', 'li', 'table', 'tr', 'th', 'td', 'center', 'color', 'font', 'size', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'nobb', 'noparse', 'pre', 'abstract']; @@ -399,6 +413,8 @@ function string2bb(element) { } } }); + + return this; }; })( jQuery ); // @license-end diff --git a/view/templates/head.tpl b/view/templates/head.tpl index f1ffcf69a..ed87017dc 100644 --- a/view/templates/head.tpl +++ b/view/templates/head.tpl @@ -35,7 +35,7 @@ - + diff --git a/view/theme/frio/templates/head.tpl b/view/theme/frio/templates/head.tpl index 9ad0c8a7e..4015a325a 100644 --- a/view/theme/frio/templates/head.tpl +++ b/view/theme/frio/templates/head.tpl @@ -56,7 +56,7 @@ - + From 08384ecf594dd2a280ff69fdc73e401192943dfc Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 22 Jul 2020 10:48:30 -0400 Subject: [PATCH 21/44] Remove obsolete view/js/jquery-textcomplete folder --- view/js/jquery-textcomplete/CHANGELOG.md | 340 ---- view/js/jquery-textcomplete/LICENSE | 21 - view/js/jquery-textcomplete/README.md | 46 - .../jquery.textcomplete.css | 33 - .../jquery.textcomplete.js | 1403 ----------------- .../jquery.textcomplete.min.js | 5 - .../jquery.textcomplete.min.map | 1 - 7 files changed, 1849 deletions(-) delete mode 100644 view/js/jquery-textcomplete/CHANGELOG.md delete mode 100644 view/js/jquery-textcomplete/LICENSE delete mode 100644 view/js/jquery-textcomplete/README.md delete mode 100644 view/js/jquery-textcomplete/jquery.textcomplete.css delete mode 100644 view/js/jquery-textcomplete/jquery.textcomplete.js delete mode 100644 view/js/jquery-textcomplete/jquery.textcomplete.min.js delete mode 100644 view/js/jquery-textcomplete/jquery.textcomplete.min.map diff --git a/view/js/jquery-textcomplete/CHANGELOG.md b/view/js/jquery-textcomplete/CHANGELOG.md deleted file mode 100644 index e115bf9af..000000000 --- a/view/js/jquery-textcomplete/CHANGELOG.md +++ /dev/null @@ -1,340 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. - -This project adheres to [Semantic Versioning](http://semver.org/) by version 1.0.0. - -This change log adheres to [keepachangelog.com](http://keepachangelog.com). - -## [Unreleased] - -## [1.3.4] - 2016-04-20 -### Fixed -- Fix endless loop when RTL ([#247](https://github.com/yuku-t/jquery-textcomplete/pull/247)) - -## [1.3.3] - 2016-04-04 -### Fixed -- Fix uncaught TypeError. - -## [1.3.2] - 2016-03-27 -### Fixed -- Fix dropdown position problem with `line-height: normal`. - -## [1.3.1] - 2016-03-23 -### Fixed -- Fix `input[type=search]` support. - -## [1.3.0] - 2016-03-20 -### Added -- Add optional "id" strategy parameter. - -## [1.2.2] - 2016-03-19 -### Fixed -- Remove dropdown element after `textcomplete('destroy')`. -- Skip search after pressing tab. -- Fix dropdown-menu positioning problem using textarea-caret package. - -## [1.2.1] - 2016-03-14 -### Fixed -- Build dist files. - -## [1.2.0] - 2016-03-14 -### Added -- Support `input[type=search]` ([#236](https://github.com/yuku-t/jquery-textcomplete/pull/236)) - -## [1.1.0] - 2016-03-10 -### Added -- Add the ability to insert HTML into a "contenteditable" field. ([#217](https://github.com/yuku-t/jquery-textcomplete/pull/217)) - -### Fixed -- Position relative to appendTo element. ([#234](https://github.com/yuku-t/jquery-textcomplete/pull/234)) -- Avoid dropdown bumping into right edge of window. ([#235](https://github.com/yuku-t/jquery-textcomplete/pull/235)) -- Fix top position issue when window is scrolled up and parents has fix position. ([#229](https://github.com/yuku-t/jquery-textcomplete/pull/229)) - -## [1.0.0] - 2016-02-29 -### Changed -- Adheres keepachangelog.com. - -## [0.8.2] - 2016-02-29 -### Added -- Add deactivate method to Completer. ([#233](https://github.com/yuku-t/jquery-textcomplete/pull/233)) - -## [0.8.1] - 2015-10-22 -### Added -- Add condition to ignore skipUnchangedTerm for empty text. ([#210](https://github.com/yuku-t/jquery-textcomplete/pull/210)) - -## [0.8.0] - 2015-08-31 -### Changed -- If undefined is returned from a replace callback dont replace the text. ([#204](https://github.com/yuku-t/jquery-textcomplete/pull/204)) - -## [0.7.3] - 2015-08-27 -### Added -- Add `Strategy#el` and `Strategy#$el` which returns current input/textarea element and corresponding jquery object respectively. - -## [0.7.2] - 2015-08-26 -### Fixed -- Reset \_term after selected ([#170](https://github.com/yuku-t/jquery-textcomplete/pull/170)) - -## [0.7.1] - 2015-08-19 -### Changed -- Remove RTL support because of some bugs. - -## [0.7.0] - 2015-07-02 -### Add -- Add support for a "no results" message like the header/footer. ([#179](https://github.com/yuku-t/jquery-textcomplete/pull/179)) -- Yield the search term to the template function. ([#177](https://github.com/yuku-t/jquery-textcomplete/pull/177)) -- Add amd wrapper. ([#167](https://github.com/yuku-t/jquery-textcomplete/pull/167)) -- Add touch devices support. ([#163](https://github.com/yuku-t/jquery-textcomplete/pull/163)) - -### Changed -- Stop sharing a dropdown element. - -## [0.6.1] - 2015-06-30 -### Fixed -- Fix bug that Dropdown.\_fitToBottom does not consider window scroll - -## [0.6.0] - 2015-06-30 -### Added -- Now dropdown elements have "textcomplete-dropdown" class. - -## [0.5.2] - 2015-06-29 -### Fixed -- Keep dropdown list in browser window. ([#172](https://github.com/yuku-t/jquery-textcomplete/pull/172)) - -## [0.5.1] - 2015-06-08 -### Changed -- Now a replace function is invoked with a user event. - -## [0.5.0] - 2015-06-08 -### Added -- Support `onKeydown` option. - -## [0.4.0] - 2015-03-10 -### Added -- Publish to [npmjs](https://www.npmjs.com/package/jquery-textcomplete). -- Support giving a function which returns a regexp to `match` option for dynamic matching. - -## [0.3.9] - 2015-03-03 -### Fixed -- Deactivate dropdown on escape. ([#155](https://github.com/yuku-t/jquery-textcomplete/pull/155)) - -## [0.3.8] - 2015-02-26 -### Fixed -- Fix completion with enter key. ([#154](https://github.com/yuku-t/jquery-textcomplete/pull/154)) -- Fix empty span node is inserted. ([#153](https://github.com/yuku-t/jquery-textcomplete/pull/153)) - -## [0.3.7] - 2015-01-21 -### Added -- Support input([type=text]. [#149](https://github.com/yuku-t/jquery-textcomplete/pull/149)) - -## [0.3.6] - 2014-12-11 -### Added -- Support element.contentEditable compatibility check. ([#147](https://github.com/yuku-t/jquery-textcomplete/pull/147)) - -### Fixed -- Fixes the fire function for events with additional parameters. ([#145](https://github.com/yuku-t/jquery-textcomplete/pull/145)) - -## [0.3.5] - 2014-12-11 -### Added -- Adds functionality to complete selection on space key. ([#141](https://github.com/yuku-t/jquery-textcomplete/pull/141)) - -### Fixed -- Loading script in head and destroy method bugfixes. ([#143](https://github.com/yuku-t/jquery-textcomplete/pull/143)) - -## [0.3.4] - 2014-12-03 -### Fixed -- Fix error when destroy is called before the field is focused. ([#138](https://github.com/yuku-t/jquery-textcomplete/pull/138)) -- Fix IE bug where it would only trigger when tha carrot was at the end of the line. ([#133](https://github.com/yuku-t/jquery-textcomplete/pull/133)) - -## [0.3.3] - 2014-09-25 -### Added -- Add `className` option. -- Add `match` as the third argument of a search function. - -### Fixed -- Ignore `.textcomplete('destory')` on non-initialized elements. ([#118](https://github.com/yuku-t/jquery-textcomplete/pull/118)) -- Trigger completer with the current text by default. ([#119](https://github.com/yuku-t/jquery-textcomplete/pull/119)) -- Hide dropdown before destroying it. ([#120](https://github.com/yuku-t/jquery-textcomplete/pull/120)) -- Don't throw an exception even if a jquery click event is manually triggered. ([#121](https://github.com/yuku-t/jquery-textcomplete/pull/121)) - -## [0.3.2] - 2014-09-16 -### Added -- Add `IETextarea` adapter which supports IE8 -- Add `idProperty` option. -- Add `adapter` option. - -### Changed -- Rename `Input` as `Adapter`. - -## [0.3.1] - 2014-09-10 -### Added -- Add `context` strategy option. -- Add `debounce` option. - -### Changed -- Recycle `.dropdown-menu` element if available. - -## [0.3.0] - 2014-09-10 -### Added -- Consider the `tab-size` of textarea. -- Add `zIndex` option. - -### Fixed -- Revive `header` and `footer` options. -- Revive `height` option. - -## [0.3.0-beta2] - 2014-09-09 -### Fixed -- Make sure that all demos work fine. - -## [0.3.0-beta1] - 2014-08-31 -### Fixed -- Huge refactoring. - -## [0.2.6] - 2014-08-16 -### Fixed -- Repair contenteditable. - -## [0.2.5] - 2014-08-07 -### Added -- Enhance contenteditable support. ([#98](https://github.com/yuku-t/jquery-textcomplete/pull/98)) -- Support absolute left/right placement. ([#96](https://github.com/yuku-t/jquery-textcomplete/pull/96)) -- Support absolute height, scrollbar, pageup and pagedown. ([#87](https://github.com/yuku-t/jquery-textcomplete/pull/87)) - -## [0.2.4] - 2014-07-02 -### Fixed -- Fix horizonal position on contentEditable elements. ([#92](https://github.com/yuku-t/jquery-textcomplete/pull/92)) - -## [0.2.3] - 2014-06-24 -### Added -- Option to supply list view position function. ([#88](https://github.com/yuku-t/jquery-textcomplete/pull/88)) - -## [0.2.2] - 2014-06-08 -### Added -- Append dropdown element to body element by default. -- Tiny refactoring. [#84] -- Ignore tab key when modifier keys are being pushed. ([#85](https://github.com/yuku-t/jquery-textcomplete/pull/85)) -- Manual triggering. - -## [0.2.1] - 2014-05-15 -### Added -- Support `appendTo` option. -- `header` and `footer` supports a function. - -### Changed -- Remove textcomplate-wrapper element. - -## [0.2.0] - 2014-05-02 -### Added -- Contenteditable support. -- Several bugfixes. -- Support `header` and `footer` setting. - -## [0.1.4.1] - 2014-04-04 -### Added -- Support placement option. -- Emacs-style prev/next keybindings. -- Replay searchFunc for the last term on slow network env. - -### Fixed -- Several bugfixes. - -## [0.1.3] - 2014-04-07 -### Added -- Support RTL positioning. - -### Fixed -- Several bugfixes. - -## [0.1.2] - 2014-02-08 -### Added -- Enable to append strategies on the fly. -- Enable to stop autocompleting. -- Enable to apply multiple textareas at once. -- Don't show popup on pressing arrow up and down keys. -- Hide dropdown by pressing ESC key. -- Prevent showing a dropdown when it just autocompleted. - -## [0.1.1] - 2014-02-02 -### Added -- Introduce `textComplete:show`, `textComplete:hide` and `textComplete:select` events. - -## [0.1.0] - 2013-10-28 -### Added -- Now strategies argument is an Array of strategy objects. - -## [0.0.4] - 2013-10-28 -### Added -- Up and Down arrows cycle instead of exit. -- Support Zepto. -- Support jQuery.overlay. - -### Fixed -- Several bugfixes. - -## [0.0.3] - 2013-09-11 -### Added -- Some performance improvement. -- Implement lazy callbacking on search function. - -## [0.0.2] - 2013-09-08 -### Added -- Support IE8. -- Some performance improvement. -- Implement cache option. - -## 0.0.1 - 2013-09-02 -### Added -- Initial release. - -[Unreleased]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.4...HEAD -[1.3.4]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.3...v1.3.4 -[1.3.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.2...v1.3.3 -[1.3.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.1...v1.3.2 -[1.3.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.3.0...v1.3.1 -[1.3.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.2.2...v1.3.0 -[1.2.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.2.1...v1.2.2 -[1.2.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.2.0...v1.2.1 -[1.2.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.1.0...v1.2.0 -[1.1.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v1.0.0...v1.1.0 -[1.0.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.8.2...v1.0.0 -[0.8.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.8.1...v0.8.2 -[0.8.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.8.0...v0.8.1 -[0.8.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.7.3...v0.8.0 -[0.7.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.7.2...v0.7.3 -[0.7.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.7.1...v0.7.2 -[0.7.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.7.0...v0.7.1 -[0.7.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.6.1...v0.7.0 -[0.6.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.6.0...v0.6.1 -[0.6.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.5.2...v0.6.0 -[0.5.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.5.1...v0.5.2 -[0.5.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.5.0...v0.5.1 -[0.5.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.4.0...v0.5.0 -[0.4.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.9...v0.4.0 -[0.3.9]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.8...v0.3.9 -[0.3.8]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.7...v0.3.8 -[0.3.7]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.6...v0.3.7 -[0.3.6]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.5...v0.3.6 -[0.3.5]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.4...v0.3.5 -[0.3.4]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.3...v0.3.4 -[0.3.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.2...v0.3.3 -[0.3.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.1...v0.3.2 -[0.3.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.0...v0.3.1 -[0.3.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.0-beta2...v0.3.0 -[0.3.0-beta2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.3.0-beta1...v0.3.0-beta2 -[0.3.0-beta1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.6...v0.3.0-beta1 -[0.2.6]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.5...v0.2.6 -[0.2.5]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.4...v0.2.5 -[0.2.4]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.3...v0.2.4 -[0.2.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.2...v0.2.3 -[0.2.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.1...v0.2.2 -[0.2.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.2.0...v0.2.1 -[0.2.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.4.1...v0.2.0 -[0.1.4.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.3...v0.1.4.1 -[0.1.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.2...v0.1.3 -[0.1.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.1...v0.1.2 -[0.1.1]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.1.0...v0.1.1 -[0.1.0]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.0.4...v0.1.0 -[0.0.4]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.0.3...v0.0.4 -[0.0.3]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.0.2...v0.0.3 -[0.0.2]: https://github.com/yuku-t/jquery-textcomplete/compare/v0.0.1...v0.0.2 diff --git a/view/js/jquery-textcomplete/LICENSE b/view/js/jquery-textcomplete/LICENSE deleted file mode 100644 index 4848bd637..000000000 --- a/view/js/jquery-textcomplete/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013-2014 Yuku Takahashi - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/view/js/jquery-textcomplete/README.md b/view/js/jquery-textcomplete/README.md deleted file mode 100644 index d74dfbd90..000000000 --- a/view/js/jquery-textcomplete/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# Autocomplete for Textarea - -[![npm version](https://badge.fury.io/js/jquery-textcomplete.svg)](http://badge.fury.io/js/jquery-textcomplete) -[![Bower version](https://badge.fury.io/bo/jquery-textcomplete.svg)](http://badge.fury.io/bo/jquery-textcomplete) -[![Analytics](https://ga-beacon.appspot.com/UA-4932407-14/jquery-textcomplete/readme)](https://github.com/igrigorik/ga-beacon) - -Introduces autocompleting power to textareas, like a GitHub comment form has. - -![Demo](http://yuku-t.com/jquery-textcomplete/media/images/demo.gif) - -[Demo](http://yuku-t.com/jquery-textcomplete/). - -## Synopsis - -```js -$('textarea').textcomplete([{ - match: /(^|\b)(\w{2,})$/, - search: function (term, callback) { - var words = ['google', 'facebook', 'github', 'microsoft', 'yahoo']; - callback($.map(words, function (word) { - return word.indexOf(term) === 0 ? word : null; - })); - }, - replace: function (word) { - return word + ' '; - } -}]); -``` - -## Dependencies - -- jQuery (>= 1.7.0) OR Zepto (>= 1.0) - -## Documents - -See [doc](https://github.com/yuku-t/jquery-textcomplete/tree/master/doc) dir. - -## License - -Licensed under the MIT License. - -## Contributors - -Patches and code improvements were contributed by: - -https://github.com/yuku-t/jquery-textcomplete/graphs/contributors diff --git a/view/js/jquery-textcomplete/jquery.textcomplete.css b/view/js/jquery-textcomplete/jquery.textcomplete.css deleted file mode 100644 index 37a761b7e..000000000 --- a/view/js/jquery-textcomplete/jquery.textcomplete.css +++ /dev/null @@ -1,33 +0,0 @@ -/* Sample */ - -.dropdown-menu { - border: 1px solid #ddd; - background-color: white; -} - -.dropdown-menu li { - border-top: 1px solid #ddd; - padding: 2px 5px; -} - -.dropdown-menu li:first-child { - border-top: none; -} - -.dropdown-menu li:hover, -.dropdown-menu .active { - background-color: rgb(110, 183, 219); -} - - -/* SHOULD not modify */ - -.dropdown-menu { - list-style: none; - padding: 0; - margin: 0; -} - -.dropdown-menu a:hover { - cursor: pointer; -} diff --git a/view/js/jquery-textcomplete/jquery.textcomplete.js b/view/js/jquery-textcomplete/jquery.textcomplete.js deleted file mode 100644 index 69ae1394a..000000000 --- a/view/js/jquery-textcomplete/jquery.textcomplete.js +++ /dev/null @@ -1,1403 +0,0 @@ -// @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt Expat -(function (factory) { - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define(['jquery'], factory); - } else if (typeof module === "object" && module.exports) { - var $ = require('jquery'); - module.exports = factory($); - } else { - // Browser globals - factory(jQuery); - } -}(function (jQuery) { - -/*! - * jQuery.textcomplete - * - * Repository: https://github.com/yuku-t/jquery-textcomplete - * License: MIT (https://github.com/yuku-t/jquery-textcomplete/blob/master/LICENSE) - * Author: Yuku Takahashi - */ - -if (typeof jQuery === 'undefined') { - throw new Error('jQuery.textcomplete requires jQuery'); -} - -+function ($) { - 'use strict'; - - var warn = function (message) { - if (console.warn) { console.warn(message); } - }; - - var id = 1; - - $.fn.textcomplete = function (strategies, option) { - var args = Array.prototype.slice.call(arguments); - return this.each(function () { - var self = this; - var $this = $(this); - var completer = $this.data('textComplete'); - if (!completer) { - option || (option = {}); - option._oid = id++; // unique object id - completer = new $.fn.textcomplete.Completer(this, option); - $this.data('textComplete', completer); - } - if (typeof strategies === 'string') { - if (!completer) return; - args.shift() - completer[strategies].apply(completer, args); - if (strategies === 'destroy') { - $this.removeData('textComplete'); - } - } else { - // For backward compatibility. - // TODO: Remove at v0.4 - $.each(strategies, function (obj) { - $.each(['header', 'footer', 'placement', 'maxCount'], function (name) { - if (obj[name]) { - completer.option[name] = obj[name]; - warn(name + 'as a strategy param is deprecated. Use option.'); - delete obj[name]; - } - }); - }); - completer.register($.fn.textcomplete.Strategy.parse(strategies, { - el: self, - $el: $this - })); - } - }); - }; - -}(jQuery); - -+function ($) { - 'use strict'; - - // Exclusive execution control utility. - // - // func - The function to be locked. It is executed with a function named - // `free` as the first argument. Once it is called, additional - // execution are ignored until the free is invoked. Then the last - // ignored execution will be replayed immediately. - // - // Examples - // - // var lockedFunc = lock(function (free) { - // setTimeout(function { free(); }, 1000); // It will be free in 1 sec. - // console.log('Hello, world'); - // }); - // lockedFunc(); // => 'Hello, world' - // lockedFunc(); // none - // lockedFunc(); // none - // // 1 sec past then - // // => 'Hello, world' - // lockedFunc(); // => 'Hello, world' - // lockedFunc(); // none - // - // Returns a wrapped function. - var lock = function (func) { - var locked, queuedArgsToReplay; - - return function () { - // Convert arguments into a real array. - var args = Array.prototype.slice.call(arguments); - if (locked) { - // Keep a copy of this argument list to replay later. - // OK to overwrite a previous value because we only replay - // the last one. - queuedArgsToReplay = args; - return; - } - locked = true; - var self = this; - args.unshift(function replayOrFree() { - if (queuedArgsToReplay) { - // Other request(s) arrived while we were locked. - // Now that the lock is becoming available, replay - // the latest such request, then call back here to - // unlock (or replay another request that arrived - // while this one was in flight). - var replayArgs = queuedArgsToReplay; - queuedArgsToReplay = undefined; - replayArgs.unshift(replayOrFree); - func.apply(self, replayArgs); - } else { - locked = false; - } - }); - func.apply(this, args); - }; - }; - - var isString = function (obj) { - return Object.prototype.toString.call(obj) === '[object String]'; - }; - - var isFunction = function (obj) { - return Object.prototype.toString.call(obj) === '[object Function]'; - }; - - var uniqueId = 0; - - function Completer(element, option) { - this.$el = $(element); - this.id = 'textcomplete' + uniqueId++; - this.strategies = []; - this.views = []; - this.option = $.extend({}, Completer._getDefaults(), option); - - if (!this.$el.is('input[type=text]') && !this.$el.is('input[type=search]') && !this.$el.is('textarea') && !element.isContentEditable && element.contentEditable != 'true') { - throw new Error('textcomplete must be called on a Textarea or a ContentEditable.'); - } - - if (element === document.activeElement) { - // element has already been focused. Initialize view objects immediately. - this.initialize() - } else { - // Initialize view objects lazily. - var self = this; - this.$el.one('focus.' + this.id, function () { self.initialize(); }); - } - } - - Completer._getDefaults = function () { - if (!Completer.DEFAULTS) { - Completer.DEFAULTS = { - appendTo: $('body'), - zIndex: '100' - }; - } - - return Completer.DEFAULTS; - } - - $.extend(Completer.prototype, { - // Public properties - // ----------------- - - id: null, - option: null, - strategies: null, - adapter: null, - dropdown: null, - $el: null, - - // Public methods - // -------------- - - initialize: function () { - var element = this.$el.get(0); - // Initialize view objects. - this.dropdown = new $.fn.textcomplete.Dropdown(element, this, this.option); - var Adapter, viewName; - if (this.option.adapter) { - Adapter = this.option.adapter; - } else { - if (this.$el.is('textarea') || this.$el.is('input[type=text]') || this.$el.is('input[type=search]')) { - viewName = typeof element.selectionEnd === 'number' ? 'Textarea' : 'IETextarea'; - } else { - viewName = 'ContentEditable'; - } - Adapter = $.fn.textcomplete[viewName]; - } - this.adapter = new Adapter(element, this, this.option); - }, - - destroy: function () { - this.$el.off('.' + this.id); - if (this.adapter) { - this.adapter.destroy(); - } - if (this.dropdown) { - this.dropdown.destroy(); - } - this.$el = this.adapter = this.dropdown = null; - }, - - deactivate: function () { - if (this.dropdown) { - this.dropdown.deactivate(); - } - }, - - // Invoke textcomplete. - trigger: function (text, skipUnchangedTerm) { - if (!this.dropdown) { this.initialize(); } - text != null || (text = this.adapter.getTextFromHeadToCaret()); - var searchQuery = this._extractSearchQuery(text); - if (searchQuery.length) { - var term = searchQuery[1]; - // Ignore shift-key, ctrl-key and so on. - if (skipUnchangedTerm && this._term === term && term !== "") { return; } - this._term = term; - this._search.apply(this, searchQuery); - } else { - this._term = null; - this.dropdown.deactivate(); - } - }, - - fire: function (eventName) { - var args = Array.prototype.slice.call(arguments, 1); - this.$el.trigger(eventName, args); - return this; - }, - - register: function (strategies) { - Array.prototype.push.apply(this.strategies, strategies); - }, - - // Insert the value into adapter view. It is called when the dropdown is clicked - // or selected. - // - // value - The selected element of the array callbacked from search func. - // strategy - The Strategy object. - // e - Click or keydown event object. - select: function (value, strategy, e) { - this._term = null; - this.adapter.select(value, strategy, e); - this.fire('change').fire('textComplete:select', value, strategy); - this.adapter.focus(); - }, - - // Private properties - // ------------------ - - _clearAtNext: true, - _term: null, - - // Private methods - // --------------- - - // Parse the given text and extract the first matching strategy. - // - // Returns an array including the strategy, the query term and the match - // object if the text matches an strategy; otherwise returns an empty array. - _extractSearchQuery: function (text) { - for (var i = 0; i < this.strategies.length; i++) { - var strategy = this.strategies[i]; - var context = strategy.context(text); - if (context || context === '') { - var matchRegexp = isFunction(strategy.match) ? strategy.match(text) : strategy.match; - if (isString(context)) { text = context; } - var match = text.match(matchRegexp); - if (match) { return [strategy, match[strategy.index], match]; } - } - } - return [] - }, - - // Call the search method of selected strategy.. - _search: lock(function (free, strategy, term, match) { - var self = this; - strategy.search(term, function (data, stillSearching) { - if (!self.dropdown.shown) { - self.dropdown.activate(); - } - if (self._clearAtNext) { - // The first callback in the current lock. - self.dropdown.clear(); - self._clearAtNext = false; - } - self.dropdown.setPosition(self.adapter.getCaretPosition()); - self.dropdown.render(self._zip(data, strategy, term)); - if (!stillSearching) { - // The last callback in the current lock. - free(); - self._clearAtNext = true; // Call dropdown.clear at the next time. - } - }, match); - }), - - // Build a parameter for Dropdown#render. - // - // Examples - // - // this._zip(['a', 'b'], 's'); - // //=> [{ value: 'a', strategy: 's' }, { value: 'b', strategy: 's' }] - _zip: function (data, strategy, term) { - return $.map(data, function (value) { - return { value: value, strategy: strategy, term: term }; - }); - } - }); - - $.fn.textcomplete.Completer = Completer; -}(jQuery); - -+function ($) { - 'use strict'; - - var $window = $(window); - - var include = function (zippedData, datum) { - var i, elem; - var idProperty = datum.strategy.idProperty - for (i = 0; i < zippedData.length; i++) { - elem = zippedData[i]; - if (elem.strategy !== datum.strategy) continue; - if (idProperty) { - if (elem.value[idProperty] === datum.value[idProperty]) return true; - } else { - if (elem.value === datum.value) return true; - } - } - return false; - }; - - var dropdownViews = {}; - $(document).on('click', function (e) { - var id = e.originalEvent && e.originalEvent.keepTextCompleteDropdown; - $.each(dropdownViews, function (key, view) { - if (key !== id) { view.deactivate(); } - }); - }); - - var commands = { - SKIP_DEFAULT: 0, - KEY_UP: 1, - KEY_DOWN: 2, - KEY_ENTER: 3, - KEY_PAGEUP: 4, - KEY_PAGEDOWN: 5, - KEY_ESCAPE: 6 - }; - - // Dropdown view - // ============= - - // Construct Dropdown object. - // - // element - Textarea or contenteditable element. - function Dropdown(element, completer, option) { - this.$el = Dropdown.createElement(option); - this.completer = completer; - this.id = completer.id + 'dropdown'; - this._data = []; // zipped data. - this.$inputEl = $(element); - this.option = option; - - // Override setPosition method. - if (option.listPosition) { this.setPosition = option.listPosition; } - if (option.height) { this.$el.height(option.height); } - var self = this; - $.each(['maxCount', 'placement', 'footer', 'header', 'noResultsMessage', 'className'], function (_i, name) { - if (option[name] != null) { self[name] = option[name]; } - }); - this._bindEvents(element); - dropdownViews[this.id] = this; - } - - $.extend(Dropdown, { - // Class methods - // ------------- - - createElement: function (option) { - var $parent = option.appendTo; - if (!($parent instanceof $)) { $parent = $($parent); } - var $el = $('
    ') - .addClass('dropdown-menu textcomplete-dropdown') - .attr('id', 'textcomplete-dropdown-' + option._oid) - .css({ - display: 'none', - left: 0, - position: 'absolute', - zIndex: option.zIndex - }) - .appendTo($parent); - return $el; - } - }); - - $.extend(Dropdown.prototype, { - // Public properties - // ----------------- - - $el: null, // jQuery object of ul.dropdown-menu element. - $inputEl: null, // jQuery object of target textarea. - completer: null, - footer: null, - header: null, - id: null, - maxCount: 10, - placement: '', - shown: false, - data: [], // Shown zipped data. - className: '', - - // Public methods - // -------------- - - destroy: function () { - // Don't remove $el because it may be shared by several textcompletes. - this.deactivate(); - - this.$el.off('.' + this.id); - this.$inputEl.off('.' + this.id); - this.clear(); - this.$el.remove(); - this.$el = this.$inputEl = this.completer = null; - delete dropdownViews[this.id] - }, - - render: function (zippedData) { - var contentsHtml = this._buildContents(zippedData); - var unzippedData = $.map(this.data, function (d) { return d.value; }); - if (this.data.length) { - var strategy = zippedData[0].strategy; - if (strategy.id) { - this.$el.attr('data-strategy', strategy.id); - } else { - this.$el.removeAttr('data-strategy'); - } - this._renderHeader(unzippedData); - this._renderFooter(unzippedData); - if (contentsHtml) { - this._renderContents(contentsHtml); - this._fitToBottom(); - this._fitToRight(); - this._activateIndexedItem(); - } - this._setScroll(); - } else if (this.noResultsMessage) { - this._renderNoResultsMessage(unzippedData); - } else if (this.shown) { - this.deactivate(); - } - }, - - setPosition: function (pos) { - // Make the dropdown fixed if the input is also fixed - // This can't be done during init, as textcomplete may be used on multiple elements on the same page - // Because the same dropdown is reused behind the scenes, we need to recheck every time the dropdown is showed - var position = 'absolute'; - // Check if input or one of its parents has positioning we need to care about - this.$inputEl.add(this.$inputEl.parents()).each(function() { - if($(this).css('position') === 'absolute') // The element has absolute positioning, so it's all OK - return false; - if($(this).css('position') === 'fixed') { - pos.top -= $window.scrollTop(); - pos.left -= $window.scrollLeft(); - position = 'fixed'; - return false; - } - }); - this.$el.css(this._applyPlacement(pos)); - this.$el.css({ position: position }); // Update positioning - - return this; - }, - - clear: function () { - this.$el.html(''); - this.data = []; - this._index = 0; - this._$header = this._$footer = this._$noResultsMessage = null; - }, - - activate: function () { - if (!this.shown) { - this.clear(); - this.$el.show(); - if (this.className) { this.$el.addClass(this.className); } - this.completer.fire('textComplete:show'); - this.shown = true; - } - return this; - }, - - deactivate: function () { - if (this.shown) { - this.$el.hide(); - if (this.className) { this.$el.removeClass(this.className); } - this.completer.fire('textComplete:hide'); - this.shown = false; - } - return this; - }, - - isUp: function (e) { - return e.keyCode === 38 || (e.ctrlKey && e.keyCode === 80); // UP, Ctrl-P - }, - - isDown: function (e) { - return e.keyCode === 40 || (e.ctrlKey && e.keyCode === 78); // DOWN, Ctrl-N - }, - - isEnter: function (e) { - var modifiers = e.ctrlKey || e.altKey || e.metaKey || e.shiftKey; - return !modifiers && (e.keyCode === 13 || e.keyCode === 9 || (this.option.completeOnSpace === true && e.keyCode === 32)) // ENTER, TAB - }, - - isPageup: function (e) { - return e.keyCode === 33; // PAGEUP - }, - - isPagedown: function (e) { - return e.keyCode === 34; // PAGEDOWN - }, - - isEscape: function (e) { - return e.keyCode === 27; // ESCAPE - }, - - // Private properties - // ------------------ - - _data: null, // Currently shown zipped data. - _index: null, - _$header: null, - _$noResultsMessage: null, - _$footer: null, - - // Private methods - // --------------- - - _bindEvents: function () { - this.$el.on('mousedown.' + this.id, '.textcomplete-item', $.proxy(this._onClick, this)); - this.$el.on('touchstart.' + this.id, '.textcomplete-item', $.proxy(this._onClick, this)); - this.$el.on('mouseover.' + this.id, '.textcomplete-item', $.proxy(this._onMouseover, this)); - this.$inputEl.on('keydown.' + this.id, $.proxy(this._onKeydown, this)); - }, - - _onClick: function (e) { - var $el = $(e.target); - e.preventDefault(); - e.originalEvent.keepTextCompleteDropdown = this.id; - if (!$el.hasClass('textcomplete-item')) { - $el = $el.closest('.textcomplete-item'); - } - var datum = this.data[parseInt($el.data('index'), 10)]; - this.completer.select(datum.value, datum.strategy, e); - var self = this; - // Deactive at next tick to allow other event handlers to know whether - // the dropdown has been shown or not. - setTimeout(function () { - self.deactivate(); - if (e.type === 'touchstart') { - self.$inputEl.focus(); - } - }, 0); - }, - - // Activate hovered item. - _onMouseover: function (e) { - var $el = $(e.target); - e.preventDefault(); - if (!$el.hasClass('textcomplete-item')) { - $el = $el.closest('.textcomplete-item'); - } - this._index = parseInt($el.data('index'), 10); - this._activateIndexedItem(); - }, - - _onKeydown: function (e) { - if (!this.shown) { return; } - - var command; - - if ($.isFunction(this.option.onKeydown)) { - command = this.option.onKeydown(e, commands); - } - - if (command == null) { - command = this._defaultKeydown(e); - } - - switch (command) { - case commands.KEY_UP: - e.preventDefault(); - this._up(); - break; - case commands.KEY_DOWN: - e.preventDefault(); - this._down(); - break; - case commands.KEY_ENTER: - e.preventDefault(); - this._enter(e); - break; - case commands.KEY_PAGEUP: - e.preventDefault(); - this._pageup(); - break; - case commands.KEY_PAGEDOWN: - e.preventDefault(); - this._pagedown(); - break; - case commands.KEY_ESCAPE: - e.preventDefault(); - this.deactivate(); - break; - } - }, - - _defaultKeydown: function (e) { - if (this.isUp(e)) { - return commands.KEY_UP; - } else if (this.isDown(e)) { - return commands.KEY_DOWN; - } else if (this.isEnter(e)) { - return commands.KEY_ENTER; - } else if (this.isPageup(e)) { - return commands.KEY_PAGEUP; - } else if (this.isPagedown(e)) { - return commands.KEY_PAGEDOWN; - } else if (this.isEscape(e)) { - return commands.KEY_ESCAPE; - } - }, - - _up: function () { - if (this._index === 0) { - this._index = this.data.length - 1; - } else { - this._index -= 1; - } - this._activateIndexedItem(); - this._setScroll(); - }, - - _down: function () { - if (this._index === this.data.length - 1) { - this._index = 0; - } else { - this._index += 1; - } - this._activateIndexedItem(); - this._setScroll(); - }, - - _enter: function (e) { - var datum = this.data[parseInt(this._getActiveElement().data('index'), 10)]; - this.completer.select(datum.value, datum.strategy, e); - this.deactivate(); - }, - - _pageup: function () { - var target = 0; - var threshold = this._getActiveElement().position().top - this.$el.innerHeight(); - this.$el.children().each(function (i) { - if ($(this).position().top + $(this).outerHeight() > threshold) { - target = i; - return false; - } - }); - this._index = target; - this._activateIndexedItem(); - this._setScroll(); - }, - - _pagedown: function () { - var target = this.data.length - 1; - var threshold = this._getActiveElement().position().top + this.$el.innerHeight(); - this.$el.children().each(function (i) { - if ($(this).position().top > threshold) { - target = i; - return false - } - }); - this._index = target; - this._activateIndexedItem(); - this._setScroll(); - }, - - _activateIndexedItem: function () { - this.$el.find('.textcomplete-item.active').removeClass('active'); - this._getActiveElement().addClass('active'); - }, - - _getActiveElement: function () { - return this.$el.children('.textcomplete-item:nth(' + this._index + ')'); - }, - - _setScroll: function () { - var $activeEl = this._getActiveElement(); - var itemTop = $activeEl.position().top; - var itemHeight = $activeEl.outerHeight(); - var visibleHeight = this.$el.innerHeight(); - var visibleTop = this.$el.scrollTop(); - if (this._index === 0 || this._index == this.data.length - 1 || itemTop < 0) { - this.$el.scrollTop(itemTop + visibleTop); - } else if (itemTop + itemHeight > visibleHeight) { - this.$el.scrollTop(itemTop + itemHeight + visibleTop - visibleHeight); - } - }, - - _buildContents: function (zippedData) { - var datum, i, index; - var html = ''; - for (i = 0; i < zippedData.length; i++) { - if (this.data.length === this.maxCount) break; - datum = zippedData[i]; - if (include(this.data, datum)) { continue; } - index = this.data.length; - this.data.push(datum); - html += '
  • '; - html += datum.strategy.template(datum.value, datum.term); - html += '
  • '; - } - return html; - }, - - _renderHeader: function (unzippedData) { - if (this.header) { - if (!this._$header) { - this._$header = $('
  • ').prependTo(this.$el); - } - var html = $.isFunction(this.header) ? this.header(unzippedData) : this.header; - this._$header.html(html); - } - }, - - _renderFooter: function (unzippedData) { - if (this.footer) { - if (!this._$footer) { - this._$footer = $('').appendTo(this.$el); - } - var html = $.isFunction(this.footer) ? this.footer(unzippedData) : this.footer; - this._$footer.html(html); - } - }, - - _renderNoResultsMessage: function (unzippedData) { - if (this.noResultsMessage) { - if (!this._$noResultsMessage) { - this._$noResultsMessage = $('
  • ').appendTo(this.$el); - } - var html = $.isFunction(this.noResultsMessage) ? this.noResultsMessage(unzippedData) : this.noResultsMessage; - this._$noResultsMessage.html(html); - } - }, - - _renderContents: function (html) { - if (this._$footer) { - this._$footer.before(html); - } else { - this.$el.append(html); - } - }, - - _fitToBottom: function() { - var windowScrollBottom = $window.scrollTop() + $window.height(); - var height = this.$el.height(); - if ((this.$el.position().top + height) > windowScrollBottom) { - this.$el.offset({top: windowScrollBottom - height}); - } - }, - - _fitToRight: function() { - // We don't know how wide our content is until the browser positions us, and at that point it clips us - // to the document width so we don't know if we would have overrun it. As a heuristic to avoid that clipping - // (which makes our elements wrap onto the next line and corrupt the next item), if we're close to the right - // edge, move left. We don't know how far to move left, so just keep nudging a bit. - var tolerance = 30; // pixels. Make wider than vertical scrollbar because we might not be able to use that space. - var lastOffset = this.$el.offset().left, offset; - var width = this.$el.width(); - var maxLeft = $window.width() - tolerance; - while (lastOffset + width > maxLeft) { - this.$el.offset({left: lastOffset - tolerance}); - offset = this.$el.offset().left; - if (offset >= lastOffset) { break; } - lastOffset = offset; - } - }, - - _applyPlacement: function (position) { - // If the 'placement' option set to 'top', move the position above the element. - if (this.placement.indexOf('top') !== -1) { - // Overwrite the position object to set the 'bottom' property instead of the top. - position = { - top: 'auto', - bottom: this.$el.parent().height() - position.top + position.lineHeight, - left: position.left - }; - } else { - position.bottom = 'auto'; - delete position.lineHeight; - } - if (this.placement.indexOf('absleft') !== -1) { - position.left = 0; - } else if (this.placement.indexOf('absright') !== -1) { - position.right = 0; - position.left = 'auto'; - } - return position; - } - }); - - $.fn.textcomplete.Dropdown = Dropdown; - $.extend($.fn.textcomplete, commands); -}(jQuery); - -+function ($) { - 'use strict'; - - // Memoize a search function. - var memoize = function (func) { - var memo = {}; - return function (term, callback) { - if (memo[term]) { - callback(memo[term]); - } else { - func.call(this, term, function (data) { - memo[term] = (memo[term] || []).concat(data); - callback.apply(null, arguments); - }); - } - }; - }; - - function Strategy(options) { - $.extend(this, options); - if (this.cache) { this.search = memoize(this.search); } - } - - Strategy.parse = function (strategiesArray, params) { - return $.map(strategiesArray, function (strategy) { - var strategyObj = new Strategy(strategy); - strategyObj.el = params.el; - strategyObj.$el = params.$el; - return strategyObj; - }); - }; - - $.extend(Strategy.prototype, { - // Public properties - // ----------------- - - // Required - match: null, - replace: null, - search: null, - - // Optional - id: null, - cache: false, - context: function () { return true; }, - index: 2, - template: function (obj) { return obj; }, - idProperty: null - }); - - $.fn.textcomplete.Strategy = Strategy; - -}(jQuery); - -+function ($) { - 'use strict'; - - var now = Date.now || function () { return new Date().getTime(); }; - - // Returns a function, that, as long as it continues to be invoked, will not - // be triggered. The function will be called after it stops being called for - // `wait` msec. - // - // This utility function was originally implemented at Underscore.js. - var debounce = function (func, wait) { - var timeout, args, context, timestamp, result; - var later = function () { - var last = now() - timestamp; - if (last < wait) { - timeout = setTimeout(later, wait - last); - } else { - timeout = null; - result = func.apply(context, args); - context = args = null; - } - }; - - return function () { - context = this; - args = arguments; - timestamp = now(); - if (!timeout) { - timeout = setTimeout(later, wait); - } - return result; - }; - }; - - function Adapter () {} - - $.extend(Adapter.prototype, { - // Public properties - // ----------------- - - id: null, // Identity. - completer: null, // Completer object which creates it. - el: null, // Textarea element. - $el: null, // jQuery object of the textarea. - option: null, - - // Public methods - // -------------- - - initialize: function (element, completer, option) { - this.el = element; - this.$el = $(element); - this.id = completer.id + this.constructor.name; - this.completer = completer; - this.option = option; - - if (this.option.debounce) { - this._onKeyup = debounce(this._onKeyup, this.option.debounce); - } - - this._bindEvents(); - }, - - destroy: function () { - this.$el.off('.' + this.id); // Remove all event handlers. - this.$el = this.el = this.completer = null; - }, - - // Update the element with the given value and strategy. - // - // value - The selected object. It is one of the item of the array - // which was callbacked from the search function. - // strategy - The Strategy associated with the selected value. - select: function (/* value, strategy */) { - throw new Error('Not implemented'); - }, - - // Returns the caret's relative coordinates from body's left top corner. - getCaretPosition: function () { - var position = this._getCaretRelativePosition(); - var offset = this.$el.offset(); - - // Calculate the left top corner of `this.option.appendTo` element. - var $parent = this.option.appendTo; - if ($parent) { - if (!($parent instanceof $)) { $parent = $($parent); } - var parentOffset = $parent.offsetParent().offset(); - offset.top -= parentOffset.top; - offset.left -= parentOffset.left; - } - - position.top += offset.top; - position.left += offset.left; - return position; - }, - - // Focus on the element. - focus: function () { - this.$el.focus(); - }, - - // Private methods - // --------------- - - _bindEvents: function () { - this.$el.on('keyup.' + this.id, $.proxy(this._onKeyup, this)); - }, - - _onKeyup: function (e) { - if (this._skipSearch(e)) { return; } - this.completer.trigger(this.getTextFromHeadToCaret(), true); - }, - - // Suppress searching if it returns true. - _skipSearch: function (clickEvent) { - switch (clickEvent.keyCode) { - case 9: // TAB - case 13: // ENTER - case 40: // DOWN - case 38: // UP - return true; - } - if (clickEvent.ctrlKey) switch (clickEvent.keyCode) { - case 78: // Ctrl-N - case 80: // Ctrl-P - return true; - } - } - }); - - $.fn.textcomplete.Adapter = Adapter; -}(jQuery); - -+function ($) { - 'use strict'; - - // Textarea adapter - // ================ - // - // Managing a textarea. It doesn't know a Dropdown. - function Textarea(element, completer, option) { - this.initialize(element, completer, option); - } - - $.extend(Textarea.prototype, $.fn.textcomplete.Adapter.prototype, { - // Public methods - // -------------- - - // Update the textarea with the given value and strategy. - select: function (value, strategy, e) { - var pre = this.getTextFromHeadToCaret(); - var post = this.el.value.substring(this.el.selectionEnd); - var newSubstr = strategy.replace(value, e); - if (typeof newSubstr !== 'undefined') { - if ($.isArray(newSubstr)) { - post = newSubstr[1] + post; - newSubstr = newSubstr[0]; - } - pre = pre.replace(strategy.match, newSubstr); - this.$el.val(pre + post); - this.el.selectionStart = this.el.selectionEnd = pre.length; - } - }, - - getTextFromHeadToCaret: function () { - return this.el.value.substring(0, this.el.selectionEnd); - }, - - // Private methods - // --------------- - - _getCaretRelativePosition: function () { - var p = $.fn.textcomplete.getCaretCoordinates(this.el, this.el.selectionStart); - return { - top: p.top + this._calculateLineHeight() - this.$el.scrollTop(), - left: p.left - this.$el.scrollLeft() - }; - }, - - _calculateLineHeight: function () { - var lineHeight = parseInt(this.$el.css('line-height'), 10); - if (isNaN(lineHeight)) { - // http://stackoverflow.com/a/4515470/1297336 - var parentNode = this.el.parentNode; - var temp = document.createElement(this.el.nodeName); - var style = this.el.style; - temp.setAttribute( - 'style', - 'margin:0px;padding:0px;font-family:' + style.fontFamily + ';font-size:' + style.fontSize - ); - temp.innerHTML = 'test'; - parentNode.appendChild(temp); - lineHeight = temp.clientHeight; - parentNode.removeChild(temp); - } - return lineHeight; - } - }); - - $.fn.textcomplete.Textarea = Textarea; -}(jQuery); - -+function ($) { - 'use strict'; - - var sentinelChar = '吶'; - - function IETextarea(element, completer, option) { - this.initialize(element, completer, option); - $('' + sentinelChar + '').css({ - position: 'absolute', - top: -9999, - left: -9999 - }).insertBefore(element); - } - - $.extend(IETextarea.prototype, $.fn.textcomplete.Textarea.prototype, { - // Public methods - // -------------- - - select: function (value, strategy, e) { - var pre = this.getTextFromHeadToCaret(); - var post = this.el.value.substring(pre.length); - var newSubstr = strategy.replace(value, e); - if (typeof newSubstr !== 'undefined') { - if ($.isArray(newSubstr)) { - post = newSubstr[1] + post; - newSubstr = newSubstr[0]; - } - pre = pre.replace(strategy.match, newSubstr); - this.$el.val(pre + post); - this.el.focus(); - var range = this.el.createTextRange(); - range.collapse(true); - range.moveEnd('character', pre.length); - range.moveStart('character', pre.length); - range.select(); - } - }, - - getTextFromHeadToCaret: function () { - this.el.focus(); - var range = document.selection.createRange(); - range.moveStart('character', -this.el.value.length); - var arr = range.text.split(sentinelChar) - return arr.length === 1 ? arr[0] : arr[1]; - } - }); - - $.fn.textcomplete.IETextarea = IETextarea; -}(jQuery); - -// NOTE: TextComplete plugin has contenteditable support but it does not work -// fine especially on old IEs. -// Any pull requests are REALLY welcome. - -+function ($) { - 'use strict'; - - // ContentEditable adapter - // ======================= - // - // Adapter for contenteditable elements. - function ContentEditable (element, completer, option) { - this.initialize(element, completer, option); - } - - $.extend(ContentEditable.prototype, $.fn.textcomplete.Adapter.prototype, { - // Public methods - // -------------- - - // Update the content with the given value and strategy. - // When an dropdown item is selected, it is executed. - select: function (value, strategy, e) { - var pre = this.getTextFromHeadToCaret(); - var sel = window.getSelection() - var range = sel.getRangeAt(0); - var selection = range.cloneRange(); - selection.selectNodeContents(range.startContainer); - var content = selection.toString(); - var post = content.substring(range.startOffset); - var newSubstr = strategy.replace(value, e); - if (typeof newSubstr !== 'undefined') { - if ($.isArray(newSubstr)) { - post = newSubstr[1] + post; - newSubstr = newSubstr[0]; - } - pre = pre.replace(strategy.match, newSubstr); - range.selectNodeContents(range.startContainer); - range.deleteContents(); - - // create temporary elements - var preWrapper = document.createElement("div"); - preWrapper.innerHTML = pre; - var postWrapper = document.createElement("div"); - postWrapper.innerHTML = post; - - // create the fragment thats inserted - var fragment = document.createDocumentFragment(); - var childNode; - var lastOfPre; - while (childNode = preWrapper.firstChild) { - lastOfPre = fragment.appendChild(childNode); - } - while (childNode = postWrapper.firstChild) { - fragment.appendChild(childNode); - } - - // insert the fragment & jump behind the last node in "pre" - range.insertNode(fragment); - range.setStartAfter(lastOfPre); - - range.collapse(true); - sel.removeAllRanges(); - sel.addRange(range); - } - }, - - // Private methods - // --------------- - - // Returns the caret's relative position from the contenteditable's - // left top corner. - // - // Examples - // - // this._getCaretRelativePosition() - // //=> { top: 18, left: 200, lineHeight: 16 } - // - // Dropdown's position will be decided using the result. - _getCaretRelativePosition: function () { - var range = window.getSelection().getRangeAt(0).cloneRange(); - var node = document.createElement('span'); - range.insertNode(node); - range.selectNodeContents(node); - range.deleteContents(); - var $node = $(node); - var position = $node.offset(); - position.left -= this.$el.offset().left; - position.top += $node.height() - this.$el.offset().top; - position.lineHeight = $node.height(); - $node.remove(); - return position; - }, - - // Returns the string between the first character and the caret. - // Completer will be triggered with the result for start autocompleting. - // - // Example - // - // // Suppose the html is 'hello wor|ld' and | is the caret. - // this.getTextFromHeadToCaret() - // // => ' wor' // not 'hello wor' - getTextFromHeadToCaret: function () { - var range = window.getSelection().getRangeAt(0); - var selection = range.cloneRange(); - selection.selectNodeContents(range.startContainer); - return selection.toString().substring(0, range.startOffset); - } - }); - - $.fn.textcomplete.ContentEditable = ContentEditable; -}(jQuery); - -// The MIT License (MIT) -// -// Copyright (c) 2015 Jonathan Ong me@jongleberry.com -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -// associated documentation files (the "Software"), to deal in the Software without restriction, -// including without limitation the rights to use, copy, modify, merge, publish, distribute, -// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT -// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -// https://github.com/component/textarea-caret-position - -(function ($) { - -// The properties that we copy into a mirrored div. -// Note that some browsers, such as Firefox, -// do not concatenate properties, i.e. padding-top, bottom etc. -> padding, -// so we have to do every single property specifically. -var properties = [ - 'direction', // RTL support - 'boxSizing', - 'width', // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does - 'height', - 'overflowX', - 'overflowY', // copy the scrollbar for IE - - 'borderTopWidth', - 'borderRightWidth', - 'borderBottomWidth', - 'borderLeftWidth', - 'borderStyle', - - 'paddingTop', - 'paddingRight', - 'paddingBottom', - 'paddingLeft', - - // https://developer.mozilla.org/en-US/docs/Web/CSS/font - 'fontStyle', - 'fontVariant', - 'fontWeight', - 'fontStretch', - 'fontSize', - 'fontSizeAdjust', - 'lineHeight', - 'fontFamily', - - 'textAlign', - 'textTransform', - 'textIndent', - 'textDecoration', // might not make a difference, but better be safe - - 'letterSpacing', - 'wordSpacing', - - 'tabSize', - 'MozTabSize' - -]; - -var isBrowser = (typeof window !== 'undefined'); -var isFirefox = (isBrowser && window.mozInnerScreenX != null); - -function getCaretCoordinates(element, position, options) { - if(!isBrowser) { - throw new Error('textarea-caret-position#getCaretCoordinates should only be called in a browser'); - } - - var debug = options && options.debug || false; - if (debug) { - var el = document.querySelector('#input-textarea-caret-position-mirror-div'); - if ( el ) { el.parentNode.removeChild(el); } - } - - // mirrored div - var div = document.createElement('div'); - div.id = 'input-textarea-caret-position-mirror-div'; - document.body.appendChild(div); - - var style = div.style; - var computed = window.getComputedStyle? getComputedStyle(element) : element.currentStyle; // currentStyle for IE < 9 - - // default textarea styles - style.whiteSpace = 'pre-wrap'; - if (element.nodeName !== 'INPUT') - style.wordWrap = 'break-word'; // only for textarea-s - - // position off-screen - style.position = 'absolute'; // required to return coordinates properly - if (!debug) - style.visibility = 'hidden'; // not 'display: none' because we want rendering - - // transfer the element's properties to the div - properties.forEach(function (prop) { - style[prop] = computed[prop]; - }); - - if (isFirefox) { - // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275 - if (element.scrollHeight > parseInt(computed.height)) - style.overflowY = 'scroll'; - } else { - style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll' - } - - div.textContent = element.value.substring(0, position); - // the second special handling for input type="text" vs textarea: spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037 - if (element.nodeName === 'INPUT') - div.textContent = div.textContent.replace(/\s/g, '\u00a0'); - - var span = document.createElement('span'); - // Wrapping must be replicated *exactly*, including when a long word gets - // onto the next line, with whitespace at the end of the line before (#7). - // The *only* reliable way to do that is to copy the *entire* rest of the - // textarea's content into the created at the caret position. - // for inputs, just '.' would be enough, but why bother? - span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all - div.appendChild(span); - - var coordinates = { - top: span.offsetTop + parseInt(computed['borderTopWidth']), - left: span.offsetLeft + parseInt(computed['borderLeftWidth']) - }; - - if (debug) { - span.style.backgroundColor = '#aaa'; - } else { - document.body.removeChild(div); - } - - return coordinates; -} - -$.fn.textcomplete.getCaretCoordinates = getCaretCoordinates; - -}(jQuery)); - -return jQuery; -})); -// @license-end diff --git a/view/js/jquery-textcomplete/jquery.textcomplete.min.js b/view/js/jquery-textcomplete/jquery.textcomplete.min.js deleted file mode 100644 index 4cdb66029..000000000 --- a/view/js/jquery-textcomplete/jquery.textcomplete.min.js +++ /dev/null @@ -1,5 +0,0 @@ -// @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt Expat -/*! jquery-textcomplete - v1.3.4 - 2016-04-19 */ -!function(a){if("function"==typeof define&&define.amd)define(["jquery"],a);else if("object"==typeof module&&module.exports){var b=require("jquery");module.exports=a(b)}else a(jQuery)}(function(a){if("undefined"==typeof a)throw new Error("jQuery.textcomplete requires jQuery");return+function(a){"use strict";var b=function(a){console.warn&&console.warn(a)},c=1;a.fn.textcomplete=function(d,e){var f=Array.prototype.slice.call(arguments);return this.each(function(){var g=this,h=a(this),i=h.data("textComplete");if(i||(e||(e={}),e._oid=c++,i=new a.fn.textcomplete.Completer(this,e),h.data("textComplete",i)),"string"==typeof d){if(!i)return;f.shift(),i[d].apply(i,f),"destroy"===d&&h.removeData("textComplete")}else a.each(d,function(c){a.each(["header","footer","placement","maxCount"],function(a){c[a]&&(i.option[a]=c[a],b(a+"as a strategy param is deprecated. Use option."),delete c[a])})}),i.register(a.fn.textcomplete.Strategy.parse(d,{el:g,$el:h}))})}}(a),+function(a){"use strict";function b(c,d){if(this.$el=a(c),this.id="textcomplete"+f++,this.strategies=[],this.views=[],this.option=a.extend({},b._getDefaults(),d),!(this.$el.is("input[type=text]")||this.$el.is("input[type=search]")||this.$el.is("textarea")||c.isContentEditable||"true"==c.contentEditable))throw new Error("textcomplete must be called on a Textarea or a ContentEditable.");if(c===document.activeElement)this.initialize();else{var e=this;this.$el.one("focus."+this.id,function(){e.initialize()})}}var c=function(a){var b,c;return function(){var d=Array.prototype.slice.call(arguments);if(b)return void(c=d);b=!0;var e=this;d.unshift(function f(){if(c){var d=c;c=void 0,d.unshift(f),a.apply(e,d)}else b=!1}),a.apply(this,d)}},d=function(a){return"[object String]"===Object.prototype.toString.call(a)},e=function(a){return"[object Function]"===Object.prototype.toString.call(a)},f=0;b._getDefaults=function(){return b.DEFAULTS||(b.DEFAULTS={appendTo:a("body"),zIndex:"100"}),b.DEFAULTS},a.extend(b.prototype,{id:null,option:null,strategies:null,adapter:null,dropdown:null,$el:null,initialize:function(){var b=this.$el.get(0);this.dropdown=new a.fn.textcomplete.Dropdown(b,this,this.option);var c,d;this.option.adapter?c=this.option.adapter:(d=this.$el.is("textarea")||this.$el.is("input[type=text]")||this.$el.is("input[type=search]")?"number"==typeof b.selectionEnd?"Textarea":"IETextarea":"ContentEditable",c=a.fn.textcomplete[d]),this.adapter=new c(b,this,this.option)},destroy:function(){this.$el.off("."+this.id),this.adapter&&this.adapter.destroy(),this.dropdown&&this.dropdown.destroy(),this.$el=this.adapter=this.dropdown=null},deactivate:function(){this.dropdown&&this.dropdown.deactivate()},trigger:function(a,b){this.dropdown||this.initialize(),null!=a||(a=this.adapter.getTextFromHeadToCaret());var c=this._extractSearchQuery(a);if(c.length){var d=c[1];if(b&&this._term===d&&""!==d)return;this._term=d,this._search.apply(this,c)}else this._term=null,this.dropdown.deactivate()},fire:function(a){var b=Array.prototype.slice.call(arguments,1);return this.$el.trigger(a,b),this},register:function(a){Array.prototype.push.apply(this.strategies,a)},select:function(a,b,c){this._term=null,this.adapter.select(a,b,c),this.fire("change").fire("textComplete:select",a,b),this.adapter.focus()},_clearAtNext:!0,_term:null,_extractSearchQuery:function(a){for(var b=0;b").addClass("dropdown-menu textcomplete-dropdown").attr("id","textcomplete-dropdown-"+b._oid).css({display:"none",left:0,position:"absolute",zIndex:b.zIndex}).appendTo(c);return d}}),a.extend(b.prototype,{$el:null,$inputEl:null,completer:null,footer:null,header:null,id:null,maxCount:10,placement:"",shown:!1,data:[],className:"",destroy:function(){this.deactivate(),this.$el.off("."+this.id),this.$inputEl.off("."+this.id),this.clear(),this.$el.remove(),this.$el=this.$inputEl=this.completer=null,delete e[this.id]},render:function(b){var c=this._buildContents(b),d=a.map(this.data,function(a){return a.value});if(this.data.length){var e=b[0].strategy;e.id?this.$el.attr("data-strategy",e.id):this.$el.removeAttr("data-strategy"),this._renderHeader(d),this._renderFooter(d),c&&(this._renderContents(c),this._fitToBottom(),this._fitToRight(),this._activateIndexedItem()),this._setScroll()}else this.noResultsMessage?this._renderNoResultsMessage(d):this.shown&&this.deactivate()},setPosition:function(b){var d="absolute";return this.$inputEl.add(this.$inputEl.parents()).each(function(){return"absolute"===a(this).css("position")?!1:"fixed"===a(this).css("position")?(b.top-=c.scrollTop(),b.left-=c.scrollLeft(),d="fixed",!1):void 0}),this.$el.css(this._applyPlacement(b)),this.$el.css({position:d}),this},clear:function(){this.$el.html(""),this.data=[],this._index=0,this._$header=this._$footer=this._$noResultsMessage=null},activate:function(){return this.shown||(this.clear(),this.$el.show(),this.className&&this.$el.addClass(this.className),this.completer.fire("textComplete:show"),this.shown=!0),this},deactivate:function(){return this.shown&&(this.$el.hide(),this.className&&this.$el.removeClass(this.className),this.completer.fire("textComplete:hide"),this.shown=!1),this},isUp:function(a){return 38===a.keyCode||a.ctrlKey&&80===a.keyCode},isDown:function(a){return 40===a.keyCode||a.ctrlKey&&78===a.keyCode},isEnter:function(a){var b=a.ctrlKey||a.altKey||a.metaKey||a.shiftKey;return!b&&(13===a.keyCode||9===a.keyCode||this.option.completeOnSpace===!0&&32===a.keyCode)},isPageup:function(a){return 33===a.keyCode},isPagedown:function(a){return 34===a.keyCode},isEscape:function(a){return 27===a.keyCode},_data:null,_index:null,_$header:null,_$noResultsMessage:null,_$footer:null,_bindEvents:function(){this.$el.on("mousedown."+this.id,".textcomplete-item",a.proxy(this._onClick,this)),this.$el.on("touchstart."+this.id,".textcomplete-item",a.proxy(this._onClick,this)),this.$el.on("mouseover."+this.id,".textcomplete-item",a.proxy(this._onMouseover,this)),this.$inputEl.on("keydown."+this.id,a.proxy(this._onKeydown,this))},_onClick:function(b){var c=a(b.target);b.preventDefault(),b.originalEvent.keepTextCompleteDropdown=this.id,c.hasClass("textcomplete-item")||(c=c.closest(".textcomplete-item"));var d=this.data[parseInt(c.data("index"),10)];this.completer.select(d.value,d.strategy,b);var e=this;setTimeout(function(){e.deactivate(),"touchstart"===b.type&&e.$inputEl.focus()},0)},_onMouseover:function(b){var c=a(b.target);b.preventDefault(),c.hasClass("textcomplete-item")||(c=c.closest(".textcomplete-item")),this._index=parseInt(c.data("index"),10),this._activateIndexedItem()},_onKeydown:function(b){if(this.shown){var c;switch(a.isFunction(this.option.onKeydown)&&(c=this.option.onKeydown(b,f)),null==c&&(c=this._defaultKeydown(b)),c){case f.KEY_UP:b.preventDefault(),this._up();break;case f.KEY_DOWN:b.preventDefault(),this._down();break;case f.KEY_ENTER:b.preventDefault(),this._enter(b);break;case f.KEY_PAGEUP:b.preventDefault(),this._pageup();break;case f.KEY_PAGEDOWN:b.preventDefault(),this._pagedown();break;case f.KEY_ESCAPE:b.preventDefault(),this.deactivate()}}},_defaultKeydown:function(a){return this.isUp(a)?f.KEY_UP:this.isDown(a)?f.KEY_DOWN:this.isEnter(a)?f.KEY_ENTER:this.isPageup(a)?f.KEY_PAGEUP:this.isPagedown(a)?f.KEY_PAGEDOWN:this.isEscape(a)?f.KEY_ESCAPE:void 0},_up:function(){0===this._index?this._index=this.data.length-1:this._index-=1,this._activateIndexedItem(),this._setScroll()},_down:function(){this._index===this.data.length-1?this._index=0:this._index+=1,this._activateIndexedItem(),this._setScroll()},_enter:function(a){var b=this.data[parseInt(this._getActiveElement().data("index"),10)];this.completer.select(b.value,b.strategy,a),this.deactivate()},_pageup:function(){var b=0,c=this._getActiveElement().position().top-this.$el.innerHeight();this.$el.children().each(function(d){return a(this).position().top+a(this).outerHeight()>c?(b=d,!1):void 0}),this._index=b,this._activateIndexedItem(),this._setScroll()},_pagedown:function(){var b=this.data.length-1,c=this._getActiveElement().position().top+this.$el.innerHeight();this.$el.children().each(function(d){return a(this).position().top>c?(b=d,!1):void 0}),this._index=b,this._activateIndexedItem(),this._setScroll()},_activateIndexedItem:function(){this.$el.find(".textcomplete-item.active").removeClass("active"),this._getActiveElement().addClass("active")},_getActiveElement:function(){return this.$el.children(".textcomplete-item:nth("+this._index+")")},_setScroll:function(){var a=this._getActiveElement(),b=a.position().top,c=a.outerHeight(),d=this.$el.innerHeight(),e=this.$el.scrollTop();0===this._index||this._index==this.data.length-1||0>b?this.$el.scrollTop(b+e):b+c>d&&this.$el.scrollTop(b+c+e-d)},_buildContents:function(a){var b,c,e,f="";for(c=0;c',f+=b.strategy.template(b.value,b.term),f+="");return f},_renderHeader:function(b){if(this.header){this._$header||(this._$header=a('
  • ').prependTo(this.$el));var c=a.isFunction(this.header)?this.header(b):this.header;this._$header.html(c)}},_renderFooter:function(b){if(this.footer){this._$footer||(this._$footer=a('').appendTo(this.$el));var c=a.isFunction(this.footer)?this.footer(b):this.footer;this._$footer.html(c)}},_renderNoResultsMessage:function(b){if(this.noResultsMessage){this._$noResultsMessage||(this._$noResultsMessage=a('
  • ').appendTo(this.$el));var c=a.isFunction(this.noResultsMessage)?this.noResultsMessage(b):this.noResultsMessage;this._$noResultsMessage.html(c)}},_renderContents:function(a){this._$footer?this._$footer.before(a):this.$el.append(a)},_fitToBottom:function(){var a=c.scrollTop()+c.height(),b=this.$el.height();this.$el.position().top+b>a&&this.$el.offset({top:a-b})},_fitToRight:function(){for(var a,b=30,d=this.$el.offset().left,e=this.$el.width(),f=c.width()-b;d+e>f&&(this.$el.offset({left:d-b}),a=this.$el.offset().left,!(a>=d));)d=a},_applyPlacement:function(a){return-1!==this.placement.indexOf("top")?a={top:"auto",bottom:this.$el.parent().height()-a.top+a.lineHeight,left:a.left}:(a.bottom="auto",delete a.lineHeight),-1!==this.placement.indexOf("absleft")?a.left=0:-1!==this.placement.indexOf("absright")&&(a.right=0,a.left="auto"),a}}),a.fn.textcomplete.Dropdown=b,a.extend(a.fn.textcomplete,f)}(a),+function(a){"use strict";function b(b){a.extend(this,b),this.cache&&(this.search=c(this.search))}var c=function(a){var b={};return function(c,d){b[c]?d(b[c]):a.call(this,c,function(a){b[c]=(b[c]||[]).concat(a),d.apply(null,arguments)})}};b.parse=function(c,d){return a.map(c,function(a){var c=new b(a);return c.el=d.el,c.$el=d.$el,c})},a.extend(b.prototype,{match:null,replace:null,search:null,id:null,cache:!1,context:function(){return!0},index:2,template:function(a){return a},idProperty:null}),a.fn.textcomplete.Strategy=b}(a),+function(a){"use strict";function b(){}var c=Date.now||function(){return(new Date).getTime()},d=function(a,b){var d,e,f,g,h,i=function(){var j=c()-g;b>j?d=setTimeout(i,b-j):(d=null,h=a.apply(f,e),f=e=null)};return function(){return f=this,e=arguments,g=c(),d||(d=setTimeout(i,b)),h}};a.extend(b.prototype,{id:null,completer:null,el:null,$el:null,option:null,initialize:function(b,c,e){this.el=b,this.$el=a(b),this.id=c.id+this.constructor.name,this.completer=c,this.option=e,this.option.debounce&&(this._onKeyup=d(this._onKeyup,this.option.debounce)),this._bindEvents()},destroy:function(){this.$el.off("."+this.id),this.$el=this.el=this.completer=null},select:function(){throw new Error("Not implemented")},getCaretPosition:function(){var b=this._getCaretRelativePosition(),c=this.$el.offset(),d=this.option.appendTo;if(d){d instanceof a||(d=a(d));var e=d.offsetParent().offset();c.top-=e.top,c.left-=e.left}return b.top+=c.top,b.left+=c.left,b},focus:function(){this.$el.focus()},_bindEvents:function(){this.$el.on("keyup."+this.id,a.proxy(this._onKeyup,this))},_onKeyup:function(a){this._skipSearch(a)||this.completer.trigger(this.getTextFromHeadToCaret(),!0)},_skipSearch:function(a){switch(a.keyCode){case 9:case 13:case 40:case 38:return!0}if(a.ctrlKey)switch(a.keyCode){case 78:case 80:return!0}}}),a.fn.textcomplete.Adapter=b}(a),+function(a){"use strict";function b(a,b,c){this.initialize(a,b,c)}a.extend(b.prototype,a.fn.textcomplete.Adapter.prototype,{select:function(b,c,d){var e=this.getTextFromHeadToCaret(),f=this.el.value.substring(this.el.selectionEnd),g=c.replace(b,d);"undefined"!=typeof g&&(a.isArray(g)&&(f=g[1]+f,g=g[0]),e=e.replace(c.match,g),this.$el.val(e+f),this.el.selectionStart=this.el.selectionEnd=e.length)},getTextFromHeadToCaret:function(){return this.el.value.substring(0,this.el.selectionEnd)},_getCaretRelativePosition:function(){var b=a.fn.textcomplete.getCaretCoordinates(this.el,this.el.selectionStart);return{top:b.top+this._calculateLineHeight()-this.$el.scrollTop(),left:b.left-this.$el.scrollLeft()}},_calculateLineHeight:function(){var a=parseInt(this.$el.css("line-height"),10);if(isNaN(a)){var b=this.el.parentNode,c=document.createElement(this.el.nodeName),d=this.el.style;c.setAttribute("style","margin:0px;padding:0px;font-family:"+d.fontFamily+";font-size:"+d.fontSize),c.innerHTML="test",b.appendChild(c),a=c.clientHeight,b.removeChild(c)}return a}}),a.fn.textcomplete.Textarea=b}(a),+function(a){"use strict";function b(b,d,e){this.initialize(b,d,e),a(""+c+"").css({position:"absolute",top:-9999,left:-9999}).insertBefore(b)}var c="吶";a.extend(b.prototype,a.fn.textcomplete.Textarea.prototype,{select:function(b,c,d){var e=this.getTextFromHeadToCaret(),f=this.el.value.substring(e.length),g=c.replace(b,d);if("undefined"!=typeof g){a.isArray(g)&&(f=g[1]+f,g=g[0]),e=e.replace(c.match,g),this.$el.val(e+f),this.el.focus();var h=this.el.createTextRange();h.collapse(!0),h.moveEnd("character",e.length),h.moveStart("character",e.length),h.select()}},getTextFromHeadToCaret:function(){this.el.focus();var a=document.selection.createRange();a.moveStart("character",-this.el.value.length);var b=a.text.split(c);return 1===b.length?b[0]:b[1]}}),a.fn.textcomplete.IETextarea=b}(a),+function(a){"use strict";function b(a,b,c){this.initialize(a,b,c)}a.extend(b.prototype,a.fn.textcomplete.Adapter.prototype,{select:function(b,c,d){var e=this.getTextFromHeadToCaret(),f=window.getSelection(),g=f.getRangeAt(0),h=g.cloneRange();h.selectNodeContents(g.startContainer);var i=h.toString(),j=i.substring(g.startOffset),k=c.replace(b,d);if("undefined"!=typeof k){a.isArray(k)&&(j=k[1]+j,k=k[0]),e=e.replace(c.match,k),g.selectNodeContents(g.startContainer),g.deleteContents();var l=document.createElement("div");l.innerHTML=e;var m=document.createElement("div");m.innerHTML=j;for(var n,o,p=document.createDocumentFragment();n=l.firstChild;)o=p.appendChild(n);for(;n=m.firstChild;)p.appendChild(n);g.insertNode(p),g.setStartAfter(o),g.collapse(!0),f.removeAllRanges(),f.addRange(g)}},_getCaretRelativePosition:function(){var b=window.getSelection().getRangeAt(0).cloneRange(),c=document.createElement("span");b.insertNode(c),b.selectNodeContents(c),b.deleteContents();var d=a(c),e=d.offset();return e.left-=this.$el.offset().left,e.top+=d.height()-this.$el.offset().top,e.lineHeight=d.height(),d.remove(),e},getTextFromHeadToCaret:function(){var a=window.getSelection().getRangeAt(0),b=a.cloneRange();return b.selectNodeContents(a.startContainer),b.toString().substring(0,a.startOffset)}}),a.fn.textcomplete.ContentEditable=b}(a),function(a){function b(a,b,f){if(!d)throw new Error("textarea-caret-position#getCaretCoordinates should only be called in a browser");var g=f&&f.debug||!1;if(g){var h=document.querySelector("#input-textarea-caret-position-mirror-div");h&&h.parentNode.removeChild(h)}var i=document.createElement("div");i.id="input-textarea-caret-position-mirror-div",document.body.appendChild(i);var j=i.style,k=window.getComputedStyle?getComputedStyle(a):a.currentStyle;j.whiteSpace="pre-wrap","INPUT"!==a.nodeName&&(j.wordWrap="break-word"),j.position="absolute",g||(j.visibility="hidden"),c.forEach(function(a){j[a]=k[a]}),e?a.scrollHeight>parseInt(k.height)&&(j.overflowY="scroll"):j.overflow="hidden",i.textContent=a.value.substring(0,b),"INPUT"===a.nodeName&&(i.textContent=i.textContent.replace(/\s/g," "));var l=document.createElement("span");l.textContent=a.value.substring(b)||".",i.appendChild(l);var m={top:l.offsetTop+parseInt(k.borderTopWidth),left:l.offsetLeft+parseInt(k.borderLeftWidth)};return g?l.style.backgroundColor="#aaa":document.body.removeChild(i),m}var c=["direction","boxSizing","width","height","overflowX","overflowY","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderStyle","paddingTop","paddingRight","paddingBottom","paddingLeft","fontStyle","fontVariant","fontWeight","fontStretch","fontSize","fontSizeAdjust","lineHeight","fontFamily","textAlign","textTransform","textIndent","textDecoration","letterSpacing","wordSpacing","tabSize","MozTabSize"],d="undefined"!=typeof window,e=d&&null!=window.mozInnerScreenX;a.fn.textcomplete.getCaretCoordinates=b}(a),a}); -//# sourceMappingURL=dist/jquery.textcomplete.min.map -// @license-end diff --git a/view/js/jquery-textcomplete/jquery.textcomplete.min.map b/view/js/jquery-textcomplete/jquery.textcomplete.min.map deleted file mode 100644 index e27ef4d40..000000000 --- a/view/js/jquery-textcomplete/jquery.textcomplete.min.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"dist/jquery.textcomplete.min.js","sources":["dist/jquery.textcomplete.js"],"names":["factory","define","amd","module","exports","$","require","jQuery","Error","warn","message","console","id","fn","textcomplete","strategies","option","args","Array","prototype","slice","call","arguments","this","each","self","$this","completer","data","_oid","Completer","shift","apply","removeData","obj","name","register","Strategy","parse","el","$el","element","uniqueId","views","extend","_getDefaults","is","isContentEditable","contentEditable","document","activeElement","initialize","one","lock","func","locked","queuedArgsToReplay","unshift","replayOrFree","replayArgs","undefined","isString","Object","toString","isFunction","DEFAULTS","appendTo","zIndex","adapter","dropdown","get","Dropdown","Adapter","viewName","selectionEnd","destroy","off","deactivate","trigger","text","skipUnchangedTerm","getTextFromHeadToCaret","searchQuery","_extractSearchQuery","length","term","_term","_search","fire","eventName","push","select","value","strategy","e","focus","_clearAtNext","i","context","matchRegexp","match","index","free","search","stillSearching","shown","activate","clear","setPosition","getCaretPosition","render","_zip","map","createElement","_data","$inputEl","listPosition","height","_i","_bindEvents","dropdownViews","$window","window","include","zippedData","datum","elem","idProperty","on","originalEvent","keepTextCompleteDropdown","key","view","commands","SKIP_DEFAULT","KEY_UP","KEY_DOWN","KEY_ENTER","KEY_PAGEUP","KEY_PAGEDOWN","KEY_ESCAPE","$parent","addClass","attr","css","display","left","position","footer","header","maxCount","placement","className","remove","contentsHtml","_buildContents","unzippedData","d","removeAttr","_renderHeader","_renderFooter","_renderContents","_fitToBottom","_fitToRight","_activateIndexedItem","_setScroll","noResultsMessage","_renderNoResultsMessage","pos","add","parents","top","scrollTop","scrollLeft","_applyPlacement","html","_index","_$header","_$footer","_$noResultsMessage","show","hide","removeClass","isUp","keyCode","ctrlKey","isDown","isEnter","modifiers","altKey","metaKey","shiftKey","completeOnSpace","isPageup","isPagedown","isEscape","proxy","_onClick","_onMouseover","_onKeydown","target","preventDefault","hasClass","closest","parseInt","setTimeout","type","command","onKeydown","_defaultKeydown","_up","_down","_enter","_pageup","_pagedown","_getActiveElement","threshold","innerHeight","children","outerHeight","find","$activeEl","itemTop","itemHeight","visibleHeight","visibleTop","template","prependTo","before","append","windowScrollBottom","offset","tolerance","lastOffset","width","maxLeft","indexOf","bottom","parent","lineHeight","right","options","cache","memoize","memo","callback","concat","strategiesArray","params","strategyObj","replace","now","Date","getTime","debounce","wait","timeout","timestamp","result","later","last","constructor","_onKeyup","_getCaretRelativePosition","parentOffset","offsetParent","_skipSearch","clickEvent","Textarea","pre","post","substring","newSubstr","isArray","val","selectionStart","p","getCaretCoordinates","_calculateLineHeight","isNaN","parentNode","temp","nodeName","style","setAttribute","fontFamily","fontSize","innerHTML","appendChild","clientHeight","removeChild","IETextarea","sentinelChar","insertBefore","range","createTextRange","collapse","moveEnd","moveStart","selection","createRange","arr","split","ContentEditable","sel","getSelection","getRangeAt","cloneRange","selectNodeContents","startContainer","content","startOffset","deleteContents","preWrapper","postWrapper","childNode","lastOfPre","fragment","createDocumentFragment","firstChild","insertNode","setStartAfter","removeAllRanges","addRange","node","$node","isBrowser","debug","querySelector","div","body","computed","getComputedStyle","currentStyle","whiteSpace","wordWrap","visibility","properties","forEach","prop","isFirefox","scrollHeight","overflowY","overflow","textContent","span","coordinates","offsetTop","offsetLeft","backgroundColor","mozInnerScreenX"],"mappings":";CAAC,SAAUA,GACT,GAAsB,kBAAXC,SAAyBA,OAAOC,IAEzCD,QAAQ,UAAWD,OACd,IAAsB,gBAAXG,SAAuBA,OAAOC,QAAS,CACvD,GAAIC,GAAIC,QAAQ,SAChBH,QAAOC,QAAUJ,EAAQK,OAGzBL,GAAQO,SAEV,SAAUA,GAUZ,GAAsB,mBAAXA,GACT,KAAM,IAAIC,OAAM,sCAi2ClB,QA91CC,SAAUH,GACT,YAEA,IAAII,GAAO,SAAUC,GACfC,QAAQF,MAAQE,QAAQF,KAAKC,IAG/BE,EAAK,CAETP,GAAEQ,GAAGC,aAAe,SAAUC,EAAYC,GACxC,GAAIC,GAAOC,MAAMC,UAAUC,MAAMC,KAAKC,UACtC,OAAOC,MAAKC,KAAK,WACf,GAAIC,GAAOF,KACPG,EAAQrB,EAAEkB,MACVI,EAAYD,EAAME,KAAK,eAO3B,IANKD,IACHX,IAAWA,MACXA,EAAOa,KAAOjB,IACde,EAAY,GAAItB,GAAEQ,GAAGC,aAAagB,UAAUP,KAAMP,GAClDU,EAAME,KAAK,eAAgBD,IAEH,gBAAfZ,GAAyB,CAClC,IAAKY,EAAW,MAChBV,GAAKc,QACLJ,EAAUZ,GAAYiB,MAAML,EAAWV,GACpB,YAAfF,GACFW,EAAMO,WAAW,oBAKnB5B,GAAEmB,KAAKT,EAAY,SAAUmB,GAC3B7B,EAAEmB,MAAM,SAAU,SAAU,YAAa,YAAa,SAAUW,GAC1DD,EAAIC,KACNR,EAAUX,OAAOmB,GAAQD,EAAIC,GAC7B1B,EAAK0B,EAAO,wDACLD,GAAIC,QAIjBR,EAAUS,SAAS/B,EAAEQ,GAAGC,aAAauB,SAASC,MAAMvB,GAClDwB,GAAId,EACJe,IAAKd,SAMbnB,IAED,SAAUF,GACT,YAoEA,SAASyB,GAAUW,EAASzB,GAO1B,GANAO,KAAKiB,IAAanC,EAAEoC,GACpBlB,KAAKX,GAAa,eAAiB8B,IACnCnB,KAAKR,cACLQ,KAAKoB,SACLpB,KAAKP,OAAaX,EAAEuC,UAAWd,EAAUe,eAAgB7B,KAEpDO,KAAKiB,IAAIM,GAAG,qBAAwBvB,KAAKiB,IAAIM,GAAG,uBAA0BvB,KAAKiB,IAAIM,GAAG,aAAgBL,EAAQM,mBAAgD,QAA3BN,EAAQO,iBAC9I,KAAM,IAAIxC,OAAM,kEAGlB,IAAIiC,IAAYQ,SAASC,cAEvB3B,KAAK4B,iBACA,CAEL,GAAI1B,GAAOF,IACXA,MAAKiB,IAAIY,IAAI,SAAW7B,KAAKX,GAAI,WAAca,EAAK0B,gBA7DxD,GAAIE,GAAO,SAAUC,GACnB,GAAIC,GAAQC,CAEZ,OAAO,YAEL,GAAIvC,GAAOC,MAAMC,UAAUC,MAAMC,KAAKC,UACtC,IAAIiC,EAKF,YADAC,EAAqBvC,EAGvBsC,IAAS,CACT,IAAI9B,GAAOF,IACXN,GAAKwC,QAAQ,QAASC,KACpB,GAAIF,EAAoB,CAMtB,GAAIG,GAAaH,CACjBA,GAAqBI,OACrBD,EAAWF,QAAQC,GACnBJ,EAAKtB,MAAMP,EAAMkC,OAEjBJ,IAAS,IAGbD,EAAKtB,MAAMT,KAAMN,KAIjB4C,EAAW,SAAU3B,GACvB,MAA+C,oBAAxC4B,OAAO3C,UAAU4C,SAAS1C,KAAKa,IAGpC8B,EAAa,SAAU9B,GACzB,MAA+C,sBAAxC4B,OAAO3C,UAAU4C,SAAS1C,KAAKa,IAGpCQ,EAAW,CAuBfZ,GAAUe,aAAe,WAQvB,MAPKf,GAAUmC,WACbnC,EAAUmC,UACRC,SAAU7D,EAAE,QACZ8D,OAAQ,QAILrC,EAAUmC,UAGnB5D,EAAEuC,OAAOd,EAAUX,WAIjBP,GAAY,KACZI,OAAY,KACZD,WAAY,KACZqD,QAAY,KACZC,SAAY,KACZ7B,IAAY,KAKZW,WAAY,WACV,GAAIV,GAAUlB,KAAKiB,IAAI8B,IAAI,EAE3B/C,MAAK8C,SAAW,GAAIhE,GAAEQ,GAAGC,aAAayD,SAAS9B,EAASlB,KAAMA,KAAKP,OACnE,IAAIwD,GAASC,CACTlD,MAAKP,OAAOoD,QACdI,EAAUjD,KAAKP,OAAOoD,SAGpBK,EADElD,KAAKiB,IAAIM,GAAG,aAAevB,KAAKiB,IAAIM,GAAG,qBAAuBvB,KAAKiB,IAAIM,GAAG,sBACjC,gBAAzBL,GAAQiC,aAA4B,WAAa,aAExD,kBAEbF,EAAUnE,EAAEQ,GAAGC,aAAa2D,IAE9BlD,KAAK6C,QAAU,GAAII,GAAQ/B,EAASlB,KAAMA,KAAKP,SAGjD2D,QAAS,WACPpD,KAAKiB,IAAIoC,IAAI,IAAMrD,KAAKX,IACpBW,KAAK6C,SACP7C,KAAK6C,QAAQO,UAEXpD,KAAK8C,UACP9C,KAAK8C,SAASM,UAEhBpD,KAAKiB,IAAMjB,KAAK6C,QAAU7C,KAAK8C,SAAW,MAG5CQ,WAAY,WACNtD,KAAK8C,UACP9C,KAAK8C,SAASQ,cAKlBC,QAAS,SAAUC,EAAMC,GAClBzD,KAAK8C,UAAY9C,KAAK4B,aACnB,MAAR4B,IAAiBA,EAAOxD,KAAK6C,QAAQa,yBACrC,IAAIC,GAAc3D,KAAK4D,oBAAoBJ,EAC3C,IAAIG,EAAYE,OAAQ,CACtB,GAAIC,GAAOH,EAAY,EAEvB,IAAIF,GAAqBzD,KAAK+D,QAAUD,GAAiB,KAATA,EAAe,MAC/D9D,MAAK+D,MAAQD,EACb9D,KAAKgE,QAAQvD,MAAMT,KAAM2D,OAEzB3D,MAAK+D,MAAQ,KACb/D,KAAK8C,SAASQ,cAIlBW,KAAM,SAAUC,GACd,GAAIxE,GAAOC,MAAMC,UAAUC,MAAMC,KAAKC,UAAW,EAEjD,OADAC,MAAKiB,IAAIsC,QAAQW,EAAWxE,GACrBM,MAGTa,SAAU,SAAUrB,GAClBG,MAAMC,UAAUuE,KAAK1D,MAAMT,KAAKR,WAAYA,IAS9C4E,OAAQ,SAAUC,EAAOC,EAAUC,GACjCvE,KAAK+D,MAAQ,KACb/D,KAAK6C,QAAQuB,OAAOC,EAAOC,EAAUC,GACrCvE,KAAKiE,KAAK,UAAUA,KAAK,sBAAuBI,EAAOC,GACvDtE,KAAK6C,QAAQ2B,SAMfC,cAAc,EACdV,MAAc,KASdH,oBAAqB,SAAUJ,GAC7B,IAAK,GAAIkB,GAAI,EAAGA,EAAI1E,KAAKR,WAAWqE,OAAQa,IAAK,CAC/C,GAAIJ,GAAWtE,KAAKR,WAAWkF,GAC3BC,EAAUL,EAASK,QAAQnB,EAC/B,IAAImB,GAAuB,KAAZA,EAAgB,CAC7B,GAAIC,GAAcnC,EAAW6B,EAASO,OAASP,EAASO,MAAMrB,GAAQc,EAASO,KAC3EvC,GAASqC,KAAYnB,EAAOmB,EAChC,IAAIE,GAAQrB,EAAKqB,MAAMD,EACvB,IAAIC,EAAS,OAAQP,EAAUO,EAAMP,EAASQ,OAAQD,IAG1D,UAIFb,QAASlC,EAAK,SAAUiD,EAAMT,EAAUR,EAAMe,GAC5C,GAAI3E,GAAOF,IACXsE,GAASU,OAAOlB,EAAM,SAAUzD,EAAM4E,GAC/B/E,EAAK4C,SAASoC,OACjBhF,EAAK4C,SAASqC,WAEZjF,EAAKuE,eAEPvE,EAAK4C,SAASsC,QACdlF,EAAKuE,cAAe,GAEtBvE,EAAK4C,SAASuC,YAAYnF,EAAK2C,QAAQyC,oBACvCpF,EAAK4C,SAASyC,OAAOrF,EAAKsF,KAAKnF,EAAMiE,EAAUR,IAC1CmB,IAEHF,IACA7E,EAAKuE,cAAe,IAErBI,KASLW,KAAM,SAAUnF,EAAMiE,EAAUR,GAC9B,MAAOhF,GAAE2G,IAAIpF,EAAM,SAAUgE,GAC3B,OAASA,MAAOA,EAAOC,SAAUA,EAAUR,KAAMA,QAKvDhF,EAAEQ,GAAGC,aAAagB,UAAYA,GAC9BvB,IAED,SAAUF,GACT,YA2CA,SAASkE,GAAS9B,EAASd,EAAWX,GACpCO,KAAKiB,IAAY+B,EAAS0C,cAAcjG,GACxCO,KAAKI,UAAYA,EACjBJ,KAAKX,GAAYe,EAAUf,GAAK,WAChCW,KAAK2F,SACL3F,KAAK4F,SAAY9G,EAAEoC,GACnBlB,KAAKP,OAAYA,EAGbA,EAAOoG,eAAgB7F,KAAKqF,YAAc5F,EAAOoG,cACjDpG,EAAOqG,QAAU9F,KAAKiB,IAAI6E,OAAOrG,EAAOqG,OAC5C,IAAI5F,GAAOF,IACXlB,GAAEmB,MAAM,WAAY,YAAa,SAAU,SAAU,mBAAoB,aAAc,SAAU8F,EAAInF,GAC/E,MAAhBnB,EAAOmB,KAAiBV,EAAKU,GAAQnB,EAAOmB,MAElDZ,KAAKgG,YAAY9E,GACjB+E,EAAcjG,KAAKX,IAAMW,KAzD3B,GAAIkG,GAAUpH,EAAEqH,QAEZC,EAAU,SAAUC,EAAYC,GAClC,GAAI5B,GAAG6B,EACHC,EAAaF,EAAMhC,SAASkC,UAChC,KAAK9B,EAAI,EAAGA,EAAI2B,EAAWxC,OAAQa,IAEjC,GADA6B,EAAOF,EAAW3B,GACd6B,EAAKjC,WAAagC,EAAMhC,SAC5B,GAAIkC,GACF,GAAID,EAAKlC,MAAMmC,KAAgBF,EAAMjC,MAAMmC,GAAa,OAAO,MAE/D,IAAID,EAAKlC,QAAUiC,EAAMjC,MAAO,OAAO,CAG3C,QAAO,GAGL4B,IACJnH,GAAE4C,UAAU+E,GAAG,QAAS,SAAUlC,GAChC,GAAIlF,GAAKkF,EAAEmC,eAAiBnC,EAAEmC,cAAcC,wBAC5C7H,GAAEmB,KAAKgG,EAAe,SAAUW,EAAKC,GAC/BD,IAAQvH,GAAMwH,EAAKvD,gBAI3B,IAAIwD,IACFC,aAAc,EACdC,OAAQ,EACRC,SAAU,EACVC,UAAW,EACXC,WAAY,EACZC,aAAc,EACdC,WAAY,EA4BdvI,GAAEuC,OAAO2B,GAIP0C,cAAe,SAAUjG,GACvB,GAAI6H,GAAU7H,EAAOkD,QACf2E,aAAmBxI,KAAMwI,EAAUxI,EAAEwI,GAC3C,IAAIrG,GAAMnC,EAAE,aACTyI,SAAS,uCACTC,KAAK,KAAM,yBAA2B/H,EAAOa,MAC7CmH,KACCC,QAAS,OACTC,KAAM,EACNC,SAAU,WACVhF,OAAQnD,EAAOmD,SAEhBD,SAAS2E,EACZ,OAAOrG,MAIXnC,EAAEuC,OAAO2B,EAASpD,WAIhBqB,IAAW,KACX2E,SAAW,KACXxF,UAAW,KACXyH,OAAW,KACXC,OAAW,KACXzI,GAAW,KACX0I,SAAW,GACXC,UAAW,GACX9C,OAAW,EACX7E,QACA4H,UAAW,GAKX7E,QAAS,WAEPpD,KAAKsD,aAELtD,KAAKiB,IAAIoC,IAAI,IAAMrD,KAAKX,IACxBW,KAAK4F,SAASvC,IAAI,IAAMrD,KAAKX,IAC7BW,KAAKoF,QACLpF,KAAKiB,IAAIiH,SACTlI,KAAKiB,IAAMjB,KAAK4F,SAAW5F,KAAKI,UAAY,WACrC6F,GAAcjG,KAAKX,KAG5BkG,OAAQ,SAAUc,GAChB,GAAI8B,GAAenI,KAAKoI,eAAe/B,GACnCgC,EAAevJ,EAAE2G,IAAIzF,KAAKK,KAAM,SAAUiI,GAAK,MAAOA,GAAEjE,OAC5D,IAAIrE,KAAKK,KAAKwD,OAAQ,CACpB,GAAIS,GAAW+B,EAAW,GAAG/B,QACzBA,GAASjF,GACXW,KAAKiB,IAAIuG,KAAK,gBAAiBlD,EAASjF,IAExCW,KAAKiB,IAAIsH,WAAW,iBAEtBvI,KAAKwI,cAAcH,GACnBrI,KAAKyI,cAAcJ,GACfF,IACFnI,KAAK0I,gBAAgBP,GACrBnI,KAAK2I,eACL3I,KAAK4I,cACL5I,KAAK6I,wBAEP7I,KAAK8I,iBACI9I,MAAK+I,iBACd/I,KAAKgJ,wBAAwBX,GACpBrI,KAAKkF,OACdlF,KAAKsD,cAIT+B,YAAa,SAAU4D,GAIrB,GAAIrB,GAAW,UAef,OAbA5H,MAAK4F,SAASsD,IAAIlJ,KAAK4F,SAASuD,WAAWlJ,KAAK,WAC9C,MAA+B,aAA5BnB,EAAEkB,MAAMyH,IAAI,aACN,EACsB,UAA5B3I,EAAEkB,MAAMyH,IAAI,aACbwB,EAAIG,KAAOlD,EAAQmD,YACnBJ,EAAItB,MAAQzB,EAAQoD,aACpB1B,EAAW,SACJ,GAJT,SAOF5H,KAAKiB,IAAIwG,IAAIzH,KAAKuJ,gBAAgBN,IAClCjJ,KAAKiB,IAAIwG,KAAMG,SAAUA,IAElB5H,MAGToF,MAAO,WACLpF,KAAKiB,IAAIuI,KAAK,IACdxJ,KAAKK,QACLL,KAAKyJ,OAAS,EACdzJ,KAAK0J,SAAW1J,KAAK2J,SAAW3J,KAAK4J,mBAAqB,MAG5DzE,SAAU,WAQR,MAPKnF,MAAKkF,QACRlF,KAAKoF,QACLpF,KAAKiB,IAAI4I,OACL7J,KAAKiI,WAAajI,KAAKiB,IAAIsG,SAASvH,KAAKiI,WAC7CjI,KAAKI,UAAU6D,KAAK,qBACpBjE,KAAKkF,OAAQ,GAERlF,MAGTsD,WAAY,WAOV,MANItD,MAAKkF,QACPlF,KAAKiB,IAAI6I,OACL9J,KAAKiI,WAAajI,KAAKiB,IAAI8I,YAAY/J,KAAKiI,WAChDjI,KAAKI,UAAU6D,KAAK,qBACpBjE,KAAKkF,OAAQ,GAERlF,MAGTgK,KAAM,SAAUzF,GACd,MAAqB,MAAdA,EAAE0F,SAAmB1F,EAAE2F,SAAyB,KAAd3F,EAAE0F,SAG7CE,OAAQ,SAAU5F,GAChB,MAAqB,MAAdA,EAAE0F,SAAmB1F,EAAE2F,SAAyB,KAAd3F,EAAE0F,SAG7CG,QAAS,SAAU7F,GACjB,GAAI8F,GAAY9F,EAAE2F,SAAW3F,EAAE+F,QAAU/F,EAAEgG,SAAWhG,EAAEiG,QACxD,QAAQH,IAA4B,KAAd9F,EAAE0F,SAAgC,IAAd1F,EAAE0F,SAAkBjK,KAAKP,OAAOgL,mBAAoB,GAAsB,KAAdlG,EAAE0F,UAG1GS,SAAU,SAAUnG,GAClB,MAAqB,MAAdA,EAAE0F,SAGXU,WAAY,SAAUpG,GACpB,MAAqB,MAAdA,EAAE0F,SAGXW,SAAU,SAAUrG,GAClB,MAAqB,MAAdA,EAAE0F,SAMXtE,MAAU,KACV8D,OAAU,KACVC,SAAU,KACVE,mBAAoB,KACpBD,SAAU,KAKV3D,YAAa,WACXhG,KAAKiB,IAAIwF,GAAG,aAAezG,KAAKX,GAAI,qBAAsBP,EAAE+L,MAAM7K,KAAK8K,SAAU9K,OACjFA,KAAKiB,IAAIwF,GAAG,cAAgBzG,KAAKX,GAAI,qBAAsBP,EAAE+L,MAAM7K,KAAK8K,SAAU9K,OAClFA,KAAKiB,IAAIwF,GAAG,aAAezG,KAAKX,GAAI,qBAAsBP,EAAE+L,MAAM7K,KAAK+K,aAAc/K,OACrFA,KAAK4F,SAASa,GAAG,WAAazG,KAAKX,GAAIP,EAAE+L,MAAM7K,KAAKgL,WAAYhL,QAGlE8K,SAAU,SAAUvG,GAClB,GAAItD,GAAMnC,EAAEyF,EAAE0G,OACd1G,GAAE2G,iBACF3G,EAAEmC,cAAcC,yBAA2B3G,KAAKX,GAC3C4B,EAAIkK,SAAS,uBAChBlK,EAAMA,EAAImK,QAAQ,sBAEpB,IAAI9E,GAAQtG,KAAKK,KAAKgL,SAASpK,EAAIZ,KAAK,SAAU,IAClDL,MAAKI,UAAUgE,OAAOkC,EAAMjC,MAAOiC,EAAMhC,SAAUC,EACnD,IAAIrE,GAAOF,IAGXsL,YAAW,WACTpL,EAAKoD,aACU,eAAXiB,EAAEgH,MACJrL,EAAK0F,SAASpB,SAEf,IAILuG,aAAc,SAAUxG,GACtB,GAAItD,GAAMnC,EAAEyF,EAAE0G,OACd1G,GAAE2G,iBACGjK,EAAIkK,SAAS,uBAChBlK,EAAMA,EAAImK,QAAQ,uBAEpBpL,KAAKyJ,OAAS4B,SAASpK,EAAIZ,KAAK,SAAU,IAC1CL,KAAK6I,wBAGPmC,WAAY,SAAUzG,GACpB,GAAKvE,KAAKkF,MAAV,CAEA,GAAIsG,EAUJ,QARI1M,EAAE2D,WAAWzC,KAAKP,OAAOgM,aAC3BD,EAAUxL,KAAKP,OAAOgM,UAAUlH,EAAGuC,IAGtB,MAAX0E,IACFA,EAAUxL,KAAK0L,gBAAgBnH,IAGzBiH,GACN,IAAK1E,GAASE,OACZzC,EAAE2G,iBACFlL,KAAK2L,KACL,MACF,KAAK7E,GAASG,SACZ1C,EAAE2G,iBACFlL,KAAK4L,OACL,MACF,KAAK9E,GAASI,UACZ3C,EAAE2G,iBACFlL,KAAK6L,OAAOtH,EACZ,MACF,KAAKuC,GAASK,WACZ5C,EAAE2G,iBACFlL,KAAK8L,SACL,MACF,KAAKhF,GAASM,aACZ7C,EAAE2G,iBACFlL,KAAK+L,WACL,MACF,KAAKjF,GAASO,WACZ9C,EAAE2G,iBACFlL,KAAKsD,gBAKXoI,gBAAiB,SAAUnH,GACzB,MAAIvE,MAAKgK,KAAKzF,GACLuC,EAASE,OACPhH,KAAKmK,OAAO5F,GACduC,EAASG,SACPjH,KAAKoK,QAAQ7F,GACfuC,EAASI,UACPlH,KAAK0K,SAASnG,GAChBuC,EAASK,WACPnH,KAAK2K,WAAWpG,GAClBuC,EAASM,aACPpH,KAAK4K,SAASrG,GAChBuC,EAASO,WADX,QAKTsE,IAAK,WACiB,IAAhB3L,KAAKyJ,OACPzJ,KAAKyJ,OAASzJ,KAAKK,KAAKwD,OAAS,EAEjC7D,KAAKyJ,QAAU,EAEjBzJ,KAAK6I,uBACL7I,KAAK8I,cAGP8C,MAAO,WACD5L,KAAKyJ,SAAWzJ,KAAKK,KAAKwD,OAAS,EACrC7D,KAAKyJ,OAAS,EAEdzJ,KAAKyJ,QAAU,EAEjBzJ,KAAK6I,uBACL7I,KAAK8I,cAGP+C,OAAQ,SAAUtH,GAChB,GAAI+B,GAAQtG,KAAKK,KAAKgL,SAASrL,KAAKgM,oBAAoB3L,KAAK,SAAU,IACvEL,MAAKI,UAAUgE,OAAOkC,EAAMjC,MAAOiC,EAAMhC,SAAUC,GACnDvE,KAAKsD,cAGPwI,QAAS,WACP,GAAIb,GAAS,EACTgB,EAAYjM,KAAKgM,oBAAoBpE,WAAWwB,IAAMpJ,KAAKiB,IAAIiL,aACnElM,MAAKiB,IAAIkL,WAAWlM,KAAK,SAAUyE,GACjC,MAAI5F,GAAEkB,MAAM4H,WAAWwB,IAAMtK,EAAEkB,MAAMoM,cAAgBH,GACnDhB,EAASvG,GACF,GAFT,SAKF1E,KAAKyJ,OAASwB,EACdjL,KAAK6I,uBACL7I,KAAK8I,cAGPiD,UAAW,WACT,GAAId,GAASjL,KAAKK,KAAKwD,OAAS,EAC5BoI,EAAYjM,KAAKgM,oBAAoBpE,WAAWwB,IAAMpJ,KAAKiB,IAAIiL,aACnElM,MAAKiB,IAAIkL,WAAWlM,KAAK,SAAUyE,GACjC,MAAI5F,GAAEkB,MAAM4H,WAAWwB,IAAM6C,GAC3BhB,EAASvG,GACF,GAFT,SAKF1E,KAAKyJ,OAASwB,EACdjL,KAAK6I,uBACL7I,KAAK8I,cAGPD,qBAAsB,WACpB7I,KAAKiB,IAAIoL,KAAK,6BAA6BtC,YAAY,UACvD/J,KAAKgM,oBAAoBzE,SAAS,WAGpCyE,kBAAmB,WACjB,MAAOhM,MAAKiB,IAAIkL,SAAS,0BAA4BnM,KAAKyJ,OAAS,MAGrEX,WAAY,WACV,GAAIwD,GAAYtM,KAAKgM,oBACjBO,EAAUD,EAAU1E,WAAWwB,IAC/BoD,EAAaF,EAAUF,cACvBK,EAAgBzM,KAAKiB,IAAIiL,cACzBQ,EAAa1M,KAAKiB,IAAIoI,WACN,KAAhBrJ,KAAKyJ,QAAgBzJ,KAAKyJ,QAAUzJ,KAAKK,KAAKwD,OAAS,GAAe,EAAV0I,EAC9DvM,KAAKiB,IAAIoI,UAAUkD,EAAUG,GACpBH,EAAUC,EAAaC,GAChCzM,KAAKiB,IAAIoI,UAAUkD,EAAUC,EAAaE,EAAaD,IAI3DrE,eAAgB,SAAU/B,GACxB,GAAIC,GAAO5B,EAAGI,EACV0E,EAAO,EACX,KAAK9E,EAAI,EAAGA,EAAI2B,EAAWxC,QACrB7D,KAAKK,KAAKwD,SAAW7D,KAAK+H,SADGrD,IAEjC4B,EAAQD,EAAW3B,GACf0B,EAAQpG,KAAKK,KAAMiG,KACvBxB,EAAQ9E,KAAKK,KAAKwD,OAClB7D,KAAKK,KAAK8D,KAAKmC,GACfkD,GAAQ,6CAA+C1E,EAAQ,QAC/D0E,GAAUlD,EAAMhC,SAASqI,SAASrG,EAAMjC,MAAOiC,EAAMxC,MACrD0F,GAAQ,YAEV,OAAOA,IAGThB,cAAe,SAAUH,GACvB,GAAIrI,KAAK8H,OAAQ,CACV9H,KAAK0J,WACR1J,KAAK0J,SAAW5K,EAAE,yCAAyC8N,UAAU5M,KAAKiB,KAE5E,IAAIuI,GAAO1K,EAAE2D,WAAWzC,KAAK8H,QAAU9H,KAAK8H,OAAOO,GAAgBrI,KAAK8H,MACxE9H,MAAK0J,SAASF,KAAKA,KAIvBf,cAAe,SAAUJ,GACvB,GAAIrI,KAAK6H,OAAQ,CACV7H,KAAK2J,WACR3J,KAAK2J,SAAW7K,EAAE,yCAAyC6D,SAAS3C,KAAKiB,KAE3E,IAAIuI,GAAO1K,EAAE2D,WAAWzC,KAAK6H,QAAU7H,KAAK6H,OAAOQ,GAAgBrI,KAAK6H,MACxE7H,MAAK2J,SAASH,KAAKA,KAIvBR,wBAAyB,SAAUX,GACjC,GAAIrI,KAAK+I,iBAAkB,CACpB/I,KAAK4J,qBACR5J,KAAK4J,mBAAqB9K,EAAE,qDAAqD6D,SAAS3C,KAAKiB,KAEjG,IAAIuI,GAAO1K,EAAE2D,WAAWzC,KAAK+I,kBAAoB/I,KAAK+I,iBAAiBV,GAAgBrI,KAAK+I,gBAC5F/I,MAAK4J,mBAAmBJ,KAAKA,KAIjCd,gBAAiB,SAAUc,GACrBxJ,KAAK2J,SACP3J,KAAK2J,SAASkD,OAAOrD,GAErBxJ,KAAKiB,IAAI6L,OAAOtD,IAIpBb,aAAc,WACZ,GAAIoE,GAAqB7G,EAAQmD,YAAcnD,EAAQJ,SACnDA,EAAS9F,KAAKiB,IAAI6E,QACjB9F,MAAKiB,IAAI2G,WAAWwB,IAAMtD,EAAUiH,GACvC/M,KAAKiB,IAAI+L,QAAQ5D,IAAK2D,EAAqBjH,KAI/C8C,YAAa,WASX,IAJA,GACyCoE,GADrCC,EAAY,GACZC,EAAalN,KAAKiB,IAAI+L,SAASrF,KAC/BwF,EAAQnN,KAAKiB,IAAIkM,QACjBC,EAAUlH,EAAQiH,QAAUF,EACzBC,EAAaC,EAAQC,IAC1BpN,KAAKiB,IAAI+L,QAAQrF,KAAMuF,EAAaD,IACpCD,EAAShN,KAAKiB,IAAI+L,SAASrF,OACvBqF,GAAUE,KACdA,EAAaF,GAIjBzD,gBAAiB,SAAU3B,GAmBzB,MAjBsC,KAAlC5H,KAAKgI,UAAUqF,QAAQ,OAEzBzF,GACEwB,IAAK,OACLkE,OAAQtN,KAAKiB,IAAIsM,SAASzH,SAAW8B,EAASwB,IAAMxB,EAAS4F,WAC7D7F,KAAMC,EAASD,OAGjBC,EAAS0F,OAAS,aACX1F,GAAS4F,YAEwB,KAAtCxN,KAAKgI,UAAUqF,QAAQ,WACzBzF,EAASD,KAAO,EACgC,KAAvC3H,KAAKgI,UAAUqF,QAAQ,cAChCzF,EAAS6F,MAAQ,EACjB7F,EAASD,KAAO,QAEXC,KAIX9I,EAAEQ,GAAGC,aAAayD,SAAWA,EAC7BlE,EAAEuC,OAAOvC,EAAEQ,GAAGC,aAAcuH,IAC5B9H,IAED,SAAUF,GACT,YAiBA,SAASgC,GAAS4M,GAChB5O,EAAEuC,OAAOrB,KAAM0N,GACX1N,KAAK2N,QAAS3N,KAAKgF,OAAS4I,EAAQ5N,KAAKgF,SAhB/C,GAAI4I,GAAU,SAAU7L,GACtB,GAAI8L,KACJ,OAAO,UAAU/J,EAAMgK,GACjBD,EAAK/J,GACPgK,EAASD,EAAK/J,IAEd/B,EAAKjC,KAAKE,KAAM8D,EAAM,SAAUzD,GAC9BwN,EAAK/J,IAAS+J,EAAK/J,QAAaiK,OAAO1N,GACvCyN,EAASrN,MAAM,KAAMV,cAW7Be,GAASC,MAAQ,SAAUiN,EAAiBC,GAC1C,MAAOnP,GAAE2G,IAAIuI,EAAiB,SAAU1J,GACtC,GAAI4J,GAAc,GAAIpN,GAASwD,EAG/B,OAFA4J,GAAYlN,GAAKiN,EAAOjN,GACxBkN,EAAYjN,IAAMgN,EAAOhN,IAClBiN,KAIXpP,EAAEuC,OAAOP,EAASlB,WAKhBiF,MAAY,KACZsJ,QAAY,KACZnJ,OAAY,KAGZ3F,GAAY,KACZsO,OAAY,EACZhJ,QAAY,WAAc,OAAO,GACjCG,MAAY,EACZ6H,SAAY,SAAUhM,GAAO,MAAOA,IACpC6F,WAAY,OAGd1H,EAAEQ,GAAGC,aAAauB,SAAWA,GAE7B9B,IAED,SAAUF,GACT,YAiCA,SAASmE,MA/BT,GAAImL,GAAMC,KAAKD,KAAO,WAAc,OAAO,GAAIC,OAAOC,WAOlDC,EAAW,SAAUxM,EAAMyM,GAC7B,GAAIC,GAAS/O,EAAMiF,EAAS+J,EAAWC,EACnCC,EAAQ,WACV,GAAIC,GAAOT,IAAQM,CACRF,GAAPK,EACFJ,EAAUnD,WAAWsD,EAAOJ,EAAOK,IAEnCJ,EAAU,KACVE,EAAS5M,EAAKtB,MAAMkE,EAASjF,GAC7BiF,EAAUjF,EAAO,MAIrB,OAAO,YAOL,MANAiF,GAAU3E,KACVN,EAAOK,UACP2O,EAAYN,IACPK,IACHA,EAAUnD,WAAWsD,EAAOJ,IAEvBG,GAMX7P,GAAEuC,OAAO4B,EAAQrD,WAIfP,GAAW,KACXe,UAAW,KACXY,GAAW,KACXC,IAAW,KACXxB,OAAW,KAKXmC,WAAY,SAAUV,EAASd,EAAWX,GACxCO,KAAKgB,GAAYE,EACjBlB,KAAKiB,IAAYnC,EAAEoC,GACnBlB,KAAKX,GAAYe,EAAUf,GAAKW,KAAK8O,YAAYlO,KACjDZ,KAAKI,UAAYA,EACjBJ,KAAKP,OAAYA,EAEbO,KAAKP,OAAO8O,WACdvO,KAAK+O,SAAWR,EAASvO,KAAK+O,SAAU/O,KAAKP,OAAO8O,WAGtDvO,KAAKgG,eAGP5C,QAAS,WACPpD,KAAKiB,IAAIoC,IAAI,IAAMrD,KAAKX,IACxBW,KAAKiB,IAAMjB,KAAKgB,GAAKhB,KAAKI,UAAY,MAQxCgE,OAAQ,WACN,KAAM,IAAInF,OAAM,oBAIlBqG,iBAAkB,WAChB,GAAIsC,GAAW5H,KAAKgP,4BAChBhC,EAAShN,KAAKiB,IAAI+L,SAGlB1F,EAAUtH,KAAKP,OAAOkD,QAC1B,IAAI2E,EAAS,CACJA,YAAmBxI,KAAMwI,EAAUxI,EAAEwI,GAC3C,IAAI2H,GAAe3H,EAAQ4H,eAAelC,QAC1CA,GAAO5D,KAAO6F,EAAa7F,IAC3B4D,EAAOrF,MAAQsH,EAAatH,KAK/B,MAFAC,GAASwB,KAAO4D,EAAO5D,IACvBxB,EAASD,MAAQqF,EAAOrF,KACjBC,GAITpD,MAAO,WACLxE,KAAKiB,IAAIuD,SAMXwB,YAAa,WACXhG,KAAKiB,IAAIwF,GAAG,SAAWzG,KAAKX,GAAIP,EAAE+L,MAAM7K,KAAK+O,SAAU/O,QAGzD+O,SAAU,SAAUxK,GACdvE,KAAKmP,YAAY5K,IACrBvE,KAAKI,UAAUmD,QAAQvD,KAAK0D,0BAA0B,IAIxDyL,YAAa,SAAUC,GACrB,OAAQA,EAAWnF,SACjB,IAAK,GACL,IAAK,IACL,IAAK,IACL,IAAK,IACH,OAAO,EAEX,GAAImF,EAAWlF,QAAS,OAAQkF,EAAWnF,SACzC,IAAK,IACL,IAAK,IACH,OAAO,MAKfnL,EAAEQ,GAAGC,aAAa0D,QAAUA,GAC5BjE,IAED,SAAUF,GACT,YAMA,SAASuQ,GAASnO,EAASd,EAAWX,GACpCO,KAAK4B,WAAWV,EAASd,EAAWX,GAGtCX,EAAEuC,OAAOgO,EAASzP,UAAWd,EAAEQ,GAAGC,aAAa0D,QAAQrD,WAKrDwE,OAAQ,SAAUC,EAAOC,EAAUC,GACjC,GAAI+K,GAAMtP,KAAK0D,yBACX6L,EAAOvP,KAAKgB,GAAGqD,MAAMmL,UAAUxP,KAAKgB,GAAGmC,cACvCsM,EAAYnL,EAAS6J,QAAQ9J,EAAOE,EACf,oBAAdkL,KACL3Q,EAAE4Q,QAAQD,KACZF,EAAOE,EAAU,GAAKF,EACtBE,EAAYA,EAAU,IAExBH,EAAMA,EAAInB,QAAQ7J,EAASO,MAAO4K,GAClCzP,KAAKiB,IAAI0O,IAAIL,EAAMC,GACnBvP,KAAKgB,GAAG4O,eAAiB5P,KAAKgB,GAAGmC,aAAemM,EAAIzL,SAIxDH,uBAAwB,WACtB,MAAO1D,MAAKgB,GAAGqD,MAAMmL,UAAU,EAAGxP,KAAKgB,GAAGmC,eAM5C6L,0BAA2B,WACzB,GAAIa,GAAI/Q,EAAEQ,GAAGC,aAAauQ,oBAAoB9P,KAAKgB,GAAIhB,KAAKgB,GAAG4O,eAC/D,QACExG,IAAKyG,EAAEzG,IAAMpJ,KAAK+P,uBAAyB/P,KAAKiB,IAAIoI,YACpD1B,KAAMkI,EAAElI,KAAO3H,KAAKiB,IAAIqI,eAI5ByG,qBAAsB,WACpB,GAAIvC,GAAanC,SAASrL,KAAKiB,IAAIwG,IAAI,eAAgB,GACvD,IAAIuI,MAAMxC,GAAa,CAErB,GAAIyC,GAAajQ,KAAKgB,GAAGiP,WACrBC,EAAOxO,SAASgE,cAAc1F,KAAKgB,GAAGmP,UACtCC,EAAQpQ,KAAKgB,GAAGoP,KACpBF,GAAKG,aACH,QACA,sCAAwCD,EAAME,WAAa,cAAgBF,EAAMG,UAEnFL,EAAKM,UAAY,OACjBP,EAAWQ,YAAYP,GACvB1C,EAAa0C,EAAKQ,aAClBT,EAAWU,YAAYT,GAEzB,MAAO1C,MAIX1O,EAAEQ,GAAGC,aAAa8P,SAAWA,GAC7BrQ,IAED,SAAUF,GACT,YAIA,SAAS8R,GAAW1P,EAASd,EAAWX,GACtCO,KAAK4B,WAAWV,EAASd,EAAWX,GACpCX,EAAE,SAAW+R,EAAe,WAAWpJ,KACrCG,SAAU,WACVwB,IAAK,MACLzB,KAAM,QACLmJ,aAAa5P,GARlB,GAAI2P,GAAe,GAWnB/R,GAAEuC,OAAOuP,EAAWhR,UAAWd,EAAEQ,GAAGC,aAAa8P,SAASzP,WAIxDwE,OAAQ,SAAUC,EAAOC,EAAUC,GACjC,GAAI+K,GAAMtP,KAAK0D,yBACX6L,EAAOvP,KAAKgB,GAAGqD,MAAMmL,UAAUF,EAAIzL,QACnC4L,EAAYnL,EAAS6J,QAAQ9J,EAAOE,EACxC,IAAyB,mBAAdkL,GAA2B,CAChC3Q,EAAE4Q,QAAQD,KACZF,EAAOE,EAAU,GAAKF,EACtBE,EAAYA,EAAU,IAExBH,EAAMA,EAAInB,QAAQ7J,EAASO,MAAO4K,GAClCzP,KAAKiB,IAAI0O,IAAIL,EAAMC,GACnBvP,KAAKgB,GAAGwD,OACR,IAAIuM,GAAQ/Q,KAAKgB,GAAGgQ,iBACpBD,GAAME,UAAS,GACfF,EAAMG,QAAQ,YAAa5B,EAAIzL,QAC/BkN,EAAMI,UAAU,YAAa7B,EAAIzL,QACjCkN,EAAM3M,WAIVV,uBAAwB,WACtB1D,KAAKgB,GAAGwD,OACR,IAAIuM,GAAQrP,SAAS0P,UAAUC,aAC/BN,GAAMI,UAAU,aAAcnR,KAAKgB,GAAGqD,MAAMR,OAC5C,IAAIyN,GAAMP,EAAMvN,KAAK+N,MAAMV,EAC3B,OAAsB,KAAfS,EAAIzN,OAAeyN,EAAI,GAAKA,EAAI,MAI3CxS,EAAEQ,GAAGC,aAAaqR,WAAaA,GAC/B5R,IAMD,SAAUF,GACT,YAMA,SAAS0S,GAAiBtQ,EAASd,EAAWX,GAC5CO,KAAK4B,WAAWV,EAASd,EAAWX,GAGtCX,EAAEuC,OAAOmQ,EAAgB5R,UAAWd,EAAEQ,GAAGC,aAAa0D,QAAQrD,WAM5DwE,OAAQ,SAAUC,EAAOC,EAAUC,GACjC,GAAI+K,GAAMtP,KAAK0D,yBACX+N,EAAMtL,OAAOuL,eACbX,EAAQU,EAAIE,WAAW,GACvBP,EAAYL,EAAMa,YACtBR,GAAUS,mBAAmBd,EAAMe,eACnC,IAAIC,GAAUX,EAAU5O,WACpB+M,EAAOwC,EAAQvC,UAAUuB,EAAMiB,aAC/BvC,EAAYnL,EAAS6J,QAAQ9J,EAAOE,EACxC,IAAyB,mBAAdkL,GAA2B,CAChC3Q,EAAE4Q,QAAQD,KACZF,EAAOE,EAAU,GAAKF,EACtBE,EAAYA,EAAU,IAExBH,EAAMA,EAAInB,QAAQ7J,EAASO,MAAO4K,GAClCsB,EAAMc,mBAAmBd,EAAMe,gBAC/Bf,EAAMkB,gBAGN,IAAIC,GAAaxQ,SAASgE,cAAc,MACxCwM,GAAW1B,UAAYlB,CACvB,IAAI6C,GAAczQ,SAASgE,cAAc,MACzCyM,GAAY3B,UAAYjB,CAMxB,KAHA,GACI6C,GACAC,EAFAC,EAAW5Q,SAAS6Q,yBAGjBH,EAAYF,EAAWM,YAC7BH,EAAYC,EAAS7B,YAAY2B,EAElC,MAAOA,EAAYD,EAAYK,YAC9BF,EAAS7B,YAAY2B,EAItBrB,GAAM0B,WAAWH,GACjBvB,EAAM2B,cAAcL,GAEpBtB,EAAME,UAAS,GACfQ,EAAIkB,kBACJlB,EAAImB,SAAS7B,KAgBjB/B,0BAA2B,WACzB,GAAI+B,GAAQ5K,OAAOuL,eAAeC,WAAW,GAAGC,aAC5CiB,EAAOnR,SAASgE,cAAc,OAClCqL,GAAM0B,WAAWI,GACjB9B,EAAMc,mBAAmBgB,GACzB9B,EAAMkB,gBACN,IAAIa,GAAQhU,EAAE+T,GACVjL,EAAWkL,EAAM9F,QAKrB,OAJApF,GAASD,MAAQ3H,KAAKiB,IAAI+L,SAASrF,KACnCC,EAASwB,KAAO0J,EAAMhN,SAAW9F,KAAKiB,IAAI+L,SAAS5D,IACnDxB,EAAS4F,WAAasF,EAAMhN,SAC5BgN,EAAM5K,SACCN,GAWTlE,uBAAwB,WACtB,GAAIqN,GAAQ5K,OAAOuL,eAAeC,WAAW,GACzCP,EAAYL,EAAMa,YAEtB,OADAR,GAAUS,mBAAmBd,EAAMe,gBAC5BV,EAAU5O,WAAWgN,UAAU,EAAGuB,EAAMiB,gBAInDlT,EAAEQ,GAAGC,aAAaiS,gBAAkBA,GACpCxS,GAuBD,SAAUF,GAmDX,QAASgR,GAAoB5O,EAAS0G,EAAU8F,GAC9C,IAAIqF,EACF,KAAM,IAAI9T,OAAM,iFAGlB,IAAI+T,GAAQtF,GAAWA,EAAQsF,QAAS,CACxC,IAAIA,EAAO,CACT,GAAIhS,GAAKU,SAASuR,cAAc,4CAC3BjS,IAAOA,EAAGiP,WAAWU,YAAY3P,GAIxC,GAAIkS,GAAMxR,SAASgE,cAAc,MACjCwN,GAAI7T,GAAK,2CACTqC,SAASyR,KAAK1C,YAAYyC,EAE1B,IAAI9C,GAAQ8C,EAAI9C,MACZgD,EAAWjN,OAAOkN,iBAAkBA,iBAAiBnS,GAAWA,EAAQoS,YAG5ElD,GAAMmD,WAAa,WACM,UAArBrS,EAAQiP,WACVC,EAAMoD,SAAW,cAGnBpD,EAAMxI,SAAW,WACZoL,IACH5C,EAAMqD,WAAa,UAGrBC,EAAWC,QAAQ,SAAUC,GAC3BxD,EAAMwD,GAAQR,EAASQ,KAGrBC,EAEE3S,EAAQ4S,aAAezI,SAAS+H,EAAStN,UAC3CsK,EAAM2D,UAAY,UAEpB3D,EAAM4D,SAAW,SAGnBd,EAAIe,YAAc/S,EAAQmD,MAAMmL,UAAU,EAAG5H,GAEpB,UAArB1G,EAAQiP,WACV+C,EAAIe,YAAcf,EAAIe,YAAY9F,QAAQ,MAAO,KAEnD,IAAI+F,GAAOxS,SAASgE,cAAc,OAMlCwO,GAAKD,YAAc/S,EAAQmD,MAAMmL,UAAU5H,IAAa,IACxDsL,EAAIzC,YAAYyD,EAEhB,IAAIC,IACF/K,IAAK8K,EAAKE,UAAY/I,SAAS+H,EAAyB,gBACxDzL,KAAMuM,EAAKG,WAAahJ,SAAS+H,EAA0B,iBAS7D,OANIJ,GACFkB,EAAK9D,MAAMkE,gBAAkB,OAE7B5S,SAASyR,KAAKxC,YAAYuC,GAGrBiB,EAhHT,GAAIT,IACF,YACA,YACA,QACA,SACA,YACA,YAEA,iBACA,mBACA,oBACA,kBACA,cAEA,aACA,eACA,gBACA,cAGA,YACA,cACA,aACA,cACA,WACA,iBACA,aACA,aAEA,YACA,gBACA,aACA,iBAEA,gBACA,cAEA,UACA,cAIEX,EAA+B,mBAAX5M,QACpB0N,EAAad,GAAuC,MAA1B5M,OAAOoO,eAwErCzV,GAAEQ,GAAGC,aAAauQ,oBAAsBA,GAEtC9Q,GAEKA"} \ No newline at end of file From 3fc3ded750afe4b90c95f3036bded015ed5c9572 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 23 Jul 2020 06:11:21 +0000 Subject: [PATCH 22/44] Useless info messages removed --- mod/events.php | 2 -- mod/follow.php | 2 +- mod/item.php | 6 ++---- mod/message.php | 15 ++++++--------- mod/network.php | 4 ++-- mod/oexchange.php | 1 - mod/photos.php | 1 - mod/settings.php | 10 +++------- mod/tagrm.php | 1 - mod/unfollow.php | 1 - mod/wallmessage.php | 2 -- src/App/Authentication.php | 4 +--- src/App/Module.php | 2 +- src/Core/UserImport.php | 2 +- src/Model/FileTag.php | 2 -- src/Module/Admin/Addons/Index.php | 2 +- src/Module/Admin/Blocklist/Server.php | 1 - src/Module/Admin/Logs/Settings.php | 1 - src/Module/Admin/Site.php | 4 +--- src/Module/Admin/Themes/Details.php | 4 +--- src/Module/Admin/Themes/Embed.php | 2 -- src/Module/Admin/Themes/Index.php | 2 +- src/Module/Admin/Tos.php | 2 -- src/Module/BaseSearch.php | 2 +- src/Module/Contact.php | 4 +--- src/Module/Contact/Advanced.php | 4 +--- src/Module/Contact/Poke.php | 4 +--- src/Module/Conversation/Community.php | 2 +- src/Module/Debug/Feed.php | 2 +- src/Module/Directory.php | 2 +- src/Module/Filer/RemoveTag.php | 6 +++--- src/Module/Filer/SaveTag.php | 3 +-- src/Module/Group.php | 9 +++------ src/Module/Notifications/Introductions.php | 2 +- src/Module/Profile/Contacts.php | 2 +- src/Module/Search/Index.php | 2 +- src/Module/Search/Saved.php | 12 +++++++----- src/Module/Settings/Profile/Index.php | 4 +--- src/Module/Settings/Profile/Photo/Index.php | 4 +--- 39 files changed, 47 insertions(+), 90 deletions(-) diff --git a/mod/events.php b/mod/events.php index 437cc160b..0c16044b4 100644 --- a/mod/events.php +++ b/mod/events.php @@ -584,8 +584,6 @@ function events_content(App $a) if (Item::exists(['id' => $ev[0]['itemid']])) { notice(DI::l10n()->t('Failed to remove event') . EOL); - } else { - info(DI::l10n()->t('Event removed') . EOL); } DI::baseUrl()->redirect('events'); diff --git a/mod/follow.php b/mod/follow.php index 141fa9fdb..ac07d040c 100644 --- a/mod/follow.php +++ b/mod/follow.php @@ -62,7 +62,7 @@ function follow_post(App $a) DI::baseUrl()->redirect('contact/' . $result['cid']); } - info(DI::l10n()->t('The contact could not be added.')); + notice(DI::l10n()->t('The contact could not be added.')); DI::baseUrl()->redirect($return_path); // NOTREACHED diff --git a/mod/item.php b/mod/item.php index c4d7231c2..57fb64e3d 100644 --- a/mod/item.php +++ b/mod/item.php @@ -333,7 +333,7 @@ function item_post(App $a) { System::jsonExit(['preview' => '']); } - info(DI::l10n()->t('Empty post discarded.')); + notice(DI::l10n()->t('Empty post discarded.')); if ($return_path) { DI::baseUrl()->redirect($return_path); } @@ -703,7 +703,6 @@ function item_post(App $a) { // update filetags in pconfig FileTag::updatePconfig($uid, $categories_old, $categories_new, 'category'); - info(DI::l10n()->t('Post updated.')); if ($return_path) { DI::baseUrl()->redirect($return_path); } @@ -725,7 +724,7 @@ function item_post(App $a) { $post_id = Item::insert($datarray); if (!$post_id) { - info(DI::l10n()->t('Item wasn\'t stored.')); + notice(DI::l10n()->t('Item wasn\'t stored.')); if ($return_path) { DI::baseUrl()->redirect($return_path); } @@ -826,7 +825,6 @@ function item_post(App $a) { return $post_id; } - info(DI::l10n()->t('Post published.')); item_post_return(DI::baseUrl(), $api_source, $return_path); // NOTREACHED } diff --git a/mod/message.php b/mod/message.php index 438f96030..4404e6aff 100644 --- a/mod/message.php +++ b/mod/message.php @@ -94,8 +94,6 @@ function message_post(App $a) case -4: notice(DI::l10n()->t('Message collection failure.') . EOL); break; - default: - info(DI::l10n()->t('Message sent.') . EOL); } // fake it to go back to the input form if no recipient listed @@ -178,17 +176,16 @@ function message_content(App $a) if ($cmd === 'drop') { $message = DBA::selectFirst('mail', ['convid'], ['id' => $a->argv[2], 'uid' => local_user()]); if(!DBA::isResult($message)){ - info(DI::l10n()->t('Conversation not found.') . EOL); + notice(DI::l10n()->t('Conversation not found.') . EOL); DI::baseUrl()->redirect('message'); } - if (DBA::delete('mail', ['id' => $a->argv[2], 'uid' => local_user()])) { - info(DI::l10n()->t('Message deleted.') . EOL); + if (!DBA::delete('mail', ['id' => $a->argv[2], 'uid' => local_user()])) { + notice(DI::l10n()->t('Message was not deleted.') . EOL); } $conversation = DBA::selectFirst('mail', ['id'], ['convid' => $message['convid'], 'uid' => local_user()]); if(!DBA::isResult($conversation)){ - info(DI::l10n()->t('Conversation removed.') . EOL); DI::baseUrl()->redirect('message'); } @@ -201,8 +198,8 @@ function message_content(App $a) if (DBA::isResult($r)) { $parent = $r[0]['parent-uri']; - if (DBA::delete('mail', ['parent-uri' => $parent, 'uid' => local_user()])) { - info(DI::l10n()->t('Conversation removed.') . EOL); + if (!DBA::delete('mail', ['parent-uri' => $parent, 'uid' => local_user()])) { + notice(DI::l10n()->t('Conversation was not removed.') . EOL); } } DI::baseUrl()->redirect('message'); @@ -301,7 +298,7 @@ function message_content(App $a) $r = get_messages(local_user(), $pager->getStart(), $pager->getItemsPerPage()); if (!DBA::isResult($r)) { - info(DI::l10n()->t('No messages.') . EOL); + notice(DI::l10n()->t('No messages.') . EOL); return $o; } diff --git a/mod/network.php b/mod/network.php index f847e6757..2afc90dbf 100644 --- a/mod/network.php +++ b/mod/network.php @@ -305,7 +305,7 @@ function network_content(App $a, $update = 0, $parent = 0) } if ($o === '') { - info("No items found"); + notice("No items found"); } return $o; @@ -569,7 +569,7 @@ function networkThreadedView(App $a, $update, $parent) $sql_extra3 .= " OR (`thread`.`contact-id` = '$contact_str_self' AND `temp1`.`allow_gid` LIKE '" . Strings::protectSprintf('%<' . intval($gid) . '>%') . "' AND `temp1`.`private`))"; } else { $sql_extra3 .= " AND false "; - info(DI::l10n()->t('Group is empty')); + notice(DI::l10n()->t('Group is empty')); } $o = Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), [ diff --git a/mod/oexchange.php b/mod/oexchange.php index 97367c3ea..6a05d4994 100644 --- a/mod/oexchange.php +++ b/mod/oexchange.php @@ -45,7 +45,6 @@ function oexchange_content(App $a) { } if (($a->argc > 1) && $a->argv[1] === 'done') { - info(DI::l10n()->t('Post successful.') . EOL); return; } diff --git a/mod/photos.php b/mod/photos.php index f33a9241e..4118b8069 100644 --- a/mod/photos.php +++ b/mod/photos.php @@ -295,7 +295,6 @@ function photos_post(App $a) // Update the photo albums cache Photo::clearAlbumCache($page_owner_uid); - notice('Successfully deleted the photo.'); } else { notice('Failed to delete the photo.'); DI::baseUrl()->redirect('photos/' . $a->argv[1] . '/image/' . $a->argv[3]); diff --git a/mod/settings.php b/mod/settings.php index 9b2f4f650..14db27e6f 100644 --- a/mod/settings.php +++ b/mod/settings.php @@ -202,9 +202,6 @@ function settings_post(App $a) } } } - if (!$failed) { - info(DI::l10n()->t('Email settings updated.') . EOL); - } } } @@ -219,7 +216,6 @@ function settings_post(App $a) DI::pConfig()->set(local_user(), 'feature', substr($k, 8), ((intval($v)) ? 1 : 0)); } } - info(DI::l10n()->t('Features updated') . EOL); return; } @@ -231,7 +227,7 @@ function settings_post(App $a) // was there an error if ($_FILES['importcontact-filename']['error'] > 0) { Logger::notice('Contact CSV file upload error'); - info(DI::l10n()->t('Contact CSV file upload error')); + notice(DI::l10n()->t('Contact CSV file upload error')); } else { $csvArray = array_map('str_getcsv', file($_FILES['importcontact-filename']['tmp_name'])); // import contacts @@ -443,8 +439,8 @@ function settings_post(App $a) $fields['openidserver'] = ''; } - if (DBA::update('user', $fields, ['uid' => local_user()])) { - info(DI::l10n()->t('Settings updated.') . EOL); + if (!DBA::update('user', $fields, ['uid' => local_user()])) { + notice(DI::l10n()->t('Settings were not updated.') . EOL); } // clear session language diff --git a/mod/tagrm.php b/mod/tagrm.php index 4022f999d..179276663 100644 --- a/mod/tagrm.php +++ b/mod/tagrm.php @@ -44,7 +44,6 @@ function tagrm_post(App $a) $item_id = $_POST['item'] ?? 0; update_tags($item_id, $tags); - info(DI::l10n()->t('Tag(s) removed') . EOL); DI::baseUrl()->redirect($_SESSION['photo_return']); // NOTREACHED diff --git a/mod/unfollow.php b/mod/unfollow.php index 09466ba80..5ccc9c859 100644 --- a/mod/unfollow.php +++ b/mod/unfollow.php @@ -79,7 +79,6 @@ function unfollow_post(App $a) $return_path = $base_return_path . '/' . $contact['id']; } - info(DI::l10n()->t('Contact unfollowed')); DI::baseUrl()->redirect($return_path); // NOTREACHED } diff --git a/mod/wallmessage.php b/mod/wallmessage.php index e5b482a65..82d87ca29 100644 --- a/mod/wallmessage.php +++ b/mod/wallmessage.php @@ -84,8 +84,6 @@ function wallmessage_post(App $a) { case -4: notice(DI::l10n()->t('Message collection failure.') . EOL); break; - default: - info(DI::l10n()->t('Message sent.') . EOL); } DI::baseUrl()->redirect('profile/'.$user['nickname']); diff --git a/src/App/Authentication.php b/src/App/Authentication.php index 678bb0058..a0ce5df65 100644 --- a/src/App/Authentication.php +++ b/src/App/Authentication.php @@ -270,7 +270,7 @@ class Authentication } } catch (Exception $e) { $this->logger->warning('authenticate: failed login attempt', ['action' => 'login', 'username' => Strings::escapeTags($username), 'ip' => $_SERVER['REMOTE_ADDR']]); - info($this->l10n->t('Login failed. Please check your credentials.' . EOL)); + notice($this->l10n->t('Login failed. Please check your credentials.' . EOL)); $this->baseUrl->redirect(); } @@ -389,8 +389,6 @@ class Authentication info($this->l10n->t('Welcome %s', $user_record['username'])); info($this->l10n->t('Please upload a profile photo.')); $this->baseUrl->redirect('settings/profile/photo/new'); - } else { - info($this->l10n->t("Welcome back %s", $user_record['username'])); } } diff --git a/src/App/Module.php b/src/App/Module.php index 4b9eb68bd..58c595cb7 100644 --- a/src/App/Module.php +++ b/src/App/Module.php @@ -237,7 +237,7 @@ class Module public function run(Core\L10n $l10n, App\BaseURL $baseUrl, LoggerInterface $logger, array $server, array $post) { if ($this->printNotAllowedAddon) { - info($l10n->t("You must be logged in to use addons. ")); + notice($l10n->t("You must be logged in to use addons. ")); } /* The URL provided does not resolve to a valid module. diff --git a/src/Core/UserImport.php b/src/Core/UserImport.php index 06ba6398a..ed131910c 100644 --- a/src/Core/UserImport.php +++ b/src/Core/UserImport.php @@ -271,7 +271,7 @@ class UserImport if ($r === false) { Logger::log("uimport:insert profile: ERROR : " . DBA::errorMessage(), Logger::INFO); - info(DI::l10n()->t("User profile creation error")); + notice(DI::l10n()->t("User profile creation error")); DBA::delete('user', ['uid' => $newuid]); DBA::delete('profile_field', ['uid' => $newuid]); return; diff --git a/src/Model/FileTag.php b/src/Model/FileTag.php index 0b728e33d..a2c8bb439 100644 --- a/src/Model/FileTag.php +++ b/src/Model/FileTag.php @@ -271,8 +271,6 @@ class FileTag if (!strlen($saved) || !stristr($saved, '[' . self::encode($file) . ']')) { DI::pConfig()->set($uid, 'system', 'filetags', $saved . '[' . self::encode($file) . ']'); } - - info(DI::l10n()->t('Item filed')); } return true; diff --git a/src/Module/Admin/Addons/Index.php b/src/Module/Admin/Addons/Index.php index 3049cdc6a..d52085389 100644 --- a/src/Module/Admin/Addons/Index.php +++ b/src/Module/Admin/Addons/Index.php @@ -50,7 +50,7 @@ class Index extends BaseAdmin } elseif (Addon::install($addon)) { info(DI::l10n()->t('Addon %s enabled.', $addon)); } else { - info(DI::l10n()->t('Addon %s failed to install.', $addon)); + notice(DI::l10n()->t('Addon %s failed to install.', $addon)); } break; diff --git a/src/Module/Admin/Blocklist/Server.php b/src/Module/Admin/Blocklist/Server.php index 4f19ca361..eccb65598 100644 --- a/src/Module/Admin/Blocklist/Server.php +++ b/src/Module/Admin/Blocklist/Server.php @@ -62,7 +62,6 @@ class Server extends BaseAdmin } } DI::config()->set('system', 'blocklist', $blocklist); - info(DI::l10n()->t('Site blocklist updated.') . EOL); } DI::baseUrl()->redirect('admin/blocklist/server'); diff --git a/src/Module/Admin/Logs/Settings.php b/src/Module/Admin/Logs/Settings.php index 5158108e4..b60936c78 100644 --- a/src/Module/Admin/Logs/Settings.php +++ b/src/Module/Admin/Logs/Settings.php @@ -51,7 +51,6 @@ class Settings extends BaseAdmin DI::config()->set('system', 'loglevel', $loglevel); } - info(DI::l10n()->t("Log settings updated.")); DI::baseUrl()->redirect('admin/logs'); } diff --git a/src/Module/Admin/Site.php b/src/Module/Admin/Site.php index 2e16cc657..c4b320e72 100644 --- a/src/Module/Admin/Site.php +++ b/src/Module/Admin/Site.php @@ -250,7 +250,7 @@ class Site extends BaseAdmin DI::baseUrl()->redirect('admin/site' . $active_panel); } } else { - info(DI::l10n()->t('Invalid storage backend setting value.')); + notice(DI::l10n()->t('Invalid storage backend setting value.')); } // Has the directory url changed? If yes, then resubmit the existing profiles there @@ -433,8 +433,6 @@ class Site extends BaseAdmin DI::config()->set('system', 'rino_encrypt' , $rino); - info(DI::l10n()->t('Site settings updated.') . EOL); - DI::baseUrl()->redirect('admin/site' . $active_panel); } diff --git a/src/Module/Admin/Themes/Details.php b/src/Module/Admin/Themes/Details.php index c8d057838..405e28902 100644 --- a/src/Module/Admin/Themes/Details.php +++ b/src/Module/Admin/Themes/Details.php @@ -48,8 +48,6 @@ class Details extends BaseAdmin } } - info(DI::l10n()->t('Theme settings updated.')); - if (DI::mode()->isAjax()) { return; } @@ -91,7 +89,7 @@ class Details extends BaseAdmin } elseif (Theme::install($theme)) { info(DI::l10n()->t('Theme %s successfully enabled.', $theme)); } else { - info(DI::l10n()->t('Theme %s failed to install.', $theme)); + notice(DI::l10n()->t('Theme %s failed to install.', $theme)); } DI::baseUrl()->redirect('admin/themes/' . $theme); diff --git a/src/Module/Admin/Themes/Embed.php b/src/Module/Admin/Themes/Embed.php index 675e33c84..37de7c238 100644 --- a/src/Module/Admin/Themes/Embed.php +++ b/src/Module/Admin/Themes/Embed.php @@ -62,8 +62,6 @@ class Embed extends BaseAdmin } } - info(DI::l10n()->t('Theme settings updated.')); - if (DI::mode()->isAjax()) { return; } diff --git a/src/Module/Admin/Themes/Index.php b/src/Module/Admin/Themes/Index.php index 955ddadc7..78d27dfa0 100644 --- a/src/Module/Admin/Themes/Index.php +++ b/src/Module/Admin/Themes/Index.php @@ -66,7 +66,7 @@ class Index extends BaseAdmin } elseif (Theme::install($theme)) { info(DI::l10n()->t('Theme %s successfully enabled.', $theme)); } else { - info(DI::l10n()->t('Theme %s failed to install.', $theme)); + notice(DI::l10n()->t('Theme %s failed to install.', $theme)); } } diff --git a/src/Module/Admin/Tos.php b/src/Module/Admin/Tos.php index 811a0eb25..a3bc94a1f 100644 --- a/src/Module/Admin/Tos.php +++ b/src/Module/Admin/Tos.php @@ -45,8 +45,6 @@ class Tos extends BaseAdmin DI::config()->set('system', 'tosprivstatement', $displayprivstatement); DI::config()->set('system', 'tostext', $tostext); - info(DI::l10n()->t('The Terms of Service settings have been updated.')); - DI::baseUrl()->redirect('admin/tos'); } diff --git a/src/Module/BaseSearch.php b/src/Module/BaseSearch.php index e67d3c3c9..57b5976ef 100644 --- a/src/Module/BaseSearch.php +++ b/src/Module/BaseSearch.php @@ -116,7 +116,7 @@ class BaseSearch extends BaseModule protected static function printResult(ResultList $results, Pager $pager, $header = '') { if ($results->getTotal() == 0) { - info(DI::l10n()->t('No matches')); + notice(DI::l10n()->t('No matches')); return ''; } diff --git a/src/Module/Contact.php b/src/Module/Contact.php index f63d42c0e..ee8ad3663 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -144,9 +144,7 @@ class Contact extends BaseModule ['id' => $contact_id, 'uid' => local_user()] ); - if (DBA::isResult($r)) { - info(DI::l10n()->t('Contact updated.') . EOL); - } else { + if (!DBA::isResult($r)) { notice(DI::l10n()->t('Failed to update contact record.') . EOL); } diff --git a/src/Module/Contact/Advanced.php b/src/Module/Contact/Advanced.php index d29d0609a..864080843 100644 --- a/src/Module/Contact/Advanced.php +++ b/src/Module/Contact/Advanced.php @@ -90,9 +90,7 @@ class Advanced extends BaseModule Model\Contact::updateAvatar($photo, local_user(), $contact['id'], true); } - if ($r) { - info(DI::l10n()->t('Contact settings applied.') . EOL); - } else { + if (!$r) { notice(DI::l10n()->t('Contact update failed.') . EOL); } diff --git a/src/Module/Contact/Poke.php b/src/Module/Contact/Poke.php index b4adff46d..9f2ae7bde 100644 --- a/src/Module/Contact/Poke.php +++ b/src/Module/Contact/Poke.php @@ -110,9 +110,7 @@ class Poke extends BaseModule */ private static function postReturn(bool $success) { - if ($success) { - info(DI::l10n()->t('Poke successfully sent.')); - } else { + if (!$success) { notice(DI::l10n()->t('Error while sending poke, please retry.')); } diff --git a/src/Module/Conversation/Community.php b/src/Module/Conversation/Community.php index 5637c6f41..c86bf9176 100644 --- a/src/Module/Conversation/Community.php +++ b/src/Module/Conversation/Community.php @@ -81,7 +81,7 @@ class Community extends BaseModule $items = self::getItems(); if (!DBA::isResult($items)) { - info(DI::l10n()->t('No results.')); + notice(DI::l10n()->t('No results.')); return $o; } diff --git a/src/Module/Debug/Feed.php b/src/Module/Debug/Feed.php index e969de9cc..f0f86f607 100644 --- a/src/Module/Debug/Feed.php +++ b/src/Module/Debug/Feed.php @@ -36,7 +36,7 @@ class Feed extends BaseModule public static function init(array $parameters = []) { if (!local_user()) { - info(DI::l10n()->t('You must be logged in to use this module')); + notice(DI::l10n()->t('You must be logged in to use this module')); DI::baseUrl()->redirect(); } } diff --git a/src/Module/Directory.php b/src/Module/Directory.php index 3d03f1071..491023145 100644 --- a/src/Module/Directory.php +++ b/src/Module/Directory.php @@ -75,7 +75,7 @@ class Directory extends BaseModule $profiles = Profile::searchProfiles($pager->getStart(), $pager->getItemsPerPage(), $search); if ($profiles['total'] === 0) { - info(DI::l10n()->t('No entries (some entries may be hidden).') . EOL); + notice(DI::l10n()->t('No entries (some entries may be hidden).') . EOL); } else { if (in_array('small', $app->argv)) { $photo = 'thumb'; diff --git a/src/Module/Filer/RemoveTag.php b/src/Module/Filer/RemoveTag.php index 7866656e3..a8a8a896b 100644 --- a/src/Module/Filer/RemoveTag.php +++ b/src/Module/Filer/RemoveTag.php @@ -59,11 +59,11 @@ class RemoveTag extends BaseModule ]); if ($item_id && strlen($term)) { - if (FileTag::unsaveFile(local_user(), $item_id, $term, $category)) { - info('Item removed'); + if (!FileTag::unsaveFile(local_user(), $item_id, $term, $category)) { + notice(DI::l10n()->t('Item was not removed')); } } else { - info('Item was not deleted'); + notice(DI::l10n()->t('Item was not deleted')); } DI::baseUrl()->redirect('network?file=' . rawurlencode($term)); diff --git a/src/Module/Filer/SaveTag.php b/src/Module/Filer/SaveTag.php index 12226107b..4b2fdb09e 100644 --- a/src/Module/Filer/SaveTag.php +++ b/src/Module/Filer/SaveTag.php @@ -35,7 +35,7 @@ class SaveTag extends BaseModule public static function init(array $parameters = []) { if (!local_user()) { - info(DI::l10n()->t('You must be logged in to use this module')); + notice(DI::l10n()->t('You must be logged in to use this module')); DI::baseUrl()->redirect(); } } @@ -54,7 +54,6 @@ class SaveTag extends BaseModule if ($item_id && strlen($term)) { // file item Model\FileTag::saveFile(local_user(), $item_id, $term); - info(DI::l10n()->t('Filetag %s saved to item', $term)); } // return filer dialog diff --git a/src/Module/Group.php b/src/Module/Group.php index 11e7f1a76..d5f1fc8ef 100644 --- a/src/Module/Group.php +++ b/src/Module/Group.php @@ -53,7 +53,6 @@ class Group extends BaseModule $name = Strings::escapeTags(trim($_POST['groupname'])); $r = Model\Group::create(local_user(), $name); if ($r) { - info(DI::l10n()->t('Group created.')); $r = Model\Group::getIdByName(local_user(), $name); if ($r) { DI::baseUrl()->redirect('group/' . $r); @@ -75,8 +74,8 @@ class Group extends BaseModule } $groupname = Strings::escapeTags(trim($_POST['groupname'])); if (strlen($groupname) && ($groupname != $group['name'])) { - if (Model\Group::update($group['id'], $groupname)) { - info(DI::l10n()->t('Group name changed.')); + if (!Model\Group::update($group['id'], $groupname)) { + notice(DI::l10n()->t('Group name was not changed.')); } } } @@ -216,9 +215,7 @@ class Group extends BaseModule DI::baseUrl()->redirect('contact'); } - if (Model\Group::remove($a->argv[2])) { - info(DI::l10n()->t('Group removed.')); - } else { + if (!Model\Group::remove($a->argv[2])) { notice(DI::l10n()->t('Unable to remove group.')); } } diff --git a/src/Module/Notifications/Introductions.php b/src/Module/Notifications/Introductions.php index 0b1cb9e6a..4b05b8c2c 100644 --- a/src/Module/Notifications/Introductions.php +++ b/src/Module/Notifications/Introductions.php @@ -191,7 +191,7 @@ class Introductions extends BaseNotifications } if (count($notifications['notifications']) == 0) { - info(DI::l10n()->t('No introductions.') . EOL); + notice(DI::l10n()->t('No introductions.') . EOL); $notificationNoContent = DI::l10n()->t('No more %s notifications.', $notifications['ident']); } diff --git a/src/Module/Profile/Contacts.php b/src/Module/Profile/Contacts.php index 3d55c57f4..dbf0cf8d8 100644 --- a/src/Module/Profile/Contacts.php +++ b/src/Module/Profile/Contacts.php @@ -92,7 +92,7 @@ class Contacts extends BaseProfile $contacts_stmt = DBA::select('contact', [], $condition, $params); if (!DBA::isResult($contacts_stmt)) { - info(DI::l10n()->t('No contacts.') . EOL); + notice(DI::l10n()->t('No contacts.') . EOL); return $o; } diff --git a/src/Module/Search/Index.php b/src/Module/Search/Index.php index 23f12d263..bf3ae2585 100644 --- a/src/Module/Search/Index.php +++ b/src/Module/Search/Index.php @@ -176,7 +176,7 @@ class Index extends BaseSearch } if (!DBA::isResult($r)) { - info(DI::l10n()->t('No results.')); + notice(DI::l10n()->t('No results.')); return $o; } diff --git a/src/Module/Search/Saved.php b/src/Module/Search/Saved.php index 73372b03a..0f45b50f5 100644 --- a/src/Module/Search/Saved.php +++ b/src/Module/Search/Saved.php @@ -41,16 +41,18 @@ class Saved extends BaseModule case 'add': $fields = ['uid' => local_user(), 'term' => $search]; if (!DBA::exists('search', $fields)) { - DBA::insert('search', $fields); - info(DI::l10n()->t('Search term successfully saved.')); + if (!DBA::insert('search', $fields)) { + notice(DI::l10n()->t('Search term was not saved.')); + } } else { - info(DI::l10n()->t('Search term already saved.')); + notice(DI::l10n()->t('Search term already saved.')); } break; case 'remove': - DBA::delete('search', ['uid' => local_user(), 'term' => $search]); - info(DI::l10n()->t('Search term successfully removed.')); + if (!DBA::delete('search', ['uid' => local_user(), 'term' => $search])) { + notice(DI::l10n()->t('Search term was not removed.')); + } break; } } diff --git a/src/Module/Settings/Profile/Index.php b/src/Module/Settings/Profile/Index.php index 1335a8211..a7e02f429 100644 --- a/src/Module/Settings/Profile/Index.php +++ b/src/Module/Settings/Profile/Index.php @@ -134,9 +134,7 @@ class Index extends BaseSettings ['uid' => local_user()] ); - if ($result) { - info(DI::l10n()->t('Profile updated.')); - } else { + if (!$result) { notice(DI::l10n()->t('Profile couldn\'t be updated.')); return; } diff --git a/src/Module/Settings/Profile/Photo/Index.php b/src/Module/Settings/Profile/Photo/Index.php index 3e4f9b8a4..df9622f2e 100644 --- a/src/Module/Settings/Profile/Photo/Index.php +++ b/src/Module/Settings/Profile/Photo/Index.php @@ -93,9 +93,7 @@ class Index extends BaseSettings $filename = ''; - if (Photo::store($Image, local_user(), 0, $resource_id, $filename, DI::l10n()->t('Profile Photos'), 0)) { - info(DI::l10n()->t('Image uploaded successfully.')); - } else { + if (!Photo::store($Image, local_user(), 0, $resource_id, $filename, DI::l10n()->t('Profile Photos'), 0)) { notice(DI::l10n()->t('Image upload failed.')); } From 0007da8630feed2f3207b95d226309a5f03ded43 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 23 Jul 2020 06:25:01 +0000 Subject: [PATCH 23/44] EOL removed --- mod/api.php | 6 +-- mod/cal.php | 2 +- mod/common.php | 4 +- mod/dfrn_confirm.php | 22 ++++----- mod/dfrn_poll.php | 4 +- mod/dfrn_request.php | 54 +++++++++++----------- mod/editpost.php | 6 +-- mod/events.php | 8 ++-- mod/item.php | 2 +- mod/lostpass.php | 6 +-- mod/match.php | 4 +- mod/message.php | 22 ++++----- mod/network.php | 6 +-- mod/notes.php | 2 +- mod/ostatus_subscribe.php | 2 +- mod/photos.php | 34 +++++++------- mod/repair_ostatus.php | 2 +- mod/settings.php | 16 +++---- mod/suggest.php | 2 +- mod/uimport.php | 6 +-- mod/videos.php | 4 +- mod/wall_attach.php | 2 +- mod/wall_upload.php | 4 +- mod/wallmessage.php | 20 ++++---- src/App/Authentication.php | 4 +- src/Model/Group.php | 2 +- src/Module/Admin/Blocklist/Server.php | 2 +- src/Module/Admin/DBSync.php | 2 +- src/Module/Admin/Item/Delete.php | 2 +- src/Module/Admin/Users.php | 2 +- src/Module/Apps.php | 2 +- src/Module/Contact.php | 14 +++--- src/Module/Contact/Advanced.php | 2 +- src/Module/Directory.php | 2 +- src/Module/FollowConfirm.php | 2 +- src/Module/Invite.php | 8 ++-- src/Module/Notifications/Introductions.php | 2 +- src/Module/Profile/Contacts.php | 4 +- src/Module/Profile/Status.php | 2 +- 39 files changed, 146 insertions(+), 146 deletions(-) diff --git a/mod/api.php b/mod/api.php index 47a809497..474d57af4 100644 --- a/mod/api.php +++ b/mod/api.php @@ -47,12 +47,12 @@ function oauth_get_client(OAuthRequest $request) function api_post(App $a) { if (!local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return; } if (count($a->user) && !empty($a->user['uid']) && $a->user['uid'] != local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return; } } @@ -107,7 +107,7 @@ function api_content(App $a) if (!local_user()) { /// @TODO We need login form to redirect to this page - notice(DI::l10n()->t('Please login to continue.') . EOL); + notice(DI::l10n()->t('Please login to continue.')); return Login::form(DI::args()->getQueryString(), false, $request->get_parameters()); } //FKOAuth1::loginUser(4); diff --git a/mod/cal.php b/mod/cal.php index 8db223784..0e8e8a2af 100644 --- a/mod/cal.php +++ b/mod/cal.php @@ -134,7 +134,7 @@ function cal_content(App $a) $is_owner = local_user() == $a->profile['uid']; if ($a->profile['hidewall'] && !$is_owner && !$remote_contact) { - notice(DI::l10n()->t('Access to this profile has been restricted.') . EOL); + notice(DI::l10n()->t('Access to this profile has been restricted.')); return; } diff --git a/mod/common.php b/mod/common.php index 9231d090b..0ff523b3f 100644 --- a/mod/common.php +++ b/mod/common.php @@ -40,7 +40,7 @@ function common_content(App $a) $zcid = 0; if (!local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return; } @@ -103,7 +103,7 @@ function common_content(App $a) } if ($total < 1) { - notice(DI::l10n()->t('No contacts in common.') . EOL); + notice(DI::l10n()->t('No contacts in common.')); return $o; } diff --git a/mod/dfrn_confirm.php b/mod/dfrn_confirm.php index 8b87bae5d..f8d2be44d 100644 --- a/mod/dfrn_confirm.php +++ b/mod/dfrn_confirm.php @@ -76,13 +76,13 @@ function dfrn_confirm_post(App $a, $handsfree = null) if (empty($_POST['source_url'])) { $uid = ($handsfree['uid'] ?? 0) ?: local_user(); if (!$uid) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return; } $user = DBA::selectFirst('user', [], ['uid' => $uid]); if (!DBA::isResult($user)) { - notice(DI::l10n()->t('Profile not found.') . EOL); + notice(DI::l10n()->t('Profile not found.')); return; } @@ -137,8 +137,8 @@ function dfrn_confirm_post(App $a, $handsfree = null) ); if (!DBA::isResult($r)) { Logger::log('Contact not found in DB.'); - notice(DI::l10n()->t('Contact not found.') . EOL); - notice(DI::l10n()->t('This may occasionally happen if contact was requested by both persons and it has already been approved.') . EOL); + notice(DI::l10n()->t('Contact not found.')); + notice(DI::l10n()->t('This may occasionally happen if contact was requested by both persons and it has already been approved.')); return; } @@ -239,20 +239,20 @@ function dfrn_confirm_post(App $a, $handsfree = null) // We shouldn't proceed, because the xml parser might choke, // and $status is going to be zero, which indicates success. // We can hardly call this a success. - notice(DI::l10n()->t('Response from remote site was not understood.') . EOL); + notice(DI::l10n()->t('Response from remote site was not understood.')); return; } if (strlen($leading_junk) && DI::config()->get('system', 'debugging')) { // This might be more common. Mixed error text and some XML. // If we're configured for debugging, show the text. Proceed in either case. - notice(DI::l10n()->t('Unexpected response from remote site: ') . EOL . $leading_junk . EOL); + notice(DI::l10n()->t('Unexpected response from remote site: ') . $leading_junk); } if (stristr($res, "t('Unexpected response from remote site: ') . EOL . htmlspecialchars($res) . EOL); + notice(DI::l10n()->t('Unexpected response from remote site: ') . EOL . htmlspecialchars($res)); return; } @@ -261,7 +261,7 @@ function dfrn_confirm_post(App $a, $handsfree = null) $message = XML::unescape($xml->message); // human readable text of what may have gone wrong. switch ($status) { case 0: - info(DI::l10n()->t("Confirmation completed successfully.") . EOL); + info(DI::l10n()->t("Confirmation completed successfully.")); break; case 1: // birthday paradox - generate new dfrn-id and fall through. @@ -273,15 +273,15 @@ function dfrn_confirm_post(App $a, $handsfree = null) ); case 2: - notice(DI::l10n()->t("Temporary failure. Please wait and try again.") . EOL); + notice(DI::l10n()->t("Temporary failure. Please wait and try again.")); break; case 3: - notice(DI::l10n()->t("Introduction failed or was revoked.") . EOL); + notice(DI::l10n()->t("Introduction failed or was revoked.")); break; } if (strlen($message)) { - notice(DI::l10n()->t('Remote site reported: ') . $message . EOL); + notice(DI::l10n()->t('Remote site reported: ') . $message); } if (($status == 0) && $intro_id) { diff --git a/mod/dfrn_poll.php b/mod/dfrn_poll.php index 8d50761db..d19ae287c 100644 --- a/mod/dfrn_poll.php +++ b/mod/dfrn_poll.php @@ -133,7 +133,7 @@ function dfrn_poll_init(App $a) Session::setVisitorsContacts(); if (!$quiet) { - info(DI::l10n()->t('%1$s welcomes %2$s', $r[0]['username'], $r[0]['name']) . EOL); + info(DI::l10n()->t('%1$s welcomes %2$s', $r[0]['username'], $r[0]['name'])); } // Visitors get 1 day session. @@ -536,7 +536,7 @@ function dfrn_poll_content(App $a) Session::setVisitorsContacts(); if (!$quiet) { - info(DI::l10n()->t('%1$s welcomes %2$s', $r[0]['username'], $r[0]['name']) . EOL); + info(DI::l10n()->t('%1$s welcomes %2$s', $r[0]['username'], $r[0]['name'])); } // Visitors get 1 day session. diff --git a/mod/dfrn_request.php b/mod/dfrn_request.php index f8e4c9023..eb323c248 100644 --- a/mod/dfrn_request.php +++ b/mod/dfrn_request.php @@ -110,7 +110,7 @@ function dfrn_request_post(App $a) if (DBA::isResult($r)) { if (strlen($r[0]['dfrn-id'])) { // We don't need to be here. It has already happened. - notice(DI::l10n()->t("This introduction has already been accepted.") . EOL); + notice(DI::l10n()->t("This introduction has already been accepted.")); return; } else { $contact_record = $r[0]; @@ -128,18 +128,18 @@ function dfrn_request_post(App $a) $parms = Probe::profile($dfrn_url); if (!count($parms)) { - notice(DI::l10n()->t('Profile location is not valid or does not contain profile information.') . EOL); + notice(DI::l10n()->t('Profile location is not valid or does not contain profile information.')); return; } else { if (empty($parms['fn'])) { - notice(DI::l10n()->t('Warning: profile location has no identifiable owner name.') . EOL); + notice(DI::l10n()->t('Warning: profile location has no identifiable owner name.')); } if (empty($parms['photo'])) { - notice(DI::l10n()->t('Warning: profile location has no profile photo.') . EOL); + notice(DI::l10n()->t('Warning: profile location has no profile photo.')); } $invalid = Probe::validDfrn($parms); if ($invalid) { - notice(DI::l10n()->tt("%d required parameter was not found at the given location", "%d required parameters were not found at the given location", $invalid) . EOL); + notice(DI::l10n()->tt("%d required parameter was not found at the given location", "%d required parameters were not found at the given location", $invalid)); return; } } @@ -177,7 +177,7 @@ function dfrn_request_post(App $a) } if ($r) { - info(DI::l10n()->t("Introduction complete.") . EOL); + info(DI::l10n()->t("Introduction complete.")); } $r = q("SELECT `id`, `network` FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `site-pubkey` = '%s' LIMIT 1", @@ -213,7 +213,7 @@ function dfrn_request_post(App $a) } // invalid/bogus request - notice(DI::l10n()->t('Unrecoverable protocol error.') . EOL); + notice(DI::l10n()->t('Unrecoverable protocol error.')); DI::baseUrl()->redirect(); return; // NOTREACHED } @@ -240,7 +240,7 @@ function dfrn_request_post(App $a) * */ if (empty($a->profile['uid'])) { - notice(DI::l10n()->t('Profile unavailable.') . EOL); + notice(DI::l10n()->t('Profile unavailable.')); return; } @@ -261,9 +261,9 @@ function dfrn_request_post(App $a) intval($uid) ); if (DBA::isResult($r) && count($r) > $maxreq) { - notice(DI::l10n()->t('%s has received too many connection requests today.', $a->profile['name']) . EOL); - notice(DI::l10n()->t('Spam protection measures have been invoked.') . EOL); - notice(DI::l10n()->t('Friends are advised to please try again in 24 hours.') . EOL); + notice(DI::l10n()->t('%s has received too many connection requests today.', $a->profile['name'])); + notice(DI::l10n()->t('Spam protection measures have been invoked.')); + notice(DI::l10n()->t('Friends are advised to please try again in 24 hours.')); return; } } @@ -287,7 +287,7 @@ function dfrn_request_post(App $a) $url = trim($_POST['dfrn_url']); if (!strlen($url)) { - notice(DI::l10n()->t("Invalid locator") . EOL); + notice(DI::l10n()->t("Invalid locator")); return; } @@ -323,10 +323,10 @@ function dfrn_request_post(App $a) if (DBA::isResult($ret)) { if (strlen($ret[0]['issued-id'])) { - notice(DI::l10n()->t('You have already introduced yourself here.') . EOL); + notice(DI::l10n()->t('You have already introduced yourself here.')); return; } elseif ($ret[0]['rel'] == Contact::FRIEND) { - notice(DI::l10n()->t('Apparently you are already friends with %s.', $a->profile['name']) . EOL); + notice(DI::l10n()->t('Apparently you are already friends with %s.', $a->profile['name'])); return; } else { $contact_record = $ret[0]; @@ -346,19 +346,19 @@ function dfrn_request_post(App $a) } else { $url = Network::isUrlValid($url); if (!$url) { - notice(DI::l10n()->t('Invalid profile URL.') . EOL); + notice(DI::l10n()->t('Invalid profile URL.')); DI::baseUrl()->redirect(DI::args()->getCommand()); return; // NOTREACHED } if (!Network::isUrlAllowed($url)) { - notice(DI::l10n()->t('Disallowed profile URL.') . EOL); + notice(DI::l10n()->t('Disallowed profile URL.')); DI::baseUrl()->redirect(DI::args()->getCommand()); return; // NOTREACHED } if (Network::isUrlBlocked($url)) { - notice(DI::l10n()->t('Blocked domain') . EOL); + notice(DI::l10n()->t('Blocked domain')); DI::baseUrl()->redirect(DI::args()->getCommand()); return; // NOTREACHED } @@ -366,18 +366,18 @@ function dfrn_request_post(App $a) $parms = Probe::profile(($hcard) ? $hcard : $url); if (!count($parms)) { - notice(DI::l10n()->t('Profile location is not valid or does not contain profile information.') . EOL); + notice(DI::l10n()->t('Profile location is not valid or does not contain profile information.')); DI::baseUrl()->redirect(DI::args()->getCommand()); } else { if (empty($parms['fn'])) { - notice(DI::l10n()->t('Warning: profile location has no identifiable owner name.') . EOL); + notice(DI::l10n()->t('Warning: profile location has no identifiable owner name.')); } if (empty($parms['photo'])) { - notice(DI::l10n()->t('Warning: profile location has no profile photo.') . EOL); + notice(DI::l10n()->t('Warning: profile location has no profile photo.')); } $invalid = Probe::validDfrn($parms); if ($invalid) { - notice(DI::l10n()->tt("%d required parameter was not found at the given location", "%d required parameters were not found at the given location", $invalid) . EOL); + notice(DI::l10n()->tt("%d required parameter was not found at the given location", "%d required parameters were not found at the given location", $invalid)); return; } @@ -425,7 +425,7 @@ function dfrn_request_post(App $a) } } if ($r === false) { - notice(DI::l10n()->t('Failed to update contact record.') . EOL); + notice(DI::l10n()->t('Failed to update contact record.')); return; } @@ -445,7 +445,7 @@ function dfrn_request_post(App $a) // This notice will only be seen by the requestor if the requestor and requestee are on the same server. if (!$failed) { - info(DI::l10n()->t('Your introduction has been sent.') . EOL); + info(DI::l10n()->t('Your introduction has been sent.')); } // "Homecoming" - send the requestor back to their site to record the introduction. @@ -477,7 +477,7 @@ function dfrn_request_post(App $a) // NOTREACHED // END $network != Protocol::PHANTOM } else { - notice(DI::l10n()->t("Remote subscription can't be done for your network. Please subscribe directly on your system.") . EOL); + notice(DI::l10n()->t("Remote subscription can't be done for your network. Please subscribe directly on your system.")); return; } } return; @@ -493,7 +493,7 @@ function dfrn_request_content(App $a) // to send us to the post section to record the introduction. if (!empty($_GET['dfrn_url'])) { if (!local_user()) { - info(DI::l10n()->t("Please login to confirm introduction.") . EOL); + info(DI::l10n()->t("Please login to confirm introduction.")); /* setup the return URL to come back to this page if they use openid */ return Login::form(); } @@ -501,7 +501,7 @@ function dfrn_request_content(App $a) // Edge case, but can easily happen in the wild. This person is authenticated, // but not as the person who needs to deal with this request. if ($a->user['nickname'] != $a->argv[1]) { - notice(DI::l10n()->t("Incorrect identity currently logged in. Please login to this profile.") . EOL); + notice(DI::l10n()->t("Incorrect identity currently logged in. Please login to this profile.")); return Login::form(); } @@ -603,7 +603,7 @@ function dfrn_request_content(App $a) // Normal web request. Display our user's introduction form. if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) { if (!DI::config()->get('system', 'local_block')) { - notice(DI::l10n()->t('Public access denied.') . EOL); + notice(DI::l10n()->t('Public access denied.')); return; } } diff --git a/mod/editpost.php b/mod/editpost.php index 8bde03293..cfca7695e 100644 --- a/mod/editpost.php +++ b/mod/editpost.php @@ -35,14 +35,14 @@ function editpost_content(App $a) $o = ''; if (!local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return; } $post_id = (($a->argc > 1) ? intval($a->argv[1]) : 0); if (!$post_id) { - notice(DI::l10n()->t('Item not found') . EOL); + notice(DI::l10n()->t('Item not found')); return; } @@ -52,7 +52,7 @@ function editpost_content(App $a) $item = Item::selectFirstForUser(local_user(), $fields, ['id' => $post_id, 'uid' => local_user()]); if (!DBA::isResult($item)) { - notice(DI::l10n()->t('Item not found') . EOL); + notice(DI::l10n()->t('Item not found')); return; } diff --git a/mod/events.php b/mod/events.php index 0c16044b4..69f6b6f32 100644 --- a/mod/events.php +++ b/mod/events.php @@ -132,7 +132,7 @@ function events_post(App $a) $onerror_path = 'events/' . $action . '?' . http_build_query($params, null, null, PHP_QUERY_RFC3986); if (strcmp($finish, $start) < 0 && !$nofinish) { - notice(DI::l10n()->t('Event can not end before it has started.') . EOL); + notice(DI::l10n()->t('Event can not end before it has started.')); if (intval($_REQUEST['preview'])) { echo DI::l10n()->t('Event can not end before it has started.'); exit(); @@ -141,7 +141,7 @@ function events_post(App $a) } if (!$summary || ($start === DBA::NULL_DATETIME)) { - notice(DI::l10n()->t('Event title and start time are required.') . EOL); + notice(DI::l10n()->t('Event title and start time are required.')); if (intval($_REQUEST['preview'])) { echo DI::l10n()->t('Event title and start time are required.'); exit(); @@ -225,7 +225,7 @@ function events_post(App $a) function events_content(App $a) { if (!local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return Login::form(); } @@ -583,7 +583,7 @@ function events_content(App $a) } if (Item::exists(['id' => $ev[0]['itemid']])) { - notice(DI::l10n()->t('Failed to remove event') . EOL); + notice(DI::l10n()->t('Failed to remove event')); } DI::baseUrl()->redirect('events'); diff --git a/mod/item.php b/mod/item.php index 57fb64e3d..7faec2e4e 100644 --- a/mod/item.php +++ b/mod/item.php @@ -888,7 +888,7 @@ function drop_item(int $id, string $return = '') $item = Item::selectFirstForUser(local_user(), $fields, ['id' => $id]); if (!DBA::isResult($item)) { - notice(DI::l10n()->t('Item not found.') . EOL); + notice(DI::l10n()->t('Item not found.')); DI::baseUrl()->redirect('network'); } diff --git a/mod/lostpass.php b/mod/lostpass.php index 211477b0d..036a308df 100644 --- a/mod/lostpass.php +++ b/mod/lostpass.php @@ -37,7 +37,7 @@ function lostpass_post(App $a) $condition = ['(`email` = ? OR `nickname` = ?) AND `verified` = 1 AND `blocked` = 0', $loginame, $loginame]; $user = DBA::selectFirst('user', ['uid', 'username', 'nickname', 'email', 'language'], $condition); if (!DBA::isResult($user)) { - notice(DI::l10n()->t('No valid account found.') . EOL); + notice(DI::l10n()->t('No valid account found.')); DI::baseUrl()->redirect(); } @@ -49,7 +49,7 @@ function lostpass_post(App $a) ]; $result = DBA::update('user', $fields, ['uid' => $user['uid']]); if ($result) { - info(DI::l10n()->t('Password reset request issued. Check your email.') . EOL); + info(DI::l10n()->t('Password reset request issued. Check your email.')); } $sitename = DI::config()->get('config', 'sitename'); @@ -152,7 +152,7 @@ function lostpass_generate_password($user) '$newpass' => $new_password, ]); - info("Your password has been reset." . EOL); + info("Your password has been reset."); $sitename = DI::config()->get('config', 'sitename'); $preamble = Strings::deindent(DI::l10n()->t(' diff --git a/mod/match.php b/mod/match.php index 747e0b2f0..b7a9d3a56 100644 --- a/mod/match.php +++ b/mod/match.php @@ -60,7 +60,7 @@ function match_content(App $a) return ''; } if (!$profile['pub_keywords'] && (!$profile['prv_keywords'])) { - notice(DI::l10n()->t('No keywords to match. Please add keywords to your profile.') . EOL); + notice(DI::l10n()->t('No keywords to match. Please add keywords to your profile.')); return ''; } @@ -141,7 +141,7 @@ function match_content(App $a) } if (empty($entries)) { - info(DI::l10n()->t('No matches') . EOL); + info(DI::l10n()->t('No matches')); } $tpl = Renderer::getMarkupTemplate('viewcontact_template.tpl'); diff --git a/mod/message.php b/mod/message.php index 4404e6aff..204b136f9 100644 --- a/mod/message.php +++ b/mod/message.php @@ -68,7 +68,7 @@ function message_init(App $a) function message_post(App $a) { if (!local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return; } @@ -82,17 +82,17 @@ function message_post(App $a) switch ($ret) { case -1: - notice(DI::l10n()->t('No recipient selected.') . EOL); + notice(DI::l10n()->t('No recipient selected.')); $norecip = true; break; case -2: - notice(DI::l10n()->t('Unable to locate contact information.') . EOL); + notice(DI::l10n()->t('Unable to locate contact information.')); break; case -3: - notice(DI::l10n()->t('Message could not be sent.') . EOL); + notice(DI::l10n()->t('Message could not be sent.')); break; case -4: - notice(DI::l10n()->t('Message collection failure.') . EOL); + notice(DI::l10n()->t('Message collection failure.')); break; } @@ -111,7 +111,7 @@ function message_content(App $a) Nav::setSelected('messages'); if (!local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return Login::form(); } @@ -176,12 +176,12 @@ function message_content(App $a) if ($cmd === 'drop') { $message = DBA::selectFirst('mail', ['convid'], ['id' => $a->argv[2], 'uid' => local_user()]); if(!DBA::isResult($message)){ - notice(DI::l10n()->t('Conversation not found.') . EOL); + notice(DI::l10n()->t('Conversation not found.')); DI::baseUrl()->redirect('message'); } if (!DBA::delete('mail', ['id' => $a->argv[2], 'uid' => local_user()])) { - notice(DI::l10n()->t('Message was not deleted.') . EOL); + notice(DI::l10n()->t('Message was not deleted.')); } $conversation = DBA::selectFirst('mail', ['id'], ['convid' => $message['convid'], 'uid' => local_user()]); @@ -199,7 +199,7 @@ function message_content(App $a) $parent = $r[0]['parent-uri']; if (!DBA::delete('mail', ['parent-uri' => $parent, 'uid' => local_user()])) { - notice(DI::l10n()->t('Conversation was not removed.') . EOL); + notice(DI::l10n()->t('Conversation was not removed.')); } } DI::baseUrl()->redirect('message'); @@ -298,7 +298,7 @@ function message_content(App $a) $r = get_messages(local_user(), $pager->getStart(), $pager->getItemsPerPage()); if (!DBA::isResult($r)) { - notice(DI::l10n()->t('No messages.') . EOL); + notice(DI::l10n()->t('No messages.')); return $o; } @@ -355,7 +355,7 @@ function message_content(App $a) } if (!DBA::isResult($messages)) { - notice(DI::l10n()->t('Message not available.') . EOL); + notice(DI::l10n()->t('Message not available.')); return $o; } diff --git a/mod/network.php b/mod/network.php index 2afc90dbf..1aa8046db 100644 --- a/mod/network.php +++ b/mod/network.php @@ -47,7 +47,7 @@ use Friendica\Util\Strings; function network_init(App $a) { if (!local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return; } @@ -548,7 +548,7 @@ function networkThreadedView(App $a, $update, $parent) if ($update) { exit(); } - notice(DI::l10n()->t('No such group') . EOL); + notice(DI::l10n()->t('No such group')); DI::baseUrl()->redirect('network/0'); // NOTREACHED } @@ -598,7 +598,7 @@ function networkThreadedView(App $a, $update, $parent) 'id' => 'network', ]) . $o; } else { - notice(DI::l10n()->t('Invalid contact.') . EOL); + notice(DI::l10n()->t('Invalid contact.')); DI::baseUrl()->redirect('network'); // NOTREACHED } diff --git a/mod/notes.php b/mod/notes.php index 67f8fcab2..f1cf6cfde 100644 --- a/mod/notes.php +++ b/mod/notes.php @@ -40,7 +40,7 @@ function notes_init(App $a) function notes_content(App $a, $update = false) { if (!local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return; } diff --git a/mod/ostatus_subscribe.php b/mod/ostatus_subscribe.php index 751afcc73..a1b378001 100644 --- a/mod/ostatus_subscribe.php +++ b/mod/ostatus_subscribe.php @@ -28,7 +28,7 @@ use Friendica\Util\Network; function ostatus_subscribe_content(App $a) { if (!local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); DI::baseUrl()->redirect('ostatus_subscribe'); // NOTREACHED } diff --git a/mod/photos.php b/mod/photos.php index 4118b8069..e5ab6b8dc 100644 --- a/mod/photos.php +++ b/mod/photos.php @@ -175,14 +175,14 @@ function photos_post(App $a) } if (!$can_post) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); exit(); } $owner_record = User::getOwnerDataById($page_owner_uid); if (!$owner_record) { - notice(DI::l10n()->t('Contact information unavailable') . EOL); + notice(DI::l10n()->t('Contact information unavailable')); Logger::log('photos_post: unable to locate contact record for page owner. uid=' . $page_owner_uid); exit(); } @@ -204,7 +204,7 @@ function photos_post(App $a) ); if (!DBA::isResult($r)) { - notice(DI::l10n()->t('Album not found.') . EOL); + notice(DI::l10n()->t('Album not found.')); DI::baseUrl()->redirect('photos/' . $a->data['user']['nickname'] . '/album'); return; // NOTREACHED } @@ -296,7 +296,7 @@ function photos_post(App $a) // Update the photo albums cache Photo::clearAlbumCache($page_owner_uid); } else { - notice('Failed to delete the photo.'); + notice(DI::l10n()->t('Failed to delete the photo.')); DI::baseUrl()->redirect('photos/' . $a->argv[1] . '/image/' . $a->argv[3]); } @@ -675,21 +675,21 @@ function photos_post(App $a) if ($error !== UPLOAD_ERR_OK) { switch ($error) { case UPLOAD_ERR_INI_SIZE: - notice(DI::l10n()->t('Image exceeds size limit of %s', ini_get('upload_max_filesize')) . EOL); + notice(DI::l10n()->t('Image exceeds size limit of %s', ini_get('upload_max_filesize'))); break; case UPLOAD_ERR_FORM_SIZE: - notice(DI::l10n()->t('Image exceeds size limit of %s', Strings::formatBytes($_REQUEST['MAX_FILE_SIZE'] ?? 0)) . EOL); + notice(DI::l10n()->t('Image exceeds size limit of %s', Strings::formatBytes($_REQUEST['MAX_FILE_SIZE'] ?? 0))); break; case UPLOAD_ERR_PARTIAL: - notice(DI::l10n()->t('Image upload didn\'t complete, please try again') . EOL); + notice(DI::l10n()->t('Image upload didn\'t complete, please try again')); break; case UPLOAD_ERR_NO_FILE: - notice(DI::l10n()->t('Image file is missing') . EOL); + notice(DI::l10n()->t('Image file is missing')); break; case UPLOAD_ERR_NO_TMP_DIR: case UPLOAD_ERR_CANT_WRITE: case UPLOAD_ERR_EXTENSION: - notice(DI::l10n()->t('Server can\'t accept new file upload at this time, please contact your administrator') . EOL); + notice(DI::l10n()->t('Server can\'t accept new file upload at this time, please contact your administrator')); break; } @unlink($src); @@ -705,7 +705,7 @@ function photos_post(App $a) $maximagesize = DI::config()->get('system', 'maximagesize'); if ($maximagesize && ($filesize > $maximagesize)) { - notice(DI::l10n()->t('Image exceeds size limit of %s', Strings::formatBytes($maximagesize)) . EOL); + notice(DI::l10n()->t('Image exceeds size limit of %s', Strings::formatBytes($maximagesize))); @unlink($src); $foo = 0; Hook::callAll('photo_post_end', $foo); @@ -713,7 +713,7 @@ function photos_post(App $a) } if (!$filesize) { - notice(DI::l10n()->t('Image file is empty.') . EOL); + notice(DI::l10n()->t('Image file is empty.')); @unlink($src); $foo = 0; Hook::callAll('photo_post_end', $foo); @@ -728,7 +728,7 @@ function photos_post(App $a) if (!$image->isValid()) { Logger::log('mod/photos.php: photos_post(): unable to process image' , Logger::DEBUG); - notice(DI::l10n()->t('Unable to process image.') . EOL); + notice(DI::l10n()->t('Unable to process image.')); @unlink($src); $foo = 0; Hook::callAll('photo_post_end',$foo); @@ -757,7 +757,7 @@ function photos_post(App $a) if (!$r) { Logger::log('mod/photos.php: photos_post(): image store failed', Logger::DEBUG); - notice(DI::l10n()->t('Image upload failed.') . EOL); + notice(DI::l10n()->t('Image upload failed.')); return; } @@ -840,12 +840,12 @@ function photos_content(App $a) // photos/name/image/xxxxx/drop if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) { - notice(DI::l10n()->t('Public access denied.') . EOL); + notice(DI::l10n()->t('Public access denied.')); return; } if (empty($a->data['user'])) { - notice(DI::l10n()->t('No photos selected') . EOL); + notice(DI::l10n()->t('No photos selected')); return; } @@ -911,7 +911,7 @@ function photos_content(App $a) } if ($a->data['user']['hidewall'] && (local_user() != $owner_uid) && !$remote_contact) { - notice(DI::l10n()->t('Access to this item is restricted.') . EOL); + notice(DI::l10n()->t('Access to this item is restricted.')); return; } @@ -1136,7 +1136,7 @@ function photos_content(App $a) if (DBA::exists('photo', ['resource-id' => $datum, 'uid' => $owner_uid])) { notice(DI::l10n()->t('Permission denied. Access to this item may be restricted.')); } else { - notice(DI::l10n()->t('Photo not available') . EOL); + notice(DI::l10n()->t('Photo not available')); } return; } diff --git a/mod/repair_ostatus.php b/mod/repair_ostatus.php index 33e97499e..0d30fc298 100644 --- a/mod/repair_ostatus.php +++ b/mod/repair_ostatus.php @@ -28,7 +28,7 @@ use Friendica\Model\Contact; function repair_ostatus_content(App $a) { if (! local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); DI::baseUrl()->redirect('ostatus_repair'); // NOTREACHED } diff --git a/mod/settings.php b/mod/settings.php index 14db27e6f..d02375ed7 100644 --- a/mod/settings.php +++ b/mod/settings.php @@ -63,7 +63,7 @@ function settings_post(App $a) } if (count($a->user) && !empty($a->user['uid']) && $a->user['uid'] != local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return; } @@ -198,7 +198,7 @@ function settings_post(App $a) unset($dcrpass); if (!$mbox) { $failed = true; - notice(DI::l10n()->t('Failed to connect with email account using the settings provided.') . EOL); + notice(DI::l10n()->t('Failed to connect with email account using the settings provided.')); } } } @@ -420,10 +420,10 @@ function settings_post(App $a) $hidewall = 1; if (!$str_contact_allow && !$str_group_allow && !$str_contact_deny && !$str_group_deny) { if ($def_gid) { - info(DI::l10n()->t('Private forum has no privacy permissions. Using default privacy group.'). EOL); + info(DI::l10n()->t('Private forum has no privacy permissions. Using default privacy group.')); $str_group_allow = '<' . $def_gid . '>'; } else { - notice(DI::l10n()->t('Private forum has no privacy permissions and no default privacy group.') . EOL); + notice(DI::l10n()->t('Private forum has no privacy permissions and no default privacy group.')); } } } @@ -440,7 +440,7 @@ function settings_post(App $a) } if (!DBA::update('user', $fields, ['uid' => local_user()])) { - notice(DI::l10n()->t('Settings were not updated.') . EOL); + notice(DI::l10n()->t('Settings were not updated.')); } // clear session language @@ -485,12 +485,12 @@ function settings_content(App $a) Nav::setSelected('settings'); if (!local_user()) { - //notice(DI::l10n()->t('Permission denied.') . EOL); + //notice(DI::l10n()->t('Permission denied.')); return Login::form(); } if (!empty($_SESSION['submanage'])) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return; } @@ -718,7 +718,7 @@ function settings_content(App $a) $profile = DBA::selectFirst('profile', [], ['uid' => local_user()]); if (!DBA::isResult($profile)) { - notice(DI::l10n()->t('Unable to find your profile. Please contact your admin.') . EOL); + notice(DI::l10n()->t('Unable to find your profile. Please contact your admin.')); return; } diff --git a/mod/suggest.php b/mod/suggest.php index 9cd2fb1cd..66d22b001 100644 --- a/mod/suggest.php +++ b/mod/suggest.php @@ -51,7 +51,7 @@ function suggest_content(App $a) $o = ''; if (! local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return; } diff --git a/mod/uimport.php b/mod/uimport.php index eb99a366f..8abff0cd9 100644 --- a/mod/uimport.php +++ b/mod/uimport.php @@ -29,7 +29,7 @@ use Friendica\DI; function uimport_post(App $a) { if ((DI::config()->get('config', 'register_policy') != \Friendica\Module\Register::OPEN) && !is_site_admin()) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return; } @@ -42,7 +42,7 @@ function uimport_post(App $a) function uimport_content(App $a) { if ((DI::config()->get('config', 'register_policy') != \Friendica\Module\Register::OPEN) && !is_site_admin()) { - notice(DI::l10n()->t('User imports on closed servers can only be done by an administrator.') . EOL); + notice(DI::l10n()->t('User imports on closed servers can only be done by an administrator.')); return; } @@ -51,7 +51,7 @@ function uimport_content(App $a) $r = q("select count(*) as total from user where register_date > UTC_TIMESTAMP - INTERVAL 1 day"); if ($r && $r[0]['total'] >= $max_dailies) { Logger::log('max daily registrations exceeded.'); - notice(DI::l10n()->t('This site has exceeded the number of allowed daily account registrations. Please try again tomorrow.') . EOL); + notice(DI::l10n()->t('This site has exceeded the number of allowed daily account registrations. Please try again tomorrow.')); return; } } diff --git a/mod/videos.php b/mod/videos.php index a3344a8b4..3dd17179a 100644 --- a/mod/videos.php +++ b/mod/videos.php @@ -126,7 +126,7 @@ function videos_content(App $a) if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) { - notice(DI::l10n()->t('Public access denied.') . EOL); + notice(DI::l10n()->t('Public access denied.')); return; } @@ -179,7 +179,7 @@ function videos_content(App $a) } if ($a->data['user']['hidewall'] && (local_user() != $owner_uid) && !$remote_contact) { - notice(DI::l10n()->t('Access to this item is restricted.') . EOL); + notice(DI::l10n()->t('Access to this item is restricted.')); return; } diff --git a/mod/wall_attach.php b/mod/wall_attach.php index c02a06c37..8cb19ab1a 100644 --- a/mod/wall_attach.php +++ b/mod/wall_attach.php @@ -106,7 +106,7 @@ function wall_attach_post(App $a) { if ($r_json) { echo json_encode(['error' => $msg]); } else { - notice($msg . EOL); + notice($msg); } @unlink($src); exit(); diff --git a/mod/wall_upload.php b/mod/wall_upload.php index 093d5db77..c3ba32304 100644 --- a/mod/wall_upload.php +++ b/mod/wall_upload.php @@ -99,7 +99,7 @@ function wall_upload_post(App $a, $desktopmode = true) echo json_encode(['error' => DI::l10n()->t('Permission denied.')]); exit(); } - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); exit(); } @@ -159,7 +159,7 @@ function wall_upload_post(App $a, $desktopmode = true) echo json_encode(['error' => DI::l10n()->t('Invalid request.')]); exit(); } - notice(DI::l10n()->t('Invalid request.').EOL); + notice(DI::l10n()->t('Invalid request.')); exit(); } diff --git a/mod/wallmessage.php b/mod/wallmessage.php index 82d87ca29..cbf53b45d 100644 --- a/mod/wallmessage.php +++ b/mod/wallmessage.php @@ -32,7 +32,7 @@ function wallmessage_post(App $a) { $replyto = Profile::getMyURL(); if (!$replyto) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return; } @@ -56,7 +56,7 @@ function wallmessage_post(App $a) { $user = $r[0]; if (! intval($user['unkmail'])) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return; } @@ -73,16 +73,16 @@ function wallmessage_post(App $a) { switch ($ret) { case -1: - notice(DI::l10n()->t('No recipient selected.') . EOL); + notice(DI::l10n()->t('No recipient selected.')); break; case -2: - notice(DI::l10n()->t('Unable to check your home location.') . EOL); + notice(DI::l10n()->t('Unable to check your home location.')); break; case -3: - notice(DI::l10n()->t('Message could not be sent.') . EOL); + notice(DI::l10n()->t('Message could not be sent.')); break; case -4: - notice(DI::l10n()->t('Message collection failure.') . EOL); + notice(DI::l10n()->t('Message collection failure.')); break; } @@ -93,14 +93,14 @@ function wallmessage_post(App $a) { function wallmessage_content(App $a) { if (!Profile::getMyURL()) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return; } $recipient = (($a->argc > 1) ? $a->argv[1] : ''); if (!$recipient) { - notice(DI::l10n()->t('No recipient.') . EOL); + notice(DI::l10n()->t('No recipient.')); return; } @@ -109,7 +109,7 @@ function wallmessage_content(App $a) { ); if (! DBA::isResult($r)) { - notice(DI::l10n()->t('No recipient.') . EOL); + notice(DI::l10n()->t('No recipient.')); Logger::log('wallmessage: no recipient'); return; } @@ -117,7 +117,7 @@ function wallmessage_content(App $a) { $user = $r[0]; if (!intval($user['unkmail'])) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return; } diff --git a/src/App/Authentication.php b/src/App/Authentication.php index a0ce5df65..e3d273747 100644 --- a/src/App/Authentication.php +++ b/src/App/Authentication.php @@ -207,7 +207,7 @@ class Authentication // if it's an email address or doesn't resolve to a URL, fail. if ($noid || strpos($openid_url, '@') || !Network::isUrlValid($openid_url)) { - notice($this->l10n->t('Login failed.') . EOL); + notice($this->l10n->t('Login failed.')); $this->baseUrl->redirect(); } @@ -270,7 +270,7 @@ class Authentication } } catch (Exception $e) { $this->logger->warning('authenticate: failed login attempt', ['action' => 'login', 'username' => Strings::escapeTags($username), 'ip' => $_SERVER['REMOTE_ADDR']]); - notice($this->l10n->t('Login failed. Please check your credentials.' . EOL)); + notice($this->l10n->t('Login failed. Please check your credentials.')); $this->baseUrl->redirect(); } diff --git a/src/Model/Group.php b/src/Model/Group.php index b4dbb87d8..5376b817f 100644 --- a/src/Model/Group.php +++ b/src/Model/Group.php @@ -89,7 +89,7 @@ class Group $group = DBA::selectFirst('group', ['deleted'], ['id' => $gid]); if (DBA::isResult($group) && $group['deleted']) { DBA::update('group', ['deleted' => 0], ['id' => $gid]); - notice(DI::l10n()->t('A deleted group with this name was revived. Existing item permissions may apply to this group and any future members. If this is not what you intended, please create another group with a different name.') . EOL); + notice(DI::l10n()->t('A deleted group with this name was revived. Existing item permissions may apply to this group and any future members. If this is not what you intended, please create another group with a different name.')); } return true; } diff --git a/src/Module/Admin/Blocklist/Server.php b/src/Module/Admin/Blocklist/Server.php index eccb65598..b145d6d01 100644 --- a/src/Module/Admin/Blocklist/Server.php +++ b/src/Module/Admin/Blocklist/Server.php @@ -46,7 +46,7 @@ class Server extends BaseAdmin 'reason' => Strings::escapeTags(trim($_POST['newentry_reason'])) ]; DI::config()->set('system', 'blocklist', $blocklist); - info(DI::l10n()->t('Server domain pattern added to blocklist.') . EOL); + info(DI::l10n()->t('Server domain pattern added to blocklist.')); } else { // Edit the entries from blocklist $blocklist = []; diff --git a/src/Module/Admin/DBSync.php b/src/Module/Admin/DBSync.php index dd7febcc5..950e258f8 100644 --- a/src/Module/Admin/DBSync.php +++ b/src/Module/Admin/DBSync.php @@ -47,7 +47,7 @@ class DBSync extends BaseAdmin if (intval($curr) == $update) { DI::config()->set('system', 'build', intval($curr) + 1); } - info(DI::l10n()->t('Update has been marked successful') . EOL); + info(DI::l10n()->t('Update has been marked successful')); } DI::baseUrl()->redirect('admin/dbsync'); } diff --git a/src/Module/Admin/Item/Delete.php b/src/Module/Admin/Item/Delete.php index 0ad20f97c..b907d39bd 100644 --- a/src/Module/Admin/Item/Delete.php +++ b/src/Module/Admin/Item/Delete.php @@ -51,7 +51,7 @@ class Delete extends BaseAdmin Item::markForDeletion(['guid' => $guid]); } - info(DI::l10n()->t('Item marked for deletion.') . EOL); + info(DI::l10n()->t('Item marked for deletion.')); DI::baseUrl()->redirect('admin/item/delete'); } diff --git a/src/Module/Admin/Users.php b/src/Module/Admin/Users.php index dca8c9c2e..41a691b0b 100644 --- a/src/Module/Admin/Users.php +++ b/src/Module/Admin/Users.php @@ -109,7 +109,7 @@ class Users extends BaseAdmin $uid = $a->argv[3]; $user = User::getById($uid, ['username', 'blocked']); if (!DBA::isResult($user)) { - notice('User not found' . EOL); + notice(DI::l10n()->t('User not found')); DI::baseUrl()->redirect('admin/users'); return ''; // NOTREACHED } diff --git a/src/Module/Apps.php b/src/Module/Apps.php index 04c7d7b6a..29a735121 100644 --- a/src/Module/Apps.php +++ b/src/Module/Apps.php @@ -44,7 +44,7 @@ class Apps extends BaseModule $apps = Nav::getAppMenu(); if (count($apps) == 0) { - notice(DI::l10n()->t('No installed applications.') . EOL); + notice(DI::l10n()->t('No installed applications.')); } $tpl = Renderer::getMarkupTemplate('apps.tpl'); diff --git a/src/Module/Contact.php b/src/Module/Contact.php index ee8ad3663..096f69330 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -112,7 +112,7 @@ class Contact extends BaseModule } if (!DBA::exists('contact', ['id' => $contact_id, 'uid' => local_user(), 'deleted' => false])) { - notice(DI::l10n()->t('Could not access contact record.') . EOL); + notice(DI::l10n()->t('Could not access contact record.')); DI::baseUrl()->redirect('contact'); return; // NOTREACHED } @@ -145,7 +145,7 @@ class Contact extends BaseModule ); if (!DBA::isResult($r)) { - notice(DI::l10n()->t('Failed to update contact record.') . EOL); + notice(DI::l10n()->t('Failed to update contact record.')); } $contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => local_user(), 'deleted' => false]); @@ -364,7 +364,7 @@ class Contact extends BaseModule Nav::setSelected('contact'); if (!local_user()) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return Login::form(); } @@ -398,7 +398,7 @@ class Contact extends BaseModule self::blockContact($contact_id); $blocked = Model\Contact::isBlockedByUser($contact_id, local_user()); - info(($blocked ? DI::l10n()->t('Contact has been blocked') : DI::l10n()->t('Contact has been unblocked')) . EOL); + info(($blocked ? DI::l10n()->t('Contact has been blocked') : DI::l10n()->t('Contact has been unblocked'))); DI::baseUrl()->redirect('contact/' . $contact_id); // NOTREACHED @@ -408,7 +408,7 @@ class Contact extends BaseModule self::ignoreContact($contact_id); $ignored = Model\Contact::isIgnoredByUser($contact_id, local_user()); - info(($ignored ? DI::l10n()->t('Contact has been ignored') : DI::l10n()->t('Contact has been unignored')) . EOL); + info(($ignored ? DI::l10n()->t('Contact has been ignored') : DI::l10n()->t('Contact has been unignored'))); DI::baseUrl()->redirect('contact/' . $contact_id); // NOTREACHED @@ -418,7 +418,7 @@ class Contact extends BaseModule $r = self::archiveContact($contact_id, $orig_record); if ($r) { $archived = (($orig_record['archive']) ? 0 : 1); - info((($archived) ? DI::l10n()->t('Contact has been archived') : DI::l10n()->t('Contact has been unarchived')) . EOL); + info((($archived) ? DI::l10n()->t('Contact has been archived') : DI::l10n()->t('Contact has been unarchived'))); } DI::baseUrl()->redirect('contact/' . $contact_id); @@ -459,7 +459,7 @@ class Contact extends BaseModule } self::dropContact($orig_record); - info(DI::l10n()->t('Contact has been removed.') . EOL); + info(DI::l10n()->t('Contact has been removed.')); DI::baseUrl()->redirect('contact'); // NOTREACHED diff --git a/src/Module/Contact/Advanced.php b/src/Module/Contact/Advanced.php index 864080843..5fa86ab37 100644 --- a/src/Module/Contact/Advanced.php +++ b/src/Module/Contact/Advanced.php @@ -91,7 +91,7 @@ class Advanced extends BaseModule } if (!$r) { - notice(DI::l10n()->t('Contact update failed.') . EOL); + notice(DI::l10n()->t('Contact update failed.')); } return; diff --git a/src/Module/Directory.php b/src/Module/Directory.php index 491023145..507da6b94 100644 --- a/src/Module/Directory.php +++ b/src/Module/Directory.php @@ -75,7 +75,7 @@ class Directory extends BaseModule $profiles = Profile::searchProfiles($pager->getStart(), $pager->getItemsPerPage(), $search); if ($profiles['total'] === 0) { - notice(DI::l10n()->t('No entries (some entries may be hidden).') . EOL); + notice(DI::l10n()->t('No entries (some entries may be hidden).')); } else { if (in_array('small', $app->argv)) { $photo = 'thumb'; diff --git a/src/Module/FollowConfirm.php b/src/Module/FollowConfirm.php index 28c849a86..f4e4c5ebf 100644 --- a/src/Module/FollowConfirm.php +++ b/src/Module/FollowConfirm.php @@ -13,7 +13,7 @@ class FollowConfirm extends BaseModule { $uid = local_user(); if (!$uid) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return; } diff --git a/src/Module/Invite.php b/src/Module/Invite.php index 98668bf71..cb944a3fd 100644 --- a/src/Module/Invite.php +++ b/src/Module/Invite.php @@ -75,7 +75,7 @@ class Invite extends BaseModule $recipient = trim($recipient); if (!filter_var($recipient, FILTER_VALIDATE_EMAIL)) { - notice(DI::l10n()->t('%s : Not a valid email address.', $recipient) . EOL); + notice(DI::l10n()->t('%s : Not a valid email address.', $recipient)); continue; } @@ -111,15 +111,15 @@ class Invite extends BaseModule $current_invites++; DI::pConfig()->set(local_user(), 'system', 'sent_invites', $current_invites); if ($current_invites > $max_invites) { - notice(DI::l10n()->t('Invitation limit exceeded. Please contact your site administrator.') . EOL); + notice(DI::l10n()->t('Invitation limit exceeded. Please contact your site administrator.')); return; } } else { - notice(DI::l10n()->t('%s : Message delivery failed.', $recipient) . EOL); + notice(DI::l10n()->t('%s : Message delivery failed.', $recipient)); } } - notice(DI::l10n()->tt('%d message sent.', '%d messages sent.', $total) . EOL); + notice(DI::l10n()->tt('%d message sent.', '%d messages sent.', $total)); } public static function content(array $parameters = []) diff --git a/src/Module/Notifications/Introductions.php b/src/Module/Notifications/Introductions.php index 4b05b8c2c..cd59626e6 100644 --- a/src/Module/Notifications/Introductions.php +++ b/src/Module/Notifications/Introductions.php @@ -191,7 +191,7 @@ class Introductions extends BaseNotifications } if (count($notifications['notifications']) == 0) { - notice(DI::l10n()->t('No introductions.') . EOL); + notice(DI::l10n()->t('No introductions.')); $notificationNoContent = DI::l10n()->t('No more %s notifications.', $notifications['ident']); } diff --git a/src/Module/Profile/Contacts.php b/src/Module/Profile/Contacts.php index dbf0cf8d8..4cd97b409 100644 --- a/src/Module/Profile/Contacts.php +++ b/src/Module/Profile/Contacts.php @@ -64,7 +64,7 @@ class Contacts extends BaseProfile $o = self::getTabsHTML($a, 'contacts', $is_owner, $nickname); if (!count($a->profile) || $a->profile['hide-friends']) { - notice(DI::l10n()->t('Permission denied.') . EOL); + notice(DI::l10n()->t('Permission denied.')); return $o; } @@ -92,7 +92,7 @@ class Contacts extends BaseProfile $contacts_stmt = DBA::select('contact', [], $condition, $params); if (!DBA::isResult($contacts_stmt)) { - notice(DI::l10n()->t('No contacts.') . EOL); + notice(DI::l10n()->t('No contacts.')); return $o; } diff --git a/src/Module/Profile/Status.php b/src/Module/Profile/Status.php index 9ab15a4e3..4b36cdd4d 100644 --- a/src/Module/Profile/Status.php +++ b/src/Module/Profile/Status.php @@ -102,7 +102,7 @@ class Status extends BaseProfile $last_updated_key = "profile:" . $a->profile['uid'] . ":" . local_user() . ":" . $remote_contact; if (!empty($a->profile['hidewall']) && !$is_owner && !$remote_contact) { - notice(DI::l10n()->t('Access to this profile has been restricted.') . EOL); + notice(DI::l10n()->t('Access to this profile has been restricted.')); return ''; } From cb830c9ad3f309636c2bc6e6176ee23684b2869e Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 23 Jul 2020 06:32:31 +0000 Subject: [PATCH 24/44] Translation function added --- mod/lostpass.php | 2 +- mod/network.php | 2 +- src/Module/Admin/Addons/Index.php | 2 +- src/Module/Admin/Site.php | 2 +- src/Module/Admin/Themes/Index.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mod/lostpass.php b/mod/lostpass.php index 036a308df..01e0006e9 100644 --- a/mod/lostpass.php +++ b/mod/lostpass.php @@ -152,7 +152,7 @@ function lostpass_generate_password($user) '$newpass' => $new_password, ]); - info("Your password has been reset."); + info(DI::l10n()->t("Your password has been reset.")); $sitename = DI::config()->get('config', 'sitename'); $preamble = Strings::deindent(DI::l10n()->t(' diff --git a/mod/network.php b/mod/network.php index 1aa8046db..a3eb16983 100644 --- a/mod/network.php +++ b/mod/network.php @@ -305,7 +305,7 @@ function network_content(App $a, $update = 0, $parent = 0) } if ($o === '') { - notice("No items found"); + notice(DI::l10n()->t("No items found")); } return $o; diff --git a/src/Module/Admin/Addons/Index.php b/src/Module/Admin/Addons/Index.php index d52085389..1636d6cc6 100644 --- a/src/Module/Admin/Addons/Index.php +++ b/src/Module/Admin/Addons/Index.php @@ -39,7 +39,7 @@ class Index extends BaseAdmin switch ($_GET['action']) { case 'reload': Addon::reload(); - info('Addons reloaded'); + info(DI::l10n()->t('Addons reloaded')); break; case 'toggle' : diff --git a/src/Module/Admin/Site.php b/src/Module/Admin/Site.php index c4b320e72..290d92d13 100644 --- a/src/Module/Admin/Site.php +++ b/src/Module/Admin/Site.php @@ -122,7 +122,7 @@ class Site extends BaseAdmin } DBA::close($usersStmt); - info("Relocation started. Could take a while to complete."); + info(DI::l10n()->t("Relocation started. Could take a while to complete.")); DI::baseUrl()->redirect('admin/site'); } diff --git a/src/Module/Admin/Themes/Index.php b/src/Module/Admin/Themes/Index.php index 78d27dfa0..9993f1f11 100644 --- a/src/Module/Admin/Themes/Index.php +++ b/src/Module/Admin/Themes/Index.php @@ -48,7 +48,7 @@ class Index extends BaseAdmin } Theme::setAllowedList($allowed_themes); - info('Themes reloaded'); + info(DI::l10n()->t('Themes reloaded')); break; case 'toggle' : From bdbe6771fdc5aa164258f644821f0b12a012290d Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 25 Jul 2020 08:07:22 +0000 Subject: [PATCH 25/44] Fix Notice: "Undefined index: host" --- src/Network/Probe.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Network/Probe.php b/src/Network/Probe.php index c41006b12..3c43fb28e 100644 --- a/src/Network/Probe.php +++ b/src/Network/Probe.php @@ -1784,6 +1784,9 @@ class Probe $base = $xpath->evaluate('string(/html/head/base/@href)') ?: $base; $baseParts = parse_url($base); + if (empty($baseParts['host'])) { + return $href; + } // Naked domain case (scheme://basehost) $path = $baseParts['path'] ?? '/'; From 9b86f40a5fbbde547ba71bff310cf13f338175f8 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 25 Jul 2020 11:48:52 +0000 Subject: [PATCH 26/44] Store avatar cache fields only when needed --- mod/dfrn_confirm.php | 4 +-- mod/dfrn_request.php | 4 +-- src/Model/Contact.php | 45 +++++++++++++++++++++++++++------ src/Model/Item.php | 4 +++ src/Module/Contact/Advanced.php | 2 +- src/Protocol/DFRN.php | 6 ++--- src/Protocol/Diaspora.php | 2 +- src/Protocol/OStatus.php | 4 +-- 8 files changed, 52 insertions(+), 19 deletions(-) diff --git a/mod/dfrn_confirm.php b/mod/dfrn_confirm.php index 668c6d2e2..bd52a67cf 100644 --- a/mod/dfrn_confirm.php +++ b/mod/dfrn_confirm.php @@ -304,7 +304,7 @@ function dfrn_confirm_post(App $a, $handsfree = null) * * We will also update the contact record with the nature and scope of the relationship. */ - Contact::updateAvatar($contact['photo'], $uid, $contact_id); + Contact::updateAvatar($contact_id, $contact['photo']); Logger::log('dfrn_confirm: confirm - imported photos'); @@ -484,7 +484,7 @@ function dfrn_confirm_post(App $a, $handsfree = null) $photo = DI::baseUrl() . '/images/person-300.jpg'; } - Contact::updateAvatar($photo, $local_uid, $dfrn_record); + Contact::updateAvatar($dfrn_record, $photo); Logger::log('dfrn_confirm: request - photos imported'); diff --git a/mod/dfrn_request.php b/mod/dfrn_request.php index e93df399f..1e485e304 100644 --- a/mod/dfrn_request.php +++ b/mod/dfrn_request.php @@ -189,7 +189,7 @@ function dfrn_request_post(App $a) Group::addMember(User::getDefaultGroup(local_user(), $r[0]["network"]), $r[0]['id']); if (isset($photo)) { - Contact::updateAvatar($photo, local_user(), $r[0]["id"], true); + Contact::updateAvatar($r[0]["id"], $photo, true); } $forward_path = "contact/" . $r[0]['id']; @@ -420,7 +420,7 @@ function dfrn_request_post(App $a) ); if (DBA::isResult($r)) { $contact_record = $r[0]; - Contact::updateAvatar($photo, $uid, $contact_record["id"], true); + Contact::updateAvatar($contact_record["id"], $photo, true); } } } diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 09d527733..708c375b0 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -1464,7 +1464,7 @@ class Contact } if (!empty($data['photo']) && ($data['network'] != Protocol::FEED)) { - self::updateAvatar($data['photo'], $uid, $contact_id); + self::updateAvatar($contact_id, $data['photo']); } if (in_array($data["network"], array_merge(Protocol::NATIVE_SUPPORT, [Protocol::PUMPIO]))) { @@ -1775,12 +1775,32 @@ class Contact return $return; } + /** + * Ensure that cached avatar exist + * + * @param integer $cid + */ + public static function checkAvatarCache(int $cid) + { + $contact = DBA::selectFirst('contact', ['url', 'avatar', 'photo', 'thumb', 'micro'], ['id' => $cid, 'uid' => 0, 'self' => false]); + if (!DBA::isResult($contact)) { + return; + } + + if (empty($contact['avatar']) || (!empty($contact['photo']) && !empty($contact['thumb']) && !empty($contact['micro']))) { + return; + } + + Logger::info('Adding avatar cache', ['id' => $cid, 'contact' => $contact]); + + self::updateAvatar($cid, $contact['avatar'], true); + } + /** * Updates the avatar links in a contact only if needed * - * @param string $avatar Link to avatar picture - * @param int $uid User id of contact owner * @param int $cid Contact id + * @param string $avatar Link to avatar picture * @param bool $force force picture update * * @return void @@ -1788,13 +1808,22 @@ class Contact * @throws HTTPException\NotFoundException * @throws \ImagickException */ - public static function updateAvatar($avatar, $uid, $cid, $force = false) + public static function updateAvatar(int $cid, string $avatar, bool $force = false) { - $contact = DBA::selectFirst('contact', ['avatar', 'photo', 'thumb', 'micro', 'nurl'], ['id' => $cid, 'self' => false]); + $contact = DBA::selectFirst('contact', ['uid', 'avatar', 'photo', 'thumb', 'micro', 'nurl'], ['id' => $cid, 'self' => false]); if (!DBA::isResult($contact)) { return; } + $uid = $contact['uid']; + + // Only update the cached photo links of public contacts when they already are cached + if (($uid == 0) && !$force && empty($contact['photo']) && empty($contact['thumb']) && empty($contact['micro'])) { + DBA::update('contact', ['avatar' => $avatar], ['id' => $cid]); + Logger::info('Only update the avatar', ['id' => $cid, 'avatar' => $avatar, 'contact' => $contact]); + return; + } + $data = [ $contact['photo'] ?? '', $contact['thumb'] ?? '', @@ -2021,7 +2050,7 @@ class Contact } if (!empty($ret['photo']) && ($ret['network'] != Protocol::FEED)) { - self::updateAvatar($ret['photo'], $uid, $id, $update || $force); + self::updateAvatar($id, $ret['photo'], $update || $force); } if (!$update) { @@ -2311,7 +2340,7 @@ class Contact Group::addMember(User::getDefaultGroup($user['uid'], $contact["network"]), $contact_id); // Update the avatar - self::updateAvatar($ret['photo'], $user['uid'], $contact_id); + self::updateAvatar($contact_id, $ret['photo']); // pull feed and consume it, which should subscribe to the hub. @@ -2493,7 +2522,7 @@ class Contact // Ensure to always have the correct network type, independent from the connection request method self::updateFromProbe($contact_id, '', true); - Contact::updateAvatar($photo, $importer["uid"], $contact_id, true); + self::updateAvatar($contact_id, $photo, true); $contact_record = DBA::selectFirst('contact', ['id', 'network', 'name', 'url', 'photo'], ['id' => $contact_id]); diff --git a/src/Model/Item.php b/src/Model/Item.php index b5a954099..1a561c7f1 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -1703,6 +1703,10 @@ class Item 'photo' => $item['owner-avatar'], 'network' => $item['network']]; $item['owner-id'] = ($item['owner-id'] ?? 0) ?: Contact::getIdForURL($item['owner-link'], 0, null, $default); + // Ensure that there is an avatar cache + Contact::checkAvatarCache($item['author-id']); + Contact::checkAvatarCache($item['owner-id']); + // The contact-id should be set before "self::insert" was called - but there seems to be issues sometimes $item["contact-id"] = self::contactId($item); diff --git a/src/Module/Contact/Advanced.php b/src/Module/Contact/Advanced.php index 5fa86ab37..be1e874a5 100644 --- a/src/Module/Contact/Advanced.php +++ b/src/Module/Contact/Advanced.php @@ -87,7 +87,7 @@ class Advanced extends BaseModule if ($photo) { DI::logger()->notice('Updating photo.', ['photo' => $photo]); - Model\Contact::updateAvatar($photo, local_user(), $contact['id'], true); + Model\Contact::updateAvatar($contact['id'], $photo, true); } if (!$r) { diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index 8190806a0..f213af8db 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -1680,11 +1680,11 @@ class DFRN $condition = ['uid' => 0, 'nurl' => Strings::normaliseLink($contact_old['url'])]; DBA::update('contact', $fields, $condition, true); - Contact::updateAvatar($author['avatar'], $importer['importer_uid'], $contact['id']); + Contact::updateAvatar($contact['id'], $author['avatar']); $pcid = Contact::getIdForURL($contact_old['url']); if (!empty($pcid)) { - Contact::updateAvatar($author['avatar'], 0, $pcid); + Contact::updateAvatar($pcid, $author['avatar']); } /* @@ -1962,7 +1962,7 @@ class DFRN DBA::update('contact', $fields, $condition); - Contact::updateAvatar($relocate["avatar"], $importer["importer_uid"], $importer["id"], true); + Contact::updateAvatar($importer["id"], $relocate["avatar"], true); Logger::log('Contacts are updated.'); diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index 98a315ce2..f3b95db68 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -2410,7 +2410,7 @@ class Diaspora $image_url = "http://".$handle_parts[1].$image_url; } - Contact::updateAvatar($image_url, $importer["uid"], $contact["id"]); + Contact::updateAvatar($contact["id"], $image_url); // Generic birthday. We don't know the timezone. The year is irrelevant. diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 9c87f367a..1cf2894eb 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -216,7 +216,7 @@ class OStatus if (!empty($author["author-avatar"]) && ($author["author-avatar"] != $current['avatar'])) { Logger::log("Update profile picture for contact ".$contact["id"], Logger::DEBUG); - Contact::updateAvatar($author["author-avatar"], $importer["uid"], $contact["id"]); + Contact::updateAvatar($contact["id"], $author["author-avatar"]); } // Ensure that we are having this contact (with uid=0) @@ -237,7 +237,7 @@ class OStatus // Update the avatar if (!empty($author["author-avatar"])) { - Contact::updateAvatar($author["author-avatar"], 0, $cid); + Contact::updateAvatar($cid, $author["author-avatar"]); } } From 18617f6c48f35a0a5df39f95a6460ea66255616d Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 26 Jul 2020 07:34:33 +0000 Subject: [PATCH 27/44] Fetch followers/followings of contacts --- database.sql | 20 +-- src/Model/Contact.php | 54 ++++---- src/Model/ContactRelation.php | 163 +++++++++++++++++++++++ src/Model/GContact.php | 136 ------------------- src/Model/Item.php | 4 +- src/Module/Admin/Site.php | 21 ++- src/Worker/UpdateGContact.php | 4 - static/dbstructure.config.php | 21 +-- view/templates/admin/site.tpl | 2 +- view/theme/frio/templates/admin/site.tpl | 2 +- 10 files changed, 219 insertions(+), 208 deletions(-) create mode 100644 src/Model/ContactRelation.php diff --git a/database.sql b/database.sql index deaa39a5b..ea89847c1 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2020.09-dev (Red Hot Poker) --- DB_UPDATE_VERSION 1357 +-- DB_UPDATE_VERSION 1358 -- ------------------------------------------ @@ -102,6 +102,7 @@ CREATE TABLE IF NOT EXISTS `contact` ( `avatar-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `term-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `last-item` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'date of the last post', + `last-discovery` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'date of the last follower discovery', `priority` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', `blocked` boolean NOT NULL DEFAULT '1' COMMENT 'Node-wide block status', `block_reason` text COMMENT 'Node-wide block reason', @@ -342,8 +343,12 @@ CREATE TABLE IF NOT EXISTS `contact-relation` ( `cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact the related contact had interacted with', `relation-cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'related contact who had interacted with the contact', `last-interaction` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last interaction', + `follow-updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last update of the contact relationship', + `follows` boolean NOT NULL DEFAULT '0' COMMENT '', PRIMARY KEY(`cid`,`relation-cid`), - INDEX `relation-cid` (`relation-cid`) + INDEX `relation-cid` (`relation-cid`), + FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE, + FOREIGN KEY (`relation-cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Contact relations'; -- @@ -517,17 +522,6 @@ CREATE TABLE IF NOT EXISTS `gcontact` ( FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='global contacts'; --- --- TABLE gfollower --- -CREATE TABLE IF NOT EXISTS `gfollower` ( - `gcid` int unsigned NOT NULL DEFAULT 0 COMMENT 'global contact', - `follower-gcid` int unsigned NOT NULL DEFAULT 0 COMMENT 'global contact of the follower', - `deleted` boolean NOT NULL DEFAULT '0' COMMENT '1 indicates that the connection has been deleted', - PRIMARY KEY(`gcid`,`follower-gcid`), - INDEX `follower-gcid` (`follower-gcid`) -) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Followers of global contacts'; - -- -- TABLE glink -- diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 708c375b0..1cf8c7423 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -1418,7 +1418,6 @@ class Contact 'poll' => $data['poll'] ?? '', 'name' => $data['name'] ?? '', 'nick' => $data['nick'] ?? '', - 'photo' => $data['photo'] ?? '', 'keywords' => $data['keywords'] ?? '', 'location' => $data['location'] ?? '', 'about' => $data['about'] ?? '', @@ -1474,14 +1473,6 @@ class Contact } else { // Else do a direct update self::updateFromProbe($contact_id, '', false); - - // Update the gcontact entry - if ($uid == 0) { - GContact::updateFromPublicContactID($contact_id); - if (($data['network'] == Protocol::ACTIVITYPUB) && in_array(DI::config()->get('system', 'gcontact_discovery'), [GContact::DISCOVERY_DIRECT, GContact::DISCOVERY_RECURSIVE])) { - GContact::discoverFollowers($data['url']); - } - } } } else { $fields = ['url', 'nurl', 'addr', 'alias', 'name', 'nick', 'keywords', 'location', 'about', 'avatar-date', 'baseurl', 'gsid']; @@ -1818,9 +1809,11 @@ class Contact $uid = $contact['uid']; // Only update the cached photo links of public contacts when they already are cached - if (($uid == 0) && !$force && empty($contact['photo']) && empty($contact['thumb']) && empty($contact['micro'])) { - DBA::update('contact', ['avatar' => $avatar], ['id' => $cid]); - Logger::info('Only update the avatar', ['id' => $cid, 'avatar' => $avatar, 'contact' => $contact]); + if (($uid == 0) && !$force && empty($contact['thumb']) && empty($contact['micro'])) { + if ($contact['avatar'] != $avatar) { + DBA::update('contact', ['avatar' => $avatar], ['id' => $cid]); + Logger::info('Only update the avatar', ['id' => $cid, 'avatar' => $avatar, 'contact' => $contact]); + } return; } @@ -1830,28 +1823,27 @@ class Contact $contact['micro'] ?? '', ]; - foreach ($data as $image_uri) { - $image_rid = Photo::ridFromURI($image_uri); - if ($image_rid && !Photo::exists(['resource-id' => $image_rid, 'uid' => $uid])) { - Logger::info('Regenerating avatar', ['contact uid' => $uid, 'cid' => $cid, 'missing photo' => $image_rid, 'avatar' => $contact['avatar']]); - $force = true; + $update = ($contact['avatar'] != $avatar) || $force; + + if (!$update) { + foreach ($data as $image_uri) { + $image_rid = Photo::ridFromURI($image_uri); + if ($image_rid && !Photo::exists(['resource-id' => $image_rid, 'uid' => $uid])) { + Logger::info('Regenerating avatar', ['contact uid' => $uid, 'cid' => $cid, 'missing photo' => $image_rid, 'avatar' => $contact['avatar']]); + $update = true; + } } } - if (($contact["avatar"] != $avatar) || $force) { + if ($update) { $photos = Photo::importProfilePhoto($avatar, $uid, $cid, true); - if ($photos) { $fields = ['avatar' => $avatar, 'photo' => $photos[0], 'thumb' => $photos[1], 'micro' => $photos[2], 'avatar-date' => DateTimeFormat::utcNow()]; DBA::update('contact', $fields, ['id' => $cid]); - - // Update the public contact (contact id = 0) - if ($uid != 0) { - $pcontact = DBA::selectFirst('contact', ['id'], ['nurl' => $contact['nurl'], 'uid' => 0]); - if (DBA::isResult($pcontact)) { - DBA::update('contact', $fields, ['id' => $pcontact['id']]); - } - } + } elseif (empty($contact['avatar'])) { + // Ensure that the avatar field is set + DBA::update('contact', ['avatar' => $avatar], ['id' => $cid]); + Logger::info('Failed profile import', ['id' => $cid, 'force' => $force, 'avatar' => $avatar, 'contact' => $contact]); } } } @@ -2035,6 +2027,13 @@ class Contact $new_pubkey = $ret['pubkey']; + // Update the gcontact entry + if ($uid == 0) { + GContact::updateFromPublicContactID($id); + } + + ContactRelation::discoverByUrl($ret['url']); + $update = false; // make sure to not overwrite existing values with blank entries except some technical fields @@ -2508,7 +2507,6 @@ class Contact 'nurl' => Strings::normaliseLink($url), 'name' => $name, 'nick' => $nick, - 'photo' => $photo, 'network' => $network, 'rel' => self::FOLLOWER, 'blocked' => 0, diff --git a/src/Model/ContactRelation.php b/src/Model/ContactRelation.php new file mode 100644 index 000000000..19baaef79 --- /dev/null +++ b/src/Model/ContactRelation.php @@ -0,0 +1,163 @@ +. + * + */ + +namespace Friendica\Model; + +use Friendica\Core\Logger; +use Friendica\Database\DBA; +use Friendica\DI; +use Friendica\Protocol\ActivityPub; +use Friendica\Util\DateTimeFormat; +use Friendica\Util\Strings; + +class ContactRelation +{ + /** + * No discovery of followers/followings + */ + const DISCOVERY_NONE = 0; + /** + * Discover followers/followings of local contacts + */ + const DISCOVERY_LOCAL = 1; + /** + * Discover followers/followings of local contacts and contacts that visibly interacted on the system + */ + const DISCOVERY_INTERACTOR = 2; + /** + * Discover followers/followings of all contacts + */ + const DISCOVERY_ALL = 3; + + public static function store(int $target, int $actor, string $interaction_date) + { + if ($actor == $target) { + return; + } + + DBA::update('contact-relation', ['last-interaction' => $interaction_date], ['cid' => $target, 'relation-cid' => $actor], true); + } + + /** + * Fetches the followers of a given profile and adds them + * + * @param string $url URL of a profile + * @return void + */ + public static function discoverByUrl(string $url) + { + $contact_discovery = DI::config()->get('system', 'contact_discovery'); + + if ($contact_discovery == self::DISCOVERY_NONE) { + return; + } + + $contact = Contact::getByURL($url); + if (empty($contact)) { + return; + } + + if ($contact['last-discovery'] > DateTimeFormat::utc('now - 1 month')) { + Logger::info('Last discovery was less then a month before.', ['id' => $contact['id'], 'url' => $url, 'discovery' => $contact['last-discovery']]); + return; + } + + if ($contact_discovery != self::DISCOVERY_ALL) { + $local = DBA::exists('contact', ["`nurl` = ? AND `uid` != ?", Strings::normaliseLink($url), 0]); + if (($contact_discovery == self::DISCOVERY_LOCAL) && !$local) { + Logger::info('No discovery - This contact is not followed/following locally.', ['id' => $contact['id'], 'url' => $url]); + return; + } + + if ($contact_discovery == self::DISCOVERY_INTERACTOR) { + $interactor = DBA::exists('contact-relation', ["`relation-cid` = ? AND `last-interaction` > ?", $contact['id'], DBA::NULL_DATETIME]); + if (!$local && !$interactor) { + Logger::info('No discovery - This contact is not interacting locally.', ['id' => $contact['id'], 'url' => $url]); + return; + } + } + } elseif ($contact['created'] > DateTimeFormat::utc('now - 1 day')) { + Logger::info('Newly created contacs are not discovered to avoid DDoS attacks.', ['id' => $contact['id'], 'url' => $url, 'discovery' => $contact['created']]); + return; + } + + $apcontact = APContact::getByURL($url); + + if (!empty($apcontact['followers']) && is_string($apcontact['followers'])) { + $followers = ActivityPub::fetchItems($apcontact['followers']); + } else { + $followers = []; + } + + if (!empty($apcontact['following']) && is_string($apcontact['following'])) { + $followings = ActivityPub::fetchItems($apcontact['following']); + } else { + $followings = []; + } + + if (empty($followers) && empty($followings)) { + return; + } + + $target = $contact['id']; + + if (!empty($followers)) { + // Clear the follower list, since it will be recreated in the next step + DBA::update('contact-relation', ['follows' => false], ['cid' => $target]); + } + + $contacts = []; + foreach (array_merge($followers, $followings) as $contact) { + if (is_string($contact)) { + $contacts[] = $contact; + } elseif (!empty($contact['url']) && is_string($contact['url'])) { + $contacts[] = $contact['url']; + } + } + $contacts = array_unique($contacts); + + Logger::info('Discover contacts', ['id' => $target, 'url' => $url, 'contacts' => count($contacts)]); + foreach ($contacts as $contact) { + $actor = Contact::getIdForURL($contact); + if (!empty($actor)) { + $fields = []; + if (in_array($contact, $followers)) { + $fields = ['cid' => $target, 'relation-cid' => $actor]; + } elseif (in_array($contact, $followings)) { + $fields = ['cid' => $actor, 'relation-cid' => $target]; + } else { + continue; + } + + DBA::update('contact-relation', ['follows' => true, 'follow-updated' => DateTimeFormat::utcNow()], $fields, true); + } + } + + if (!empty($followers)) { + // Delete all followers that aren't followers anymore (and aren't interacting) + DBA::delete('contact-relation', ['cid' => $target, 'follows' => false, 'last-interaction' => DBA::NULL_DATETIME]); + } + + DBA::update('contact', ['last-discovery' => DateTimeFormat::utcNow()], ['id' => $target]); + Logger::info('Contacts discovery finished, "last-discovery" set', ['id' => $target, 'url' => $url]); + return; + } +} diff --git a/src/Model/GContact.php b/src/Model/GContact.php index 41ca763fc..ec53133c9 100644 --- a/src/Model/GContact.php +++ b/src/Model/GContact.php @@ -43,19 +43,6 @@ use Friendica\Util\Strings; */ class GContact { - /** - * No discovery of followers/followings - */ - const DISCOVERY_NONE = 0; - /** - * Only discover followers/followings from direct contacts - */ - const DISCOVERY_DIRECT = 1; - /** - * Recursive discovery of followers/followings - */ - const DISCOVERY_RECURSIVE = 2; - /** * Search global contact table by nick or name * @@ -1288,129 +1275,6 @@ class GContact } } - /** - * Fetches the followers of a given profile and adds them - * - * @param string $url URL of a profile - * @return void - */ - public static function discoverFollowers(string $url) - { - $gcontact = DBA::selectFirst('gcontact', ['id', 'last_discovery'], ['nurl' => Strings::normaliseLink(($url))]); - if (!DBA::isResult($gcontact)) { - return; - } - - if ($gcontact['last_discovery'] > DateTimeFormat::utc('now - 1 month')) { - Logger::info('Last discovery was less then a month before.', ['url' => $url, 'discovery' => $gcontact['last_discovery']]); - return; - } - - $gcid = $gcontact['id']; - - $apcontact = APContact::getByURL($url); - - if (!empty($apcontact['followers']) && is_string($apcontact['followers'])) { - $followers = ActivityPub::fetchItems($apcontact['followers']); - } else { - $followers = []; - } - - if (!empty($apcontact['following']) && is_string($apcontact['following'])) { - $followings = ActivityPub::fetchItems($apcontact['following']); - } else { - $followings = []; - } - - if (!empty($followers) || !empty($followings)) { - if (!empty($followers)) { - // Clear the follower list, since it will be recreated in the next step - DBA::update('gfollower', ['deleted' => true], ['gcid' => $gcid]); - } - - $contacts = []; - foreach (array_merge($followers, $followings) as $contact) { - if (is_string($contact)) { - $contacts[] = $contact; - } elseif (!empty($contact['url']) && is_string($contact['url'])) { - $contacts[] = $contact['url']; - } - } - $contacts = array_unique($contacts); - - Logger::info('Discover AP contacts', ['url' => $url, 'contacts' => count($contacts)]); - foreach ($contacts as $contact) { - $gcontact = DBA::selectFirst('gcontact', ['id'], ['nurl' => Strings::normaliseLink(($contact))]); - if (DBA::isResult($gcontact)) { - $fields = []; - if (in_array($contact, $followers)) { - $fields = ['gcid' => $gcid, 'follower-gcid' => $gcontact['id']]; - } elseif (in_array($contact, $followings)) { - $fields = ['gcid' => $gcontact['id'], 'follower-gcid' => $gcid]; - } - - if (!empty($fields)) { - Logger::info('Set relation between contacts', $fields); - DBA::update('gfollower', ['deleted' => false], $fields, true); - continue; - } - } - - if (!Network::isUrlBlocked($contact)) { - Logger::info('Discover new AP contact', ['url' => $contact]); - Worker::add(PRIORITY_LOW, 'UpdateGContact', $contact, 'nodiscover'); - } else { - Logger::info('No discovery, the URL is blocked.', ['url' => $contact]); - } - } - if (!empty($followers)) { - // Delete all followers that aren't undeleted - DBA::delete('gfollower', ['gcid' => $gcid, 'deleted' => true]); - } - - DBA::update('gcontact', ['last_discovery' => DateTimeFormat::utcNow()], ['id' => $gcid]); - Logger::info('AP contacts discovery finished, last discovery set', ['url' => $url]); - return; - } - - $data = Probe::uri($url); - if (empty($data['poco'])) { - return; - } - - $curlResult = DI::httpRequest()->get($data['poco']); - if (!$curlResult->isSuccess()) { - return; - } - $poco = json_decode($curlResult->getBody(), true); - if (empty($poco['entry'])) { - return; - } - - Logger::info('PoCo Discovery started', ['url' => $url, 'contacts' => count($poco['entry'])]); - - foreach ($poco['entry'] as $entries) { - if (!empty($entries['urls'])) { - foreach ($entries['urls'] as $entry) { - if ($entry['type'] == 'profile') { - if (DBA::exists('gcontact', ['nurl' => Strings::normaliseLink(($entry['value']))])) { - continue; - } - if (!Network::isUrlBlocked($entry['value'])) { - Logger::info('Discover new PoCo contact', ['url' => $entry['value']]); - Worker::add(PRIORITY_LOW, 'UpdateGContact', $entry['value'], 'nodiscover'); - } else { - Logger::info('No discovery, the URL is blocked.', ['url' => $entry['value']]); - } - } - } - } - } - - DBA::update('gcontact', ['last_discovery' => DateTimeFormat::utcNow()], ['id' => $gcid]); - Logger::info('PoCo Discovery finished', ['url' => $url]); - } - /** * Returns a random, global contact of the current node * diff --git a/src/Model/Item.php b/src/Model/Item.php index 1a561c7f1..d8fbac830 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -1554,9 +1554,7 @@ class Item } // Update the contact relations - if ($item['author-id'] != $parent['author-id']) { - DBA::update('contact-relation', ['last-interaction' => $item['created']], ['cid' => $parent['author-id'], 'relation-cid' => $item['author-id']], true); - } + ContactRelation::store($parent['author-id'], $item['author-id'], $item['created']); } return $item; diff --git a/src/Module/Admin/Site.php b/src/Module/Admin/Site.php index 290d92d13..c1d4479d9 100644 --- a/src/Module/Admin/Site.php +++ b/src/Module/Admin/Site.php @@ -28,7 +28,7 @@ use Friendica\Core\Theme; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Model\GContact; +use Friendica\Model\ContactRelation; use Friendica\Module\BaseAdmin; use Friendica\Module\Register; use Friendica\Protocol\PortableContact; @@ -178,8 +178,8 @@ class Site extends BaseAdmin $min_memory = (!empty($_POST['min_memory']) ? intval(trim($_POST['min_memory'])) : 0); $optimize_max_tablesize = (!empty($_POST['optimize_max_tablesize']) ? intval(trim($_POST['optimize_max_tablesize'])) : 100); $optimize_fragmentation = (!empty($_POST['optimize_fragmentation']) ? intval(trim($_POST['optimize_fragmentation'])) : 30); + $contact_discovery = (!empty($_POST['contact_discovery']) ? intval(trim($_POST['contact_discovery'])) : ContactRelation::DISCOVERY_NONE); $poco_completion = (!empty($_POST['poco_completion']) ? intval(trim($_POST['poco_completion'])) : false); - $gcontact_discovery = (!empty($_POST['gcontact_discovery']) ? intval(trim($_POST['gcontact_discovery'])) : GContact::DISCOVERY_NONE); $poco_requery_days = (!empty($_POST['poco_requery_days']) ? intval(trim($_POST['poco_requery_days'])) : 7); $poco_discovery = (!empty($_POST['poco_discovery']) ? intval(trim($_POST['poco_discovery'])) : PortableContact::DISABLED); $poco_discovery_since = (!empty($_POST['poco_discovery_since']) ? intval(trim($_POST['poco_discovery_since'])) : 30); @@ -308,7 +308,7 @@ class Site extends BaseAdmin DI::config()->set('system', 'optimize_max_tablesize', $optimize_max_tablesize); DI::config()->set('system', 'optimize_fragmentation', $optimize_fragmentation); DI::config()->set('system', 'poco_completion' , $poco_completion); - DI::config()->set('system', 'gcontact_discovery' , $gcontact_discovery); + DI::config()->set('system', 'contact_discovery' , $contact_discovery); DI::config()->set('system', 'poco_requery_days' , $poco_requery_days); DI::config()->set('system', 'poco_discovery' , $poco_discovery); DI::config()->set('system', 'poco_discovery_since' , $poco_discovery_since); @@ -551,9 +551,11 @@ class Site extends BaseAdmin ]; $discovery_choices = [ - GContact::DISCOVERY_NONE => DI::l10n()->t('none'), - GContact::DISCOVERY_DIRECT => DI::l10n()->t('Direct contacts'), - GContact::DISCOVERY_RECURSIVE => DI::l10n()->t('Contacts of contacts') + ContactRelation::DISCOVERY_NONE => DI::l10n()->t('none'), + ContactRelation::DISCOVERY_LOCAL => DI::l10n()->t('Local contacts'), + ContactRelation::DISCOVERY_INTERACTOR => DI::l10n()->t('Interactors'), + // "All" is deactivated until we are sure not to put too much stress on the fediverse with this + // ContactRelation::DISCOVERY_ALL => DI::l10n()->t('All'), ]; $diaspora_able = (DI::baseUrl()->getUrlPath() == ''); @@ -677,8 +679,13 @@ class Site extends BaseAdmin '$optimize_max_tablesize' => ['optimize_max_tablesize', DI::l10n()->t('Maximum table size for optimization'), $optimize_max_tablesize, DI::l10n()->t('Maximum table size (in MB) for the automatic optimization. Enter -1 to disable it.')], '$optimize_fragmentation' => ['optimize_fragmentation', DI::l10n()->t('Minimum level of fragmentation'), DI::config()->get('system', 'optimize_fragmentation', 30), DI::l10n()->t('Minimum fragmenation level to start the automatic optimization - default value is 30%.')], + '$contact_discovery' => ['contact_discovery', DI::l10n()->t('Discover followers/followings from contacts'), DI::config()->get('system', 'contact_discovery'), DI::l10n()->t('If enabled, contacts are checked for their followers and following contacts.') . '
      ' . + '
    • ' . DI::l10n()->t('None - deactivated') . '
    • ' . + '
    • ' . DI::l10n()->t('Local contacts - contacts of our local contacts are discovered for their followers/followings.') . '
    • ' . + '
    • ' . DI::l10n()->t('Interactors - contacts of our local contacts and contacts who interacted on locally visible postings are discovered for their followers/followings.') . '
    ', + $discovery_choices], + '$poco_completion' => ['poco_completion', DI::l10n()->t('Periodical check of global contacts'), DI::config()->get('system', 'poco_completion'), DI::l10n()->t('If enabled, the global contacts are checked periodically for missing or outdated data and the vitality of the contacts and servers.')], - '$gcontact_discovery' => ['gcontact_discovery', DI::l10n()->t('Discover followers/followings from global contacts'), DI::config()->get('system', 'gcontact_discovery'), DI::l10n()->t('If enabled, the global contacts are checked for new contacts among their followers and following contacts. This option will create huge masses of jobs, so it should only be activated on powerful machines.'), $discovery_choices], '$poco_requery_days' => ['poco_requery_days', DI::l10n()->t('Days between requery'), DI::config()->get('system', 'poco_requery_days'), DI::l10n()->t('Number of days after which a server is requeried for his contacts.')], '$poco_discovery' => ['poco_discovery', DI::l10n()->t('Discover contacts from other servers'), DI::config()->get('system', 'poco_discovery'), DI::l10n()->t('Periodically query other servers for contacts. You can choose between "Users": the users on the remote system, "Global Contacts": active contacts that are known on the system. The fallback is meant for Redmatrix servers and older friendica servers, where global contacts weren\'t available. The fallback increases the server load, so the recommended setting is "Users, Global Contacts".'), $poco_discovery_choices], '$poco_discovery_since' => ['poco_discovery_since', DI::l10n()->t('Timeframe for fetching global contacts'), DI::config()->get('system', 'poco_discovery_since'), DI::l10n()->t('When the discovery is activated, this value defines the timeframe for the activity of the global contacts that are fetched from other servers.'), $poco_discovery_since_choices], diff --git a/src/Worker/UpdateGContact.php b/src/Worker/UpdateGContact.php index 94e4d07d7..3f71241fa 100644 --- a/src/Worker/UpdateGContact.php +++ b/src/Worker/UpdateGContact.php @@ -40,9 +40,5 @@ class UpdateGContact $success = GContact::updateFromProbe($url, $force); Logger::info('Updated from probe', ['url' => $url, 'force' => $force, 'success' => $success]); - - if ($success && !$nodiscover && (DI::config()->get('system', 'gcontact_discovery') == GContact::DISCOVERY_RECURSIVE)) { - GContact::discoverFollowers($url); - } } } diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 14a52aa9a..56412b20b 100755 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -54,7 +54,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1357); + define('DB_UPDATE_VERSION', 1358); } return [ @@ -158,6 +158,7 @@ return [ "avatar-date" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""], "term-date" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""], "last-item" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "date of the last post"], + "last-discovery" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "date of the last follower discovery"], "priority" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => ""], "blocked" => ["type" => "boolean", "not null" => "1", "default" => "1", "comment" => "Node-wide block status"], "block_reason" => ["type" => "text", "comment" => "Node-wide block reason"], @@ -407,9 +408,11 @@ return [ "contact-relation" => [ "comment" => "Contact relations", "fields" => [ - "cid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "relation" => ["contact" => "id"], "primary" => "1", "comment" => "contact the related contact had interacted with"], - "relation-cid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "relation" => ["contact" => "id"], "primary" => "1", "comment" => "related contact who had interacted with the contact"], + "cid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "foreign" => ["contact" => "id"], "primary" => "1", "comment" => "contact the related contact had interacted with"], + "relation-cid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "foreign" => ["contact" => "id"], "primary" => "1", "comment" => "related contact who had interacted with the contact"], "last-interaction" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Date of the last interaction"], + "follow-updated" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Date of the last update of the contact relationship"], + "follows" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], ], "indexes" => [ "PRIMARY" => ["cid", "relation-cid"], @@ -593,18 +596,6 @@ return [ "gsid" => ["gsid"] ] ], - "gfollower" => [ - "comment" => "Followers of global contacts", - "fields" => [ - "gcid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "primary" => "1", "relation" => ["gcontact" => "id"], "comment" => "global contact"], - "follower-gcid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "primary" => "1", "relation" => ["gcontact" => "id"], "comment" => "global contact of the follower"], - "deleted" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "1 indicates that the connection has been deleted"], - ], - "indexes" => [ - "PRIMARY" => ["gcid", "follower-gcid"], - "follower-gcid" => ["follower-gcid"], - ] - ], "glink" => [ "comment" => "'friends of friends' linkages derived from poco", "fields" => [ diff --git a/view/templates/admin/site.tpl b/view/templates/admin/site.tpl index dc76db31c..49bae09ef 100644 --- a/view/templates/admin/site.tpl +++ b/view/templates/admin/site.tpl @@ -97,8 +97,8 @@

    {{$portable_contacts}}

    + {{include file="field_select.tpl" field=$contact_discovery}} {{include file="field_checkbox.tpl" field=$poco_completion}} - {{include file="field_select.tpl" field=$gcontact_discovery}} {{include file="field_input.tpl" field=$poco_requery_days}} {{include file="field_select.tpl" field=$poco_discovery}} {{include file="field_select.tpl" field=$poco_discovery_since}} diff --git a/view/theme/frio/templates/admin/site.tpl b/view/theme/frio/templates/admin/site.tpl index ddd6606a7..5f5df4aac 100644 --- a/view/theme/frio/templates/admin/site.tpl +++ b/view/theme/frio/templates/admin/site.tpl @@ -218,8 +218,8 @@
    + {{include file="field_select.tpl" field=$contact_discovery}} {{include file="field_checkbox.tpl" field=$poco_completion}} - {{include file="field_select.tpl" field=$gcontact_discovery}} {{include file="field_input.tpl" field=$poco_requery_days}} {{include file="field_select.tpl" field=$poco_discovery}} {{include file="field_select.tpl" field=$poco_discovery_since}} From d2a4a578102ca511cfe571f58e306b1206cae3c3 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Sun, 26 Jul 2020 23:39:30 +0200 Subject: [PATCH 28/44] Update src/Model/ContactRelation.php Co-authored-by: Hypolite Petovan --- src/Model/ContactRelation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/ContactRelation.php b/src/Model/ContactRelation.php index 19baaef79..dba1d53c8 100644 --- a/src/Model/ContactRelation.php +++ b/src/Model/ContactRelation.php @@ -76,7 +76,7 @@ class ContactRelation } if ($contact['last-discovery'] > DateTimeFormat::utc('now - 1 month')) { - Logger::info('Last discovery was less then a month before.', ['id' => $contact['id'], 'url' => $url, 'discovery' => $contact['last-discovery']]); + Logger::info('No discovery - Last was less than a month ago.', ['id' => $contact['id'], 'url' => $url, 'discovery' => $contact['last-discovery']]); return; } From ab04227ce3eb45142190c6a8410cf1d8ac07735e Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Sun, 26 Jul 2020 23:39:47 +0200 Subject: [PATCH 29/44] Update src/Model/ContactRelation.php Co-authored-by: Hypolite Petovan --- src/Model/ContactRelation.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Model/ContactRelation.php b/src/Model/ContactRelation.php index dba1d53c8..06c25059f 100644 --- a/src/Model/ContactRelation.php +++ b/src/Model/ContactRelation.php @@ -95,7 +95,8 @@ class ContactRelation } } } elseif ($contact['created'] > DateTimeFormat::utc('now - 1 day')) { - Logger::info('Newly created contacs are not discovered to avoid DDoS attacks.', ['id' => $contact['id'], 'url' => $url, 'discovery' => $contact['created']]); + // Newly created contacts are not discovered to avoid DDoS attacks + Logger::info('No discovery - Contact record is less than a day old.', ['id' => $contact['id'], 'url' => $url, 'discovery' => $contact['created']]); return; } From d7b567447616c560e2bb1e1d4fad9193042b1dd2 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sun, 26 Jul 2020 18:35:02 -0400 Subject: [PATCH 30/44] Move mod/lockview to Module\PermissionTooltip - Add explicit type parameter to lockview() in main.js --- doc/Addons.md | 8 +- doc/de/Addons.md | 8 +- src/Module/PermissionTooltip.php | 122 ++++++++++++++++++ static/routes.config.php | 2 + view/js/main.js | 27 ++-- view/templates/photo_view.tpl | 2 +- view/templates/search_item.tpl | 2 +- view/templates/wall_thread.tpl | 2 +- view/theme/frio/templates/photo_view.tpl | 2 +- view/theme/frio/templates/search_item.tpl | 4 +- view/theme/frio/templates/wall_thread.tpl | 4 +- view/theme/quattro/templates/photo_view.tpl | 2 +- view/theme/quattro/templates/search_item.tpl | 2 +- view/theme/quattro/templates/wall_thread.tpl | 2 +- view/theme/smoothly/templates/search_item.tpl | 2 +- view/theme/smoothly/templates/wall_thread.tpl | 2 +- view/theme/vier/templates/photo_item.tpl | 2 +- view/theme/vier/templates/photo_view.tpl | 2 +- view/theme/vier/templates/search_item.tpl | 4 +- view/theme/vier/templates/wall_thread.tpl | 2 +- 20 files changed, 158 insertions(+), 45 deletions(-) create mode 100644 src/Module/PermissionTooltip.php diff --git a/doc/Addons.md b/doc/Addons.md index 54363cb1d..c1861c791 100644 --- a/doc/Addons.md +++ b/doc/Addons.md @@ -604,10 +604,6 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep- Hook::callAll('post_local_end', $arr); -### mod/lockview.php - - Hook::callAll('lockview_content', $item); - ### mod/uexport.php Hook::callAll('uexport_options', $options); @@ -679,6 +675,10 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep- Hook::callAll('register_account', $uid); Hook::callAll('remove_user', $user); +### src/Module/PermissionTooltip.php + + Hook::callAll('lockview_content', $item); + ### src/Content/ContactBlock.php Hook::callAll('contact_block_end', $arr); diff --git a/doc/de/Addons.md b/doc/de/Addons.md index 745010ff4..2ff749549 100644 --- a/doc/de/Addons.md +++ b/doc/de/Addons.md @@ -312,10 +312,6 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap Hook::callAll('post_local_end', $arr); -### mod/lockview.php - - Hook::callAll('lockview_content', $item); - ### mod/uexport.php Hook::callAll('uexport_options', $options); @@ -422,6 +418,10 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap Hook::callAll('storage_instance', $data); +### src/Module/PermissionTooltip.php + + Hook::callAll('lockview_content', $item); + ### src/Worker/Directory.php Hook::callAll('globaldir_update', $arr); diff --git a/src/Module/PermissionTooltip.php b/src/Module/PermissionTooltip.php new file mode 100644 index 000000000..59478b326 --- /dev/null +++ b/src/Module/PermissionTooltip.php @@ -0,0 +1,122 @@ +t('Wrong type "%s", expected one of: %s', $type, implode(', ', $expectedTypes))); + } + + $condition = ['id' => $referenceId]; + if ($type == 'item') { + $fields = ['uid', 'psid', 'private']; + $model = Item::selectFirst($fields, $condition); + } else { + $fields = ['uid', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid']; + $model = DBA::selectFirst($type, $fields, $condition); + } + + if (!DBA::isResult($model)) { + throw new HttpException\NotFoundException(DI::l10n()->t('Model not found')); + } + + if (isset($model['psid'])) { + $permissionSet = DI::permissionSet()->selectFirst(['id' => $model['psid']]); + $model['allow_cid'] = $permissionSet->allow_cid; + $model['allow_gid'] = $permissionSet->allow_gid; + $model['deny_cid'] = $permissionSet->deny_cid; + $model['deny_gid'] = $permissionSet->deny_gid; + } + + // Kept for backwards compatiblity + Hook::callAll('lockview_content', $model); + + if ($model['uid'] != local_user() || + isset($model['private']) + && $model['private'] == Item::PRIVATE + && empty($model['allow_cid']) + && empty($model['allow_gid']) + && empty($model['deny_cid']) + && empty($model['deny_gid'])) + { + echo DI::l10n()->t('Remote privacy information not available.'); + exit; + } + + $aclFormatter = DI::aclFormatter(); + + $allowed_users = $aclFormatter->expand($model['allow_cid']); + $allowed_groups = $aclFormatter->expand($model['allow_gid']); + $deny_users = $aclFormatter->expand($model['deny_cid']); + $deny_groups = $aclFormatter->expand($model['deny_gid']); + + $o = DI::l10n()->t('Visible to:') . '
    '; + $l = []; + + if (count($allowed_groups)) { + $key = array_search(Group::FOLLOWERS, $allowed_groups); + if ($key !== false) { + $l[] = '' . DI::l10n()->t('Followers') . ''; + unset($allowed_groups[$key]); + } + + $key = array_search(Group::MUTUALS, $allowed_groups); + if ($key !== false) { + $l[] = '' . DI::l10n()->t('Mutuals') . ''; + unset($allowed_groups[$key]); + } + + foreach (DI::dba()->selectToArray('group', ['name'], ['id' => $allowed_groups]) as $group) { + $l[] = '' . $group['name'] . ''; + } + } + + foreach (DI::dba()->selectToArray('contact', ['name'], ['id' => $allowed_users]) as $contact) { + $l[] = $contact['name']; + } + + if (count($deny_groups)) { + $key = array_search(Group::FOLLOWERS, $deny_groups); + if ($key !== false) { + $l[] = '' . DI::l10n()->t('Followers') . ''; + unset($deny_groups[$key]); + } + + $key = array_search(Group::MUTUALS, $deny_groups); + if ($key !== false) { + $l[] = '' . DI::l10n()->t('Mutuals') . ''; + unset($deny_groups[$key]); + } + + foreach (DI::dba()->selectToArray('group', ['name'], ['id' => $allowed_groups]) as $group) { + $l[] = '' . $group['name'] . ''; + } + } + + foreach (DI::dba()->selectToArray('contact', ['name'], ['id' => $deny_users]) as $contact) { + $l[] = '' . $contact['name'] . ''; + } + + echo $o . implode(', ', $l); + exit(); + } +} diff --git a/static/routes.config.php b/static/routes.config.php index 8c3fba99b..ddfabd778 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -237,6 +237,8 @@ return [ '/openid' => [Module\Security\OpenID::class, [R::GET]], '/opensearch' => [Module\OpenSearch::class, [R::GET]], + '/permission/tooltip/{type}/{id:\d+}' => [Module\PermissionTooltip::class, [R::GET]], + '/photo' => [ '/{name}' => [Module\Photo::class, [R::GET]], '/{type}/{name}' => [Module\Photo::class, [R::GET]], diff --git a/view/js/main.js b/view/js/main.js index 60337918b..36c9cbb88 100644 --- a/view/js/main.js +++ b/view/js/main.js @@ -750,26 +750,23 @@ function getPosition(e) { var lockvisible = false; -function lockview(event,id) { +function lockview(event, type, id) { event = event || window.event; cursor = getPosition(event); if (lockvisible) { - lockviewhide(); + lockvisible = false; + $('#panel').hide(); } else { lockvisible = true; - $.get('lockview/' + id, function(data) { - $('#panel').html(data); - $('#panel').css({'left': cursor.x + 5 , 'top': cursor.y + 5}); - $('#panel').show(); + $.get('permission/tooltip/' + type + '/' + id, function(data) { + $('#panel') + .html(data) + .css({'left': cursor.x + 5 , 'top': cursor.y + 5}) + .show(); }); } } -function lockviewhide() { - lockvisible = false; - $('#panel').hide(); -} - function post_comment(id) { unpause(); commentBusy = true; @@ -940,14 +937,6 @@ function groupChangeMember(gid, cid, sec_token) { }); } -function profChangeMember(gid,cid) { - $('body .fakelink').css('cursor', 'wait'); - $.get('profperm/' + gid + '/' + cid, function(data) { - $('#prof-update-wrapper').html(data); - $('body .fakelink').css('cursor', 'auto'); - }); -} - function contactgroupChangeMember(checkbox, gid, cid) { let url; // checkbox.checked is the checkbox state after the click diff --git a/view/templates/photo_view.tpl b/view/templates/photo_view.tpl index 7170ceb33..0d7ccb20f 100644 --- a/view/templates/photo_view.tpl +++ b/view/templates/photo_view.tpl @@ -17,7 +17,7 @@ | {{$tools.profile.1}} {{/if}} {{if $tools.lock}} - | {{$tools.lock}} + | {{$tools.lock}} {{/if}} {{/if}}
    diff --git a/view/templates/search_item.tpl b/view/templates/search_item.tpl index 38aa94749..1a756db8a 100644 --- a/view/templates/search_item.tpl +++ b/view/templates/search_item.tpl @@ -17,7 +17,7 @@
    - {{if $item.lock}}
    {{$item.lock}}
    + {{if $item.lock}}
    {{$item.lock}}
    {{else}}
    {{/if}}
    {{$item.location nofilter}}
    diff --git a/view/templates/wall_thread.tpl b/view/templates/wall_thread.tpl index 0d8c896e1..cec886253 100644 --- a/view/templates/wall_thread.tpl +++ b/view/templates/wall_thread.tpl @@ -43,7 +43,7 @@
    - {{if $item.lock}}
    {{$item.lock}}
    + {{if $item.lock}}
    {{$item.lock}}
    {{else}}
    {{/if}}
    {{$item.location nofilter}}
    diff --git a/view/theme/frio/templates/photo_view.tpl b/view/theme/frio/templates/photo_view.tpl index 91e9dafe4..de45eecff 100644 --- a/view/theme/frio/templates/photo_view.tpl +++ b/view/theme/frio/templates/photo_view.tpl @@ -37,7 +37,7 @@ {{/if}} {{if $tools.lock}} - + {{/if}} diff --git a/view/theme/frio/templates/search_item.tpl b/view/theme/frio/templates/search_item.tpl index a5b6d52d6..2cd231f8f 100644 --- a/view/theme/frio/templates/search_item.tpl +++ b/view/theme/frio/templates/search_item.tpl @@ -1,7 +1,7 @@ @@ -56,7 +56,7 @@ {{/if}} {{if $item.lock}} - +   {{/if}} diff --git a/view/theme/frio/templates/wall_thread.tpl b/view/theme/frio/templates/wall_thread.tpl index c15b110ec..5e7f49bf4 100644 --- a/view/theme/frio/templates/wall_thread.tpl +++ b/view/theme/frio/templates/wall_thread.tpl @@ -63,7 +63,7 @@ as the value of $top_child_total (this is done at the end of this file) {{if $item.star}} {{$item.star.starred}} {{/if}} - {{if $item.lock}}{{/if}} + {{if $item.lock}}{{/if}} {{* /TODO => Unknown block *}} @@ -138,7 +138,7 @@ as the value of $top_child_total (this is done at the end of this file) {{/if}} {{if $item.lock}} - +   {{/if}} diff --git a/view/theme/quattro/templates/photo_view.tpl b/view/theme/quattro/templates/photo_view.tpl index 1ce336b0a..11947643c 100644 --- a/view/theme/quattro/templates/photo_view.tpl +++ b/view/theme/quattro/templates/photo_view.tpl @@ -16,7 +16,7 @@ | {{$tools.profile.1}} {{/if}} {{if $tools.lock}} - | {{$tools.lock}} + | {{$tools.lock}} {{/if}} {{/if}} diff --git a/view/theme/quattro/templates/search_item.tpl b/view/theme/quattro/templates/search_item.tpl index 0e4aaaf8f..cb400ac4f 100644 --- a/view/theme/quattro/templates/search_item.tpl +++ b/view/theme/quattro/templates/search_item.tpl @@ -1,6 +1,6 @@
    {{if $item.star}}{{$item.star.starred}}{{/if}} - {{if $item.lock}}{{$item.lock}}{{/if}} + {{if $item.lock}}{{$item.lock}}{{/if}}
    diff --git a/view/theme/quattro/templates/wall_thread.tpl b/view/theme/quattro/templates/wall_thread.tpl index e6d8b9754..b6907219f 100644 --- a/view/theme/quattro/templates/wall_thread.tpl +++ b/view/theme/quattro/templates/wall_thread.tpl @@ -24,7 +24,7 @@
    {{if $item.star}}{{$item.star.starred}}{{/if}} - {{if $item.lock}}{{$item.lock}}{{/if}} + {{if $item.lock}}{{$item.lock}}{{/if}}
    diff --git a/view/theme/smoothly/templates/search_item.tpl b/view/theme/smoothly/templates/search_item.tpl index 23af1b794..b3ae01eb4 100644 --- a/view/theme/smoothly/templates/search_item.tpl +++ b/view/theme/smoothly/templates/search_item.tpl @@ -18,7 +18,7 @@
    {{if $item.location}}{{$item.location nofilter}} {{/if}}
    - {{if $item.lock}}
    {{$item.lock}}
    + {{if $item.lock}}
    {{$item.lock}}
    {{else}}
    {{/if}}
    diff --git a/view/theme/smoothly/templates/wall_thread.tpl b/view/theme/smoothly/templates/wall_thread.tpl index 85d480c31..4ff34aed1 100644 --- a/view/theme/smoothly/templates/wall_thread.tpl +++ b/view/theme/smoothly/templates/wall_thread.tpl @@ -42,7 +42,7 @@
    {{if $item.lock}}
    - {{$item.lock}} + {{$item.lock}}
    {{else}}
    diff --git a/view/theme/vier/templates/photo_item.tpl b/view/theme/vier/templates/photo_item.tpl index f0b7a6089..94e9232af 100644 --- a/view/theme/vier/templates/photo_item.tpl +++ b/view/theme/vier/templates/photo_item.tpl @@ -11,7 +11,7 @@ {{$name}} {{if $plink}}{{$ago}}{{else}} {{$ago}} {{/if}} - {{if $lock}}{{$lock}} {{/if}} + {{if $lock}}{{$lock}} {{/if}}
    diff --git a/view/theme/vier/templates/photo_view.tpl b/view/theme/vier/templates/photo_view.tpl index f70ec5b56..87501c031 100644 --- a/view/theme/vier/templates/photo_view.tpl +++ b/view/theme/vier/templates/photo_view.tpl @@ -17,7 +17,7 @@ | {{$tools.profile.1}} {{/if}} {{if $tools.lock}} - | {{$tools.lock}} + | {{$tools.lock}} {{/if}} {{/if}}
    diff --git a/view/theme/vier/templates/search_item.tpl b/view/theme/vier/templates/search_item.tpl index 1813dd49f..1da18b086 100644 --- a/view/theme/vier/templates/search_item.tpl +++ b/view/theme/vier/templates/search_item.tpl @@ -2,7 +2,7 @@
    {{if $item.star}}{{$item.star.starred}}{{/if}} - {{if $item.lock}}{{$item.lock}}{{/if}} + {{if $item.lock}}{{$item.lock}}{{/if}}
    @@ -25,7 +25,7 @@ {{$item.name}} {{if $item.plink}}{{$item.ago}}{{else}} {{$item.ago}} {{/if}} - {{if $item.lock}}{{$item.lock}} {{/if}} + {{if $item.lock}}{{$item.lock}} {{/if}}
    diff --git a/view/theme/vier/templates/wall_thread.tpl b/view/theme/vier/templates/wall_thread.tpl index 04ea7b424..430a6943e 100644 --- a/view/theme/vier/templates/wall_thread.tpl +++ b/view/theme/vier/templates/wall_thread.tpl @@ -65,7 +65,7 @@ {{/if}} {{$item.pinned}} - {{if $item.lock}}{{$item.lock}}{{/if}} + {{if $item.lock}}{{$item.lock}}{{/if}} {{$item.network_name}} From 6749b2c887552feef3a671c4063425b11a92014a Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 27 Jul 2020 02:01:42 -0400 Subject: [PATCH 31/44] Remove obsolete mod/lockview.php file --- mod/lockview.php | 161 ----------------------------------------------- 1 file changed, 161 deletions(-) delete mode 100644 mod/lockview.php diff --git a/mod/lockview.php b/mod/lockview.php deleted file mode 100644 index e48debfc6..000000000 --- a/mod/lockview.php +++ /dev/null @@ -1,161 +0,0 @@ -. - * - */ - -use Friendica\App; -use Friendica\Core\Hook; -use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Model\Group; -use Friendica\Model\Item; - -function lockview_content(App $a) -{ - $type = (($a->argc > 1) ? $a->argv[1] : 0); - if (is_numeric($type)) { - $item_id = intval($type); - $type = 'item'; - } else { - $item_id = (($a->argc > 2) ? intval($a->argv[2]) : 0); - } - - if (!$item_id) { - exit(); - } - - if (!in_array($type, ['item','photo','event'])) { - exit(); - } - - $fields = ['uid', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid']; - $condition = ['id' => $item_id]; - - if ($type != 'item') { - $item = DBA::selectFirst($type, $fields, $condition); - } else { - $fields[] = 'private'; - $item = Item::selectFirst($fields, $condition); - } - - if (!DBA::isResult($item)) { - exit(); - } - - Hook::callAll('lockview_content', $item); - - if ($item['uid'] != local_user()) { - echo DI::l10n()->t('Remote privacy information not available.') . '
    '; - exit(); - } - - if (isset($item['private']) - && $item['private'] == Item::PRIVATE - && empty($item['allow_cid']) - && empty($item['allow_gid']) - && empty($item['deny_cid']) - && empty($item['deny_gid'])) - { - echo DI::l10n()->t('Remote privacy information not available.') . '
    '; - exit(); - } - - $aclFormatter = DI::aclFormatter(); - - $allowed_users = $aclFormatter->expand($item['allow_cid']); - $allowed_groups = $aclFormatter->expand($item['allow_gid']); - $deny_users = $aclFormatter->expand($item['deny_cid']); - $deny_groups = $aclFormatter->expand($item['deny_gid']); - - $o = DI::l10n()->t('Visible to:') . '
    '; - $l = []; - - if (count($allowed_groups)) { - $key = array_search(Group::FOLLOWERS, $allowed_groups); - if ($key !== false) { - $l[] = '' . DI::l10n()->t('Followers') . ''; - unset($allowed_groups[$key]); - } - - $key = array_search(Group::MUTUALS, $allowed_groups); - if ($key !== false) { - $l[] = '' . DI::l10n()->t('Mutuals') . ''; - unset($allowed_groups[$key]); - } - - - $r = q("SELECT `name` FROM `group` WHERE `id` IN ( %s )", - DBA::escape(implode(', ', $allowed_groups)) - ); - if (DBA::isResult($r)) { - foreach ($r as $rr) { - $l[] = '' . $rr['name'] . ''; - } - } - } - - if (count($allowed_users)) { - $r = q("SELECT `name` FROM `contact` WHERE `id` IN ( %s )", - DBA::escape(implode(', ', $allowed_users)) - ); - if (DBA::isResult($r)) { - foreach ($r as $rr) { - $l[] = $rr['name']; - } - } - } - - if (count($deny_groups)) { - $key = array_search(Group::FOLLOWERS, $deny_groups); - if ($key !== false) { - $l[] = '' . DI::l10n()->t('Followers') . ''; - unset($deny_groups[$key]); - } - - $key = array_search(Group::MUTUALS, $deny_groups); - if ($key !== false) { - $l[] = '' . DI::l10n()->t('Mutuals') . ''; - unset($deny_groups[$key]); - } - - $r = q("SELECT `name` FROM `group` WHERE `id` IN ( %s )", - DBA::escape(implode(', ', $deny_groups)) - ); - if (DBA::isResult($r)) { - foreach ($r as $rr) { - $l[] = '' . $rr['name'] . ''; - } - } - } - - if (count($deny_users)) { - $r = q("SELECT `name` FROM `contact` WHERE `id` IN ( %s )", - DBA::escape(implode(', ', $deny_users)) - ); - if (DBA::isResult($r)) { - foreach ($r as $rr) { - $l[] = '' . $rr['name'] . ''; - } - } - } - - echo $o . implode(', ', $l); - exit(); - -} From 19141b1bcfeff348de1cd3138dda07d972c0c0da Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 27 Jul 2020 00:20:30 -0400 Subject: [PATCH 32/44] Add offset parameter to System::callstack - Enable its use in centralized methods without polluting the stack --- src/Core/System.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Core/System.php b/src/Core/System.php index 67ee3a803..cb9166102 100644 --- a/src/Core/System.php +++ b/src/Core/System.php @@ -33,16 +33,17 @@ class System /** * Returns a string with a callstack. Can be used for logging. * - * @param integer $depth optional, default 4 + * @param integer $depth How many calls to include in the stacks after filtering + * @param int $offset How many calls to shave off the top of the stack, for example if + * this is called from a centralized method that isn't relevant to the callstack * @return string */ - public static function callstack($depth = 4) + public static function callstack(int $depth = 4, int $offset = 0) { $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - // We remove the first two items from the list since they contain data that we don't need. - array_shift($trace); - array_shift($trace); + // We remove at least the first two items from the list since they contain data that we don't need. + $trace = array_slice($trace, 2 + $offset); $callstack = []; $previous = ['class' => '', 'function' => '', 'database' => false]; From afb882048efa8c4b2914a6b054b129dee57061ef Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 27 Jul 2020 00:22:07 -0400 Subject: [PATCH 33/44] Generate callstack value from inside Profiler::saveTimestamp - Save a massive amount of time computing callstacks when profiling is disabled --- src/Content/Text/BBCode.php | 6 +++--- src/Content/Text/Markdown.php | 2 +- src/Core/Addon.php | 2 +- src/Core/Cache/ProfilerCache.php | 16 ++++++++-------- src/Core/Renderer.php | 4 ++-- src/Core/Theme.php | 2 +- src/Database/Database.php | 8 ++++---- src/Factory/SessionFactory.php | 2 +- src/Network/HTTPRequest.php | 8 ++++---- src/Object/Image.php | 2 +- src/Util/Images.php | 2 +- src/Util/Logger/ProfilerLogger.php | 18 +++++++++--------- src/Util/Profiler.php | 9 ++++++--- 13 files changed, 42 insertions(+), 39 deletions(-) diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index 1181c8f47..cae3e941a 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -1098,7 +1098,7 @@ class BBCode @curl_exec($ch); $curl_info = @curl_getinfo($ch); - DI::profiler()->saveTimestamp($stamp1, "network", System::callstack()); + DI::profiler()->saveTimestamp($stamp1, "network"); if (substr($curl_info['content_type'], 0, 6) == 'image/') { $text = "[url=" . $match[1] . ']' . $match[1] . "[/url]"; @@ -1172,7 +1172,7 @@ class BBCode @curl_exec($ch); $curl_info = @curl_getinfo($ch); - DI::profiler()->saveTimestamp($stamp1, "network", System::callstack()); + DI::profiler()->saveTimestamp($stamp1, "network"); // if its a link to a picture then embed this picture if (substr($curl_info['content_type'], 0, 6) == 'image/') { @@ -2045,7 +2045,7 @@ class BBCode // Now convert HTML to Markdown $text = HTML::toMarkdown($text); - DI::profiler()->saveTimestamp($stamp1, "parser", System::callstack()); + DI::profiler()->saveTimestamp($stamp1, "parser"); // Libertree has a problem with escaped hashtags. $text = str_replace(['\#'], ['#'], $text); diff --git a/src/Content/Text/Markdown.php b/src/Content/Text/Markdown.php index 1b3f8ec71..45f38e4c5 100644 --- a/src/Content/Text/Markdown.php +++ b/src/Content/Text/Markdown.php @@ -57,7 +57,7 @@ class Markdown $html = $MarkdownParser->transform($text); - DI::profiler()->saveTimestamp($stamp1, "parser", System::callstack()); + DI::profiler()->saveTimestamp($stamp1, "parser"); return $html; } diff --git a/src/Core/Addon.php b/src/Core/Addon.php index dd229be28..ee70a66b6 100644 --- a/src/Core/Addon.php +++ b/src/Core/Addon.php @@ -256,7 +256,7 @@ class Addon $stamp1 = microtime(true); $f = file_get_contents("addon/$addon/$addon.php"); - DI::profiler()->saveTimestamp($stamp1, "file", System::callstack()); + DI::profiler()->saveTimestamp($stamp1, "file"); $r = preg_match("|/\*.*\*/|msU", $f, $m); diff --git a/src/Core/Cache/ProfilerCache.php b/src/Core/Cache/ProfilerCache.php index 1f77db67a..7c4802077 100644 --- a/src/Core/Cache/ProfilerCache.php +++ b/src/Core/Cache/ProfilerCache.php @@ -56,7 +56,7 @@ class ProfilerCache implements ICache, IMemoryCache $return = $this->cache->getAllKeys($prefix); - $this->profiler->saveTimestamp($time, 'cache', System::callstack()); + $this->profiler->saveTimestamp($time, 'cache'); return $return; } @@ -70,7 +70,7 @@ class ProfilerCache implements ICache, IMemoryCache $return = $this->cache->get($key); - $this->profiler->saveTimestamp($time, 'cache', System::callstack()); + $this->profiler->saveTimestamp($time, 'cache'); return $return; } @@ -84,7 +84,7 @@ class ProfilerCache implements ICache, IMemoryCache $return = $this->cache->set($key, $value, $ttl); - $this->profiler->saveTimestamp($time, 'cache', System::callstack()); + $this->profiler->saveTimestamp($time, 'cache'); return $return; } @@ -98,7 +98,7 @@ class ProfilerCache implements ICache, IMemoryCache $return = $this->cache->delete($key); - $this->profiler->saveTimestamp($time, 'cache', System::callstack()); + $this->profiler->saveTimestamp($time, 'cache'); return $return; } @@ -112,7 +112,7 @@ class ProfilerCache implements ICache, IMemoryCache $return = $this->cache->clear($outdated); - $this->profiler->saveTimestamp($time, 'cache', System::callstack()); + $this->profiler->saveTimestamp($time, 'cache'); return $return; } @@ -127,7 +127,7 @@ class ProfilerCache implements ICache, IMemoryCache $return = $this->cache->add($key, $value, $ttl); - $this->profiler->saveTimestamp($time, 'cache', System::callstack()); + $this->profiler->saveTimestamp($time, 'cache'); return $return; } else { @@ -145,7 +145,7 @@ class ProfilerCache implements ICache, IMemoryCache $return = $this->cache->compareSet($key, $oldValue, $newValue, $ttl); - $this->profiler->saveTimestamp($time, 'cache', System::callstack()); + $this->profiler->saveTimestamp($time, 'cache'); return $return; } else { @@ -163,7 +163,7 @@ class ProfilerCache implements ICache, IMemoryCache $return = $this->cache->compareDelete($key, $value); - $this->profiler->saveTimestamp($time, 'cache', System::callstack()); + $this->profiler->saveTimestamp($time, 'cache'); return $return; } else { diff --git a/src/Core/Renderer.php b/src/Core/Renderer.php index bf4cd3907..24f003417 100644 --- a/src/Core/Renderer.php +++ b/src/Core/Renderer.php @@ -92,7 +92,7 @@ class Renderer throw new InternalServerErrorException($message); } - DI::profiler()->saveTimestamp($stamp1, "rendering", System::callstack()); + DI::profiler()->saveTimestamp($stamp1, "rendering"); return $output; } @@ -121,7 +121,7 @@ class Renderer throw new InternalServerErrorException($message); } - DI::profiler()->saveTimestamp($stamp1, "file", System::callstack()); + DI::profiler()->saveTimestamp($stamp1, "file"); return $template; } diff --git a/src/Core/Theme.php b/src/Core/Theme.php index 03f1dfd9c..3548544e9 100644 --- a/src/Core/Theme.php +++ b/src/Core/Theme.php @@ -90,7 +90,7 @@ class Theme $stamp1 = microtime(true); $theme_file = file_get_contents("view/theme/$theme/theme.php"); - DI::profiler()->saveTimestamp($stamp1, "file", System::callstack()); + DI::profiler()->saveTimestamp($stamp1, "file"); $result = preg_match("|/\*.*\*/|msU", $theme_file, $matches); diff --git a/src/Database/Database.php b/src/Database/Database.php index 5ef481556..0b38c24ba 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -699,7 +699,7 @@ class Database $this->errorno = $errorno; } - $this->profiler->saveTimestamp($stamp1, 'database', System::callstack()); + $this->profiler->saveTimestamp($stamp1, 'database'); if ($this->configCache->get('system', 'db_log')) { $stamp2 = microtime(true); @@ -783,7 +783,7 @@ class Database $this->errorno = $errorno; } - $this->profiler->saveTimestamp($stamp, "database_write", System::callstack()); + $this->profiler->saveTimestamp($stamp, "database_write"); return $retval; } @@ -964,7 +964,7 @@ class Database } } - $this->profiler->saveTimestamp($stamp1, 'database', System::callstack()); + $this->profiler->saveTimestamp($stamp1, 'database'); return $columns; } @@ -1644,7 +1644,7 @@ class Database break; } - $this->profiler->saveTimestamp($stamp1, 'database', System::callstack()); + $this->profiler->saveTimestamp($stamp1, 'database'); return $ret; } diff --git a/src/Factory/SessionFactory.php b/src/Factory/SessionFactory.php index f513eef35..116afe18a 100644 --- a/src/Factory/SessionFactory.php +++ b/src/Factory/SessionFactory.php @@ -85,7 +85,7 @@ class SessionFactory $session = new Session\Native($baseURL, $handler); } } finally { - $profiler->saveTimestamp($stamp1, 'parser', System::callstack()); + $profiler->saveTimestamp($stamp1, 'parser'); return $session; } } diff --git a/src/Network/HTTPRequest.php b/src/Network/HTTPRequest.php index 839586880..87177b1a4 100644 --- a/src/Network/HTTPRequest.php +++ b/src/Network/HTTPRequest.php @@ -199,7 +199,7 @@ class HTTPRequest implements IHTTPRequest @curl_close($ch); - $this->profiler->saveTimestamp($stamp1, 'network', System::callstack()); + $this->profiler->saveTimestamp($stamp1, 'network'); return $curlResponse; } @@ -285,7 +285,7 @@ class HTTPRequest implements IHTTPRequest curl_close($ch); - $this->profiler->saveTimestamp($stamp1, 'network', System::callstack()); + $this->profiler->saveTimestamp($stamp1, 'network'); // Very old versions of Lighttpd don't like the "Expect" header, so we remove it when needed if ($curlResponse->getReturnCode() == 417) { @@ -335,7 +335,7 @@ class HTTPRequest implements IHTTPRequest $http_code = $curl_info['http_code']; curl_close($ch); - $this->profiler->saveTimestamp($stamp1, "network", System::callstack()); + $this->profiler->saveTimestamp($stamp1, "network"); if ($http_code == 0) { return $url; @@ -377,7 +377,7 @@ class HTTPRequest implements IHTTPRequest $body = curl_exec($ch); curl_close($ch); - $this->profiler->saveTimestamp($stamp1, "network", System::callstack()); + $this->profiler->saveTimestamp($stamp1, "network"); if (trim($body) == "") { return $url; diff --git a/src/Object/Image.php b/src/Object/Image.php index 8787db052..b69682ca6 100644 --- a/src/Object/Image.php +++ b/src/Object/Image.php @@ -625,7 +625,7 @@ class Image $stamp1 = microtime(true); file_put_contents($path, $string); - DI::profiler()->saveTimestamp($stamp1, "file", System::callstack()); + DI::profiler()->saveTimestamp($stamp1, "file"); } /** diff --git a/src/Util/Images.php b/src/Util/Images.php index ef171873f..f39b0db00 100644 --- a/src/Util/Images.php +++ b/src/Util/Images.php @@ -200,7 +200,7 @@ class Images $stamp1 = microtime(true); file_put_contents($tempfile, $img_str); - DI::profiler()->saveTimestamp($stamp1, "file", System::callstack()); + DI::profiler()->saveTimestamp($stamp1, "file"); $data = getimagesize($tempfile); unlink($tempfile); diff --git a/src/Util/Logger/ProfilerLogger.php b/src/Util/Logger/ProfilerLogger.php index 2f1940952..e0f18b285 100644 --- a/src/Util/Logger/ProfilerLogger.php +++ b/src/Util/Logger/ProfilerLogger.php @@ -61,7 +61,7 @@ class ProfilerLogger implements LoggerInterface { $stamp1 = microtime(true); $this->logger->emergency($message, $context); - $this->profiler->saveTimestamp($stamp1, 'file', System::callstack()); + $this->profiler->saveTimestamp($stamp1, 'file'); } /** @@ -71,7 +71,7 @@ class ProfilerLogger implements LoggerInterface { $stamp1 = microtime(true); $this->logger->alert($message, $context); - $this->profiler->saveTimestamp($stamp1, 'file', System::callstack()); + $this->profiler->saveTimestamp($stamp1, 'file'); } /** @@ -81,7 +81,7 @@ class ProfilerLogger implements LoggerInterface { $stamp1 = microtime(true); $this->logger->critical($message, $context); - $this->profiler->saveTimestamp($stamp1, 'file', System::callstack()); + $this->profiler->saveTimestamp($stamp1, 'file'); } /** @@ -91,7 +91,7 @@ class ProfilerLogger implements LoggerInterface { $stamp1 = microtime(true); $this->logger->error($message, $context); - $this->profiler->saveTimestamp($stamp1, 'file', System::callstack()); + $this->profiler->saveTimestamp($stamp1, 'file'); } /** @@ -101,7 +101,7 @@ class ProfilerLogger implements LoggerInterface { $stamp1 = microtime(true); $this->logger->warning($message, $context); - $this->profiler->saveTimestamp($stamp1, 'file', System::callstack()); + $this->profiler->saveTimestamp($stamp1, 'file'); } /** @@ -111,7 +111,7 @@ class ProfilerLogger implements LoggerInterface { $stamp1 = microtime(true); $this->logger->notice($message, $context); - $this->profiler->saveTimestamp($stamp1, 'file', System::callstack()); + $this->profiler->saveTimestamp($stamp1, 'file'); } /** @@ -121,7 +121,7 @@ class ProfilerLogger implements LoggerInterface { $stamp1 = microtime(true); $this->logger->info($message, $context); - $this->profiler->saveTimestamp($stamp1, 'file', System::callstack()); + $this->profiler->saveTimestamp($stamp1, 'file'); } /** @@ -131,7 +131,7 @@ class ProfilerLogger implements LoggerInterface { $stamp1 = microtime(true); $this->logger->debug($message, $context); - $this->profiler->saveTimestamp($stamp1, 'file', System::callstack()); + $this->profiler->saveTimestamp($stamp1, 'file'); } /** @@ -141,6 +141,6 @@ class ProfilerLogger implements LoggerInterface { $stamp1 = microtime(true); $this->logger->log($level, $message, $context); - $this->profiler->saveTimestamp($stamp1, 'file', System::callstack()); + $this->profiler->saveTimestamp($stamp1, 'file'); } } diff --git a/src/Util/Profiler.php b/src/Util/Profiler.php index 240273bde..db3e1bb97 100644 --- a/src/Util/Profiler.php +++ b/src/Util/Profiler.php @@ -23,6 +23,7 @@ namespace Friendica\Util; use Friendica\Core\Config\Cache; use Friendica\Core\Config\IConfig; +use Friendica\Core\System; use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface; use Psr\Container\NotFoundExceptionInterface; @@ -88,9 +89,9 @@ class Profiler implements ContainerInterface * Saves a timestamp for a value - f.e. a call * Necessary for profiling Friendica * - * @param int $timestamp the Timestamp - * @param string $value A value to profile - * @param string $callstack The callstack of the current profiling data + * @param int $timestamp the Timestamp + * @param string $value A value to profile + * @param string $callstack A callstack string, generated if absent */ public function saveTimestamp($timestamp, $value, $callstack = '') { @@ -98,6 +99,8 @@ class Profiler implements ContainerInterface return; } + $callstack = $callstack ?: System::callstack(4, 1); + $duration = floatval(microtime(true) - $timestamp); if (!isset($this->performance[$value])) { From bd1f4ebbde207b0c6b84a11f3170c4f5b9a2ea12 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 27 Jul 2020 01:33:24 -0400 Subject: [PATCH 34/44] Refactor Hook and Addon to systematically use Hook::delete --- src/Core/Addon.php | 14 +++----------- src/Core/Hook.php | 39 +++++++++++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/Core/Addon.php b/src/Core/Addon.php index ee70a66b6..0462504e7 100644 --- a/src/Core/Addon.php +++ b/src/Core/Addon.php @@ -136,7 +136,7 @@ class Addon $func(); } - DBA::delete('hook', ['file' => 'addon/' . $addon . '/' . $addon . '.php']); + Hook::delete(['file' => 'addon/' . $addon . '/' . $addon . '.php']); unset(self::$addons[array_search($addon, self::$addons)]); } @@ -204,17 +204,9 @@ class Addon } Logger::notice("Addon {addon}: {action}", ['action' => 'reload', 'addon' => $addon['name']]); - @include_once($fname); - if (function_exists($addonname . '_uninstall')) { - $func = $addonname . '_uninstall'; - $func(DI::app()); - } - if (function_exists($addonname . '_install')) { - $func = $addonname . '_install'; - $func(DI::app()); - } - DBA::update('addon', ['timestamp' => $t], ['id' => $addon['id']]); + self::uninstall($fname); + self::install($fname); } } diff --git a/src/Core/Hook.php b/src/Core/Hook.php index 8fdadd666..851dd60ed 100644 --- a/src/Core/Hook.php +++ b/src/Core/Hook.php @@ -99,9 +99,7 @@ class Hook return true; } - $result = DBA::insert('hook', ['hook' => $hook, 'file' => $file, 'function' => $function, 'priority' => $priority]); - - return $result; + return self::insert(['hook' => $hook, 'file' => $file, 'function' => $function, 'priority' => $priority]); } /** @@ -119,10 +117,10 @@ class Hook // This here is only needed for fixing a problem that existed on the develop branch $condition = ['hook' => $hook, 'file' => $file, 'function' => $function]; - DBA::delete('hook', $condition); + self::delete($condition); $condition = ['hook' => $hook, 'file' => $relative_file, 'function' => $function]; - $result = DBA::delete('hook', $condition); + $result = self::delete($condition); return $result; } @@ -220,7 +218,7 @@ class Hook } else { // remove orphan hooks $condition = ['hook' => $name, 'file' => $hook[0], 'function' => $hook[1]]; - DBA::delete('hook', $condition, ['cascade' => false]); + self::delete($condition, ['cascade' => false]); } } @@ -245,4 +243,33 @@ class Hook return false; } + + /** + * Deletes one or more hook records + * + * @param array $condition + * @param array $options + * @return bool + * @throws \Exception + */ + public static function delete(array $condition, array $options = []) + { + $result = DBA::delete('hook', $condition, $options); + + return $result; + } + + /** + * Inserts a hook record + * + * @param array $condition + * @return bool + * @throws \Exception + */ + private static function insert(array $condition) + { + $result = DBA::insert('hook', $condition); + + return $result; + } } From 1d0cd7328b10875f5b6bb0fa23153f338df18461 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 27 Jul 2020 01:57:44 -0400 Subject: [PATCH 35/44] Add dispatch data caching in App\Router - Add new cache key "routerDispatchData" - Update Dice dependencies since Router constructor signature changed --- src/App/Router.php | 78 ++++++++++++++++++++++++++++++---- src/Core/Hook.php | 12 ++++++ static/dependencies.config.php | 7 ++- 3 files changed, 85 insertions(+), 12 deletions(-) diff --git a/src/App/Router.php b/src/App/Router.php index 8094e3b46..dfe890fb9 100644 --- a/src/App/Router.php +++ b/src/App/Router.php @@ -26,6 +26,8 @@ use FastRoute\DataGenerator\GroupCountBased; use FastRoute\Dispatcher; use FastRoute\RouteCollector; use FastRoute\RouteParser\Std; +use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\ICache; use Friendica\Core\Hook; use Friendica\Core\L10n; use Friendica\Network\HTTPException; @@ -66,14 +68,24 @@ class Router /** @var L10n */ private $l10n; + /** @var ICache */ + private $cache; + + /** @var string */ + private $baseRoutesFilepath; + /** - * @param array $server The $_SERVER variable - * @param L10n $l10n - * @param RouteCollector|null $routeCollector Optional the loaded Route collector + * @param array $server The $_SERVER variable + * @param string $baseRoutesFilepath The path to a base routes file to leverage cache, can be empty + * @param L10n $l10n + * @param ICache $cache + * @param RouteCollector|null $routeCollector */ - public function __construct(array $server, L10n $l10n, RouteCollector $routeCollector = null) + public function __construct(array $server, string $baseRoutesFilepath, L10n $l10n, ICache $cache, RouteCollector $routeCollector = null) { + $this->baseRoutesFilepath = $baseRoutesFilepath; $this->l10n = $l10n; + $this->cache = $cache; $httpMethod = $server['REQUEST_METHOD'] ?? self::GET; $this->httpMethod = in_array($httpMethod, self::ALLOWED_METHODS) ? $httpMethod : self::GET; @@ -84,6 +96,9 @@ class Router } /** + * This will be called either automatically if a base routes file path was submitted, + * or can be called manually with a custom route array. + * * @param array $routes The routes to add to the Router * * @return self The router instance with the loaded routes @@ -100,6 +115,9 @@ class Router $this->routeCollector = $routeCollector; + // Add routes from addons + Hook::callAll('route_collection', $this->routeCollector); + return $this; } @@ -191,12 +209,9 @@ class Router */ public function getModuleClass($cmd) { - // Add routes from addons - Hook::callAll('route_collection', $this->routeCollector); - $cmd = '/' . ltrim($cmd, '/'); - $dispatcher = new Dispatcher\GroupCountBased($this->routeCollector->getData()); + $dispatcher = new Dispatcher\GroupCountBased($this->getCachedDispatchData()); $moduleClass = null; $this->parameters = []; @@ -223,4 +238,51 @@ class Router { return $this->parameters; } + + /** + * If a base routes file path has been provided, we can load routes from it if the cache misses. + * + * @return array + * @throws HTTPException\InternalServerErrorException + */ + private function getDispatchData() + { + $dispatchData = []; + + if ($this->baseRoutesFilepath && file_exists($this->baseRoutesFilepath)) { + $dispatchData = require $this->baseRoutesFilepath; + if (!is_array($dispatchData)) { + throw new HTTPException\InternalServerErrorException('Invalid base routes file'); + } + } + + $this->loadRoutes($dispatchData); + + return $this->routeCollector->getData(); + } + + /** + * We cache the dispatch data for speed, as computing the current routes (version 2020.09) + * takes about 850ms for each requests. + * + * The cached "routerDispatchData" lasts for a day, and must be cleared manually when there + * is any changes in the enabled addons list. + * + * @return array|mixed + * @throws HTTPException\InternalServerErrorException + */ + private function getCachedDispatchData() + { + $routerDispatchData = $this->cache->get('routerDispatchData'); + + if ($routerDispatchData) { + return $routerDispatchData; + } + + $routerDispatchData = $this->getDispatchData(); + + $this->cache->set('routerDispatchData', $routerDispatchData, Duration::DAY); + + return $routerDispatchData; + } } diff --git a/src/Core/Hook.php b/src/Core/Hook.php index 851dd60ed..d7b1f737a 100644 --- a/src/Core/Hook.php +++ b/src/Core/Hook.php @@ -247,6 +247,8 @@ class Hook /** * Deletes one or more hook records * + * We have to clear the cached routerDispatchData because addons can provide routes + * * @param array $condition * @param array $options * @return bool @@ -256,12 +258,18 @@ class Hook { $result = DBA::delete('hook', $condition, $options); + if ($result) { + DI::cache()->delete('routerDispatchData'); + } + return $result; } /** * Inserts a hook record * + * We have to clear the cached routerDispatchData because addons can provide routes + * * @param array $condition * @return bool * @throws \Exception @@ -270,6 +278,10 @@ class Hook { $result = DBA::insert('hook', $condition); + if ($result) { + DI::cache()->delete('routerDispatchData'); + } + return $result; } } diff --git a/static/dependencies.config.php b/static/dependencies.config.php index fe8a8caee..3df54b79e 100644 --- a/static/dependencies.config.php +++ b/static/dependencies.config.php @@ -191,10 +191,9 @@ return [ ], App\Router::class => [ 'constructParams' => [ - $_SERVER, null - ], - 'call' => [ - ['loadRoutes', [include __DIR__ . '/routes.config.php'], Dice::CHAIN_CALL], + $_SERVER, + __DIR__ . '/routes.config.php', + null ], ], L10n::class => [ From ce04c13ea8b6fc80f71e8d1740dc8a63d291a7f5 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 27 Jul 2020 01:58:12 -0400 Subject: [PATCH 36/44] Update App\Router-related tests after constructor signature change --- tests/src/App/ModuleTest.php | 7 ++++++- tests/src/App/RouterTest.php | 36 ++++++++++++++++++++++++------------ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/tests/src/App/ModuleTest.php b/tests/src/App/ModuleTest.php index a7c439d1f..03bb14b60 100644 --- a/tests/src/App/ModuleTest.php +++ b/tests/src/App/ModuleTest.php @@ -22,6 +22,7 @@ namespace Friendica\Test\src\App; use Friendica\App; +use Friendica\Core\Cache\ICache; use Friendica\Core\Config\IConfig; use Friendica\Core\L10n; use Friendica\LegacyModule; @@ -175,7 +176,11 @@ class ModuleTest extends DatabaseTest $l10n = \Mockery::mock(L10n::class); $l10n->shouldReceive('t')->andReturnUsing(function ($args) { return $args; }); - $router = (new App\Router([], $l10n))->loadRoutes(include __DIR__ . '/../../../static/routes.config.php'); + $cache = \Mockery::mock(ICache::class); + $cache->shouldReceive('get')->with('routerDispatchData')->andReturn('')->atMost()->once(); + $cache->shouldReceive('set')->withAnyArgs()->andReturn(false)->atMost()->once(); + + $router = (new App\Router([], __DIR__ . '/../../../static/routes.config.php', $l10n, $cache)); $module = (new App\Module($name))->determineClass(new App\Arguments('', $command), $router, $config); diff --git a/tests/src/App/RouterTest.php b/tests/src/App/RouterTest.php index 064e37a12..df1ea5e9a 100644 --- a/tests/src/App/RouterTest.php +++ b/tests/src/App/RouterTest.php @@ -22,6 +22,7 @@ namespace Friendica\Test\src\App; use Friendica\App\Router; +use Friendica\Core\Cache\ICache; use Friendica\Core\L10n; use Friendica\Module; use Friendica\Network\HTTPException\MethodNotAllowedException; @@ -33,6 +34,10 @@ class RouterTest extends TestCase { /** @var L10n|MockInterface */ private $l10n; + /** + * @var ICache + */ + private $cache; protected function setUp() { @@ -40,11 +45,15 @@ class RouterTest extends TestCase $this->l10n = \Mockery::mock(L10n::class); $this->l10n->shouldReceive('t')->andReturnUsing(function ($args) { return $args; }); + + $this->cache = \Mockery::mock(ICache::class); + $this->cache->shouldReceive('get')->andReturn(null); + $this->cache->shouldReceive('set')->andReturn(false); } public function testGetModuleClass() { - $router = new Router(['REQUEST_METHOD' => Router::GET], $this->l10n); + $router = new Router(['REQUEST_METHOD' => Router::GET], '', $this->l10n, $this->cache); $routeCollector = $router->getRouteCollector(); $routeCollector->addRoute([Router::GET], '/', 'IndexModuleClassName'); @@ -68,7 +77,7 @@ class RouterTest extends TestCase public function testPostModuleClass() { - $router = new Router(['REQUEST_METHOD' => Router::POST], $this->l10n); + $router = new Router(['REQUEST_METHOD' => Router::POST], '', $this->l10n, $this->cache); $routeCollector = $router->getRouteCollector(); $routeCollector->addRoute([Router::POST], '/', 'IndexModuleClassName'); @@ -94,7 +103,7 @@ class RouterTest extends TestCase { $this->expectException(NotFoundException::class); - $router = new Router(['REQUEST_METHOD' => Router::GET], $this->l10n); + $router = new Router(['REQUEST_METHOD' => Router::GET], '', $this->l10n, $this->cache); $router->getModuleClass('/unsupported'); } @@ -103,7 +112,7 @@ class RouterTest extends TestCase { $this->expectException(NotFoundException::class); - $router = new Router(['REQUEST_METHOD' => Router::GET], $this->l10n); + $router = new Router(['REQUEST_METHOD' => Router::GET], '', $this->l10n, $this->cache); $routeCollector = $router->getRouteCollector(); $routeCollector->addRoute([Router::GET], '/test', 'TestModuleClassName'); @@ -115,7 +124,7 @@ class RouterTest extends TestCase { $this->expectException(NotFoundException::class); - $router = new Router(['REQUEST_METHOD' => Router::GET], $this->l10n); + $router = new Router(['REQUEST_METHOD' => Router::GET], '', $this->l10n, $this->cache); $routeCollector = $router->getRouteCollector(); $routeCollector->addRoute([Router::GET], '/optional[/option]', 'OptionalModuleClassName'); @@ -127,7 +136,7 @@ class RouterTest extends TestCase { $this->expectException(NotFoundException::class); - $router = new Router(['REQUEST_METHOD' => Router::GET], $this->l10n); + $router = new Router(['REQUEST_METHOD' => Router::GET], '', $this->l10n, $this->cache); $routeCollector = $router->getRouteCollector(); $routeCollector->addRoute([Router::GET], '/variable/{var}', 'VariableModuleClassName'); @@ -139,7 +148,7 @@ class RouterTest extends TestCase { $this->expectException(MethodNotAllowedException::class); - $router = new Router(['REQUEST_METHOD' => Router::POST], $this->l10n); + $router = new Router(['REQUEST_METHOD' => Router::POST], '', $this->l10n, $this->cache); $routeCollector = $router->getRouteCollector(); $routeCollector->addRoute([Router::GET], '/test', 'TestModuleClassName'); @@ -151,7 +160,7 @@ class RouterTest extends TestCase { $this->expectException(MethodNotAllowedException::class); - $router = new Router(['REQUEST_METHOD' => Router::GET], $this->l10n); + $router = new Router(['REQUEST_METHOD' => Router::GET], '', $this->l10n, $this->cache); $routeCollector = $router->getRouteCollector(); $routeCollector->addRoute([Router::POST], '/test', 'TestModuleClassName'); @@ -189,9 +198,12 @@ class RouterTest extends TestCase */ public function testGetRoutes(array $routes) { - $router = (new Router([ - 'REQUEST_METHOD' => Router::GET - ], $this->l10n))->loadRoutes($routes); + $router = (new Router( + ['REQUEST_METHOD' => Router::GET], + '', + $this->l10n, + $this->cache + ))->loadRoutes($routes); $this->assertEquals(Module\Home::class, $router->getModuleClass('/')); $this->assertEquals(Module\Friendica::class, $router->getModuleClass('/group/route')); @@ -206,7 +218,7 @@ class RouterTest extends TestCase { $router = (new Router([ 'REQUEST_METHOD' => Router::POST - ], $this->l10n))->loadRoutes($routes); + ], '', $this->l10n, $this->cache))->loadRoutes($routes); // Don't find GET $this->assertEquals(Module\NodeInfo::class, $router->getModuleClass('/post/it')); From 121bb8571989b28527258c321c9246e1dc5c6956 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 27 Jul 2020 04:20:46 -0400 Subject: [PATCH 37/44] Remove expected third parameter value to saveTimestamp() in Util\ProfilerLogger test --- tests/src/Util/Logger/ProfilerLoggerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/Util/Logger/ProfilerLoggerTest.php b/tests/src/Util/Logger/ProfilerLoggerTest.php index 68db1448a..0023dff54 100644 --- a/tests/src/Util/Logger/ProfilerLoggerTest.php +++ b/tests/src/Util/Logger/ProfilerLoggerTest.php @@ -58,7 +58,7 @@ class ProfilerLoggerTest extends MockedTest $logger = new ProfilerLogger($this->logger, $this->profiler); $this->logger->shouldReceive($function)->with($message, $context)->once(); - $this->profiler->shouldReceive('saveTimestamp')->with(\Mockery::any(), 'file', \Mockery::any())->once(); + $this->profiler->shouldReceive('saveTimestamp')->with(\Mockery::any(), 'file')->once(); $logger->$function($message, $context); } @@ -70,7 +70,7 @@ class ProfilerLoggerTest extends MockedTest $logger = new ProfilerLogger($this->logger, $this->profiler); $this->logger->shouldReceive('log')->with(LogLevel::WARNING, 'test', ['a' => 'context'])->once(); - $this->profiler->shouldReceive('saveTimestamp')->with(\Mockery::any(), 'file', \Mockery::any())->once(); + $this->profiler->shouldReceive('saveTimestamp')->with(\Mockery::any(), 'file')->once(); $logger->log(LogLevel::WARNING, 'test', ['a' => 'context']); } From 6ab82eaa4938389501be8556089f2407a2f8f1a4 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 27 Jul 2020 10:11:12 +0000 Subject: [PATCH 38/44] Ensure that cached avatar fields are set --- src/Model/Contact.php | 48 ++++++++++++++++++++++++++++++++++++++++++ src/Module/Contact.php | 3 +++ 2 files changed, 51 insertions(+) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 1cf8c7423..4f8cb84db 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -1787,6 +1787,54 @@ class Contact self::updateAvatar($cid, $contact['avatar'], true); } + /** + * Check the given contact array for avatar cache fields + * + * @param array $contact + * @return array contact array with avatar cache fields + */ + public static function checkAvatarCacheArray(array $contact) + { + $update = false; + $contact_fields = []; + $fields = ['photo', 'thumb', 'micro']; + foreach ($fields as $field) { + if (isset($contact[$field])) { + $contact_fields[] = $field; + } + if (isset($contact[$field]) && empty($contact[$field])) { + $update = true; + } + } + + if (!$update) { + return $contact; + } + + if (!empty($contact['id']) && !empty($contact['avatar'])) { + self::updateAvatar($contact['id'], $contact['avatar'], true); + + $new_contact = self::getById($contact['id'], $contact_fields); + if (DBA::isResult($new_contact)) { + // We only update the cache fields + $contact = array_merge($contact, $new_contact); + } + } + + /// add the default avatars if the fields aren't filled + if (isset($contact['photo']) && empty($contact['photo'])) { + $contact['photo'] = DI::baseUrl() . '/images/person-300.jpg'; + } + if (isset($contact['thumb']) && empty($contact['thumb'])) { + $contact['thumb'] = DI::baseUrl() . '/images/person-80.jpg'; + } + if (isset($contact['micro']) && empty($contact['micro'])) { + $contact['micro'] = DI::baseUrl() . '/images/person-48.jpg'; + } + + return $contact; + } + /** * Updates the avatar links in a contact only if needed * diff --git a/src/Module/Contact.php b/src/Module/Contact.php index 096f69330..e9f00a1b6 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -36,6 +36,7 @@ use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model; +use Friendica\Model\Contact as ModelContact; use Friendica\Module\Security\Login; use Friendica\Network\HTTPException\BadRequestException; use Friendica\Network\HTTPException\NotFoundException; @@ -278,6 +279,8 @@ class Contact extends BaseModule if ($contact['network'] == Protocol::PHANTOM) { $contact = false; } + + $contact = ModelContact::checkAvatarCacheArray($contact); } if (DBA::isResult($contact)) { From 5b884e834892a75f50adc3122d11b74384a711e4 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 27 Jul 2020 10:22:02 +0000 Subject: [PATCH 39/44] Changed function name --- src/Model/Contact.php | 2 +- src/Module/Contact.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 4f8cb84db..9e89fc30a 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -1793,7 +1793,7 @@ class Contact * @param array $contact * @return array contact array with avatar cache fields */ - public static function checkAvatarCacheArray(array $contact) + public static function checkAvatarCacheByArray(array $contact) { $update = false; $contact_fields = []; diff --git a/src/Module/Contact.php b/src/Module/Contact.php index e9f00a1b6..5a4d0b1e8 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -280,7 +280,7 @@ class Contact extends BaseModule $contact = false; } - $contact = ModelContact::checkAvatarCacheArray($contact); + $contact = ModelContact::checkAvatarCacheByArray($contact); } if (DBA::isResult($contact)) { From fd1da749808cdfccf05a4fa9ba8ba6aeafd35a9b Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 27 Jul 2020 11:50:36 +0000 Subject: [PATCH 40/44] Fix fatal error because of unknown function "fetchUrl" --- src/Core/System.php | 2 +- src/Protocol/Salmon.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Core/System.php b/src/Core/System.php index 67ee3a803..8b2c2d500 100644 --- a/src/Core/System.php +++ b/src/Core/System.php @@ -48,7 +48,7 @@ class System $previous = ['class' => '', 'function' => '', 'database' => false]; // The ignore list contains all functions that are only wrapper functions - $ignore = ['fetchUrl', 'call_user_func_array']; + $ignore = ['call_user_func_array']; while ($func = array_pop($trace)) { if (!empty($func['class'])) { diff --git a/src/Protocol/Salmon.php b/src/Protocol/Salmon.php index 921c060f8..88c342a87 100644 --- a/src/Protocol/Salmon.php +++ b/src/Protocol/Salmon.php @@ -22,6 +22,7 @@ namespace Friendica\Protocol; use Friendica\Core\Logger; +use Friendica\DI; use Friendica\Network\Probe; use Friendica\Util\Crypto; use Friendica\Util\Strings; @@ -71,7 +72,7 @@ class Salmon $ret[$x] = substr($ret[$x], 5); } } elseif (Strings::normaliseLink($ret[$x]) == 'http://') { - $ret[$x] = DI::httpRequest()->fetchUrl($ret[$x]); + $ret[$x] = DI::httpRequest()->fetch($ret[$x]); } } } From cd84f9a921885b0cfa8483dc2456f7f3726d9ce6 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 27 Jul 2020 08:24:55 -0400 Subject: [PATCH 41/44] Reduce number of calls to Hook::delete when uninstalling addons/themes - Add a sweeping Hook deletion on theme uninstall (like for addons) --- src/Core/Theme.php | 2 ++ view/theme/frio/theme.php | 12 ------------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/Core/Theme.php b/src/Core/Theme.php index 3548544e9..334f31a6e 100644 --- a/src/Core/Theme.php +++ b/src/Core/Theme.php @@ -158,6 +158,8 @@ class Theme if (function_exists($func)) { $func(); } + + Hook::delete(['file' => "view/theme/$theme/theme.php"]); } $allowed_themes = Theme::getAllowedList(); diff --git a/view/theme/frio/theme.php b/view/theme/frio/theme.php index fd6927835..8b49d9c30 100644 --- a/view/theme/frio/theme.php +++ b/view/theme/frio/theme.php @@ -54,18 +54,6 @@ function frio_install() Logger::log('installed theme frio'); } -function frio_uninstall() -{ - Hook::unregister('prepare_body_final', 'view/theme/frio/theme.php', 'frio_item_photo_links'); - Hook::unregister('item_photo_menu', 'view/theme/frio/theme.php', 'frio_item_photo_menu'); - Hook::unregister('contact_photo_menu', 'view/theme/frio/theme.php', 'frio_contact_photo_menu'); - Hook::unregister('nav_info', 'view/theme/frio/theme.php', 'frio_remote_nav'); - Hook::unregister('acl_lookup_end', 'view/theme/frio/theme.php', 'frio_acl_lookup'); - Hook::unregister('display_item', 'view/theme/frio/theme.php', 'frio_display_item'); - - Logger::log('uninstalled theme frio'); -} - /** * Replace friendica photo links hook * From 477e9bd67a0d88b959af363739ab3f8469852f46 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 27 Jul 2020 19:36:11 +0000 Subject: [PATCH 42/44] Selective probing for AP --- src/Model/ContactRelation.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Model/ContactRelation.php b/src/Model/ContactRelation.php index 06c25059f..bc4a38b74 100644 --- a/src/Model/ContactRelation.php +++ b/src/Model/ContactRelation.php @@ -22,6 +22,7 @@ namespace Friendica\Model; use Friendica\Core\Logger; +use Friendica\Core\Protocol; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Protocol\ActivityPub; @@ -100,7 +101,13 @@ class ContactRelation return; } - $apcontact = APContact::getByURL($url); + if (in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN])) { + // The contact is (most likely) speaking AP, so updating is allowed + $apcontact = APContact::getByURL($url); + } else { + // The contact isn't obviously speaking AP, so we don't allow updating + $apcontact = APContact::getByURL($url, false); + } if (!empty($apcontact['followers']) && is_string($apcontact['followers'])) { $followers = ActivityPub::fetchItems($apcontact['followers']); From 3cc026b8a850fa196784d987d115f6da509cc4d0 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 27 Jul 2020 19:45:35 +0000 Subject: [PATCH 43/44] Adding OStatus as well --- src/Model/ContactRelation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/ContactRelation.php b/src/Model/ContactRelation.php index bc4a38b74..dfbea17cb 100644 --- a/src/Model/ContactRelation.php +++ b/src/Model/ContactRelation.php @@ -101,7 +101,7 @@ class ContactRelation return; } - if (in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN])) { + if (in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::OSTATUS])) { // The contact is (most likely) speaking AP, so updating is allowed $apcontact = APContact::getByURL($url); } else { From 2497d36030154dc317a881f53d7bcc5f548c89dd Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 27 Jul 2020 15:54:36 -0400 Subject: [PATCH 44/44] Remove unnecessary parent call in PermissionTooltip::rawContent --- src/Module/PermissionTooltip.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Module/PermissionTooltip.php b/src/Module/PermissionTooltip.php index 59478b326..3e760ef1e 100644 --- a/src/Module/PermissionTooltip.php +++ b/src/Module/PermissionTooltip.php @@ -16,8 +16,6 @@ class PermissionTooltip extends \Friendica\BaseModule { public static function rawContent(array $parameters = []) { - parent::rawContent($parameters); // TODO: Change the autogenerated stub - $type = $parameters['type']; $referenceId = $parameters['id'];