From 13a4483c59d916b48f9585beb7f5a71328055b72 Mon Sep 17 00:00:00 2001 From: Tobias Diekershoff Date: Sun, 12 Jul 2020 20:50:27 +0200 Subject: [PATCH 001/573] Version 2020.09-dev --- VERSION | 2 +- boot.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 13c5ea6649..454e445f6e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2020.07 +2020.09-dev diff --git a/boot.php b/boot.php index cf86d67627..b07ad8483f 100644 --- a/boot.php +++ b/boot.php @@ -38,7 +38,7 @@ use Friendica\Util\DateTimeFormat; define('FRIENDICA_PLATFORM', 'Friendica'); define('FRIENDICA_CODENAME', 'Red Hot Poker'); -define('FRIENDICA_VERSION', '2020.07'); +define('FRIENDICA_VERSION', '2020.09-dev'); define('DFRN_PROTOCOL_VERSION', '2.23'); define('NEW_UPDATE_ROUTINE_VERSION', 1170); From a345a7acfbe550715d64dba08c20f8bde9e2fc46 Mon Sep 17 00:00:00 2001 From: Tobias Diekershoff Date: Sun, 12 Jul 2020 21:23:03 +0200 Subject: [PATCH 002/573] mark 2020.09 as unreleased in the CHANGELOG --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 5de7de698c..2c5662c2b7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +Version 2020.09 (unreleased) + Version 2020.07 (2020-07-12) Friendica Core: Update to the translations: DE, EN GB, EN US, FR, ET, NL, PL, RU, ZH-CN [translation teams] From 2a1e6e1a74c9a6836b8528a76fdd382d7d7a5160 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 12 Jul 2020 21:53:17 +0000 Subject: [PATCH 003/573] Support Nodeinfo2 --- src/Model/Nodeinfo.php | 1 + src/Model/User.php | 7 ++++ src/Module/NodeInfo.php | 87 +++++++++++++++++++++++++++++++++++++--- static/routes.config.php | 1 + 4 files changed, 91 insertions(+), 5 deletions(-) diff --git a/src/Model/Nodeinfo.php b/src/Model/Nodeinfo.php index d2e168fa59..7ccfefd007 100644 --- a/src/Model/Nodeinfo.php +++ b/src/Model/Nodeinfo.php @@ -55,6 +55,7 @@ class Nodeinfo $config->set('nodeinfo', 'total_users', $userStats['total_users']); $config->set('nodeinfo', 'active_users_halfyear', $userStats['active_users_halfyear']); $config->set('nodeinfo', 'active_users_monthly', $userStats['active_users_monthly']); + $config->set('nodeinfo', 'active_users_weekly', $userStats['active_users_weekly']); $logger->debug('user statistics', $userStats); diff --git a/src/Model/User.php b/src/Model/User.php index 16dfb51220..b4ada344e1 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -1272,6 +1272,7 @@ class User 'total_users' => 0, 'active_users_halfyear' => 0, 'active_users_monthly' => 0, + 'active_users_weekly' => 0, ]; $userStmt = DBA::select('owner-view', ['uid', 'login_date', 'last-item'], @@ -1284,6 +1285,7 @@ class User $halfyear = time() - (180 * 24 * 60 * 60); $month = time() - (30 * 24 * 60 * 60); + $week = time() - (7 * 24 * 60 * 60); while ($user = DBA::fetch($userStmt)) { $statistics['total_users']++; @@ -1297,6 +1299,11 @@ class User ) { $statistics['active_users_monthly']++; } + + if ((strtotime($user['login_date']) > $week) || (strtotime($user['last-item']) > $week) + ) { + $statistics['active_users_weekly']++; + } } DBA::close($userStmt); diff --git a/src/Module/NodeInfo.php b/src/Module/NodeInfo.php index 87321489f3..eca0ae3e34 100644 --- a/src/Module/NodeInfo.php +++ b/src/Module/NodeInfo.php @@ -24,6 +24,7 @@ namespace Friendica\Module; use Friendica\BaseModule; use Friendica\Core\Addon; use Friendica\DI; +use Friendica\Model\User; use stdClass; /** @@ -34,10 +35,12 @@ class NodeInfo extends BaseModule { public static function rawContent(array $parameters = []) { - if ($parameters['version'] == '1.0') { + if (empty($parameters['version'])) { + self::printNodeInfo2(); + } elseif ($parameters['version'] == '1.0') { self::printNodeInfo1(); } elseif ($parameters['version'] == '2.0') { - self::printNodeInfo2(); + self::printNodeInfo20(); } else { throw new \Friendica\Network\HTTPException\NotFoundException(); } @@ -48,7 +51,7 @@ class NodeInfo extends BaseModule * * @return Object with supported services */ - private static function getUsage() + private static function getUsage(bool $version2 = false) { $config = DI::config(); @@ -62,6 +65,10 @@ class NodeInfo extends BaseModule ]; $usage->localPosts = intval($config->get('nodeinfo', 'local_posts')); $usage->localComments = intval($config->get('nodeinfo', 'local_comments')); + + if ($version2) { + $usage->users['activeWeek'] = intval($config->get('nodeinfo', 'active_users_weekly')); + } } return $usage; @@ -189,9 +196,9 @@ class NodeInfo extends BaseModule } /** - * Print the nodeinfo version 2 + * Print the nodeinfo version 2.0 */ - private static function printNodeInfo2() + private static function printNodeInfo20() { $config = DI::config(); @@ -242,4 +249,74 @@ class NodeInfo extends BaseModule echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); exit; } + + /** + * Print the nodeinfo version 2 + */ + private static function printNodeInfo2() + { + $config = DI::config(); + + $imap = (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only')); + + $nodeinfo = [ + 'version' => '1.0', + 'server' => [ + 'baseUrl' => DI::baseUrl()->get(), + 'name' => $config->get('config', 'sitename'), + 'software' => 'friendica', + 'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION, + ], + 'organization' => self::getOrganization($config), + 'protocols' => ['dfrn', 'activitypub'], + 'services' => [], + 'openRegistrations' => intval($config->get('config', 'register_policy')) !== Register::CLOSED, + 'usage' => [], + ]; + + if (!empty($config->get('system', 'diaspora_enabled'))) { + $nodeinfo['protocols'][] = 'diaspora'; + } + + if (empty($config->get('system', 'ostatus_disabled'))) { + $nodeinfo['protocols'][] = 'ostatus'; + } + + $nodeinfo['usage'] = self::getUsage(true); + + $nodeinfo['services'] = self::getServices(); + + if (Addon::isEnabled('twitter')) { + $nodeinfo['services']['inbound'][] = 'twitter'; + } + + $nodeinfo['services']['inbound'][] = 'atom1.0'; + $nodeinfo['services']['inbound'][] = 'rss2.0'; + $nodeinfo['services']['outbound'][] = 'atom1.0'; + + if ($imap) { + $nodeinfo['services']['inbound'][] = 'imap'; + } + + header('Content-type: application/json; charset=utf-8'); + echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + exit; + } + + private static function getOrganization($config) + { + $organization = ['name' => null, 'contact' => null, 'account' => null]; + + if (!empty($config->get('config', 'admin_email'))) { + $adminList = explode(',', str_replace(' ', '', $config->get('config', 'admin_email'))); + $organization['contact'] = $adminList[0]; + $administrator = User::getByEmail($adminList[0], ['username', 'nickname']); + if (!empty($administrator)) { + $organization['name'] = $administrator['username']; + $organization['account'] = DI::baseUrl()->get() . '/profile/' . $administrator['nickname']; + } + } + + return $organization; + } } diff --git a/static/routes.config.php b/static/routes.config.php index 074c1f5710..cb569e01a0 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -37,6 +37,7 @@ return [ '/host-meta' => [Module\WellKnown\HostMeta::class, [R::GET]], '/nodeinfo' => [Module\WellKnown\NodeInfo::class, [R::GET]], '/webfinger' => [Module\Xrd::class, [R::GET]], + '/x-nodeinfo2' => [Module\NodeInfo::class, [R::GET]], '/x-social-relay' => [Module\WellKnown\XSocialRelay::class, [R::GET]], ], From 6ad8bf0cca82b34bad742826b9caaeb719014979 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sun, 12 Jul 2020 22:41:12 -0400 Subject: [PATCH 004/573] Remove network tabs additional features - Remove New network tab - Remove Bookmark network tab --- mod/network.php | 50 +++++------------------------------------ src/Content/Feature.php | 7 ------ 2 files changed, 6 insertions(+), 51 deletions(-) diff --git a/mod/network.php b/mod/network.php index 16fde6c351..3311a796a8 100644 --- a/mod/network.php +++ b/mod/network.php @@ -102,9 +102,7 @@ function network_init(App $a) 'order=activity', //all 'order=post', //postord 'conv=1', //conv - 'new=1', //new 'star=1', //starred - 'bmark=1', //bookmarked ]; $k = array_search('active', $last_sel_tabs); @@ -154,40 +152,28 @@ function network_init(App $a) * '/network?order=activity' => $activity_active = 'active' * '/network?order=post' => $postord_active = 'active' * '/network?conv=1', => $conv_active = 'active' - * '/network?new=1', => $new_active = 'active' * '/network?star=1', => $starred_active = 'active' - * '/network?bmark=1', => $bookmarked_active = 'active' * * @param App $a - * @return array ($no_active, $activity_active, $postord_active, $conv_active, $new_active, $starred_active, $bookmarked_active); + * @return array ($no_active, $activity_active, $postord_active, $conv_active, $starred_active); */ function network_query_get_sel_tab(App $a) { $no_active = ''; $starred_active = ''; - $new_active = ''; - $bookmarked_active = ''; $all_active = ''; $conv_active = ''; $postord_active = ''; - if (!empty($_GET['new'])) { - $new_active = 'active'; - } - if (!empty($_GET['star'])) { $starred_active = 'active'; } - if (!empty($_GET['bmark'])) { - $bookmarked_active = 'active'; - } - if (!empty($_GET['conv'])) { $conv_active = 'active'; } - if (($new_active == '') && ($starred_active == '') && ($bookmarked_active == '') && ($conv_active == '')) { + if (($starred_active == '') && ($conv_active == '')) { $no_active = 'active'; } @@ -198,7 +184,7 @@ function network_query_get_sel_tab(App $a) } } - return [$no_active, $all_active, $postord_active, $conv_active, $new_active, $starred_active, $bookmarked_active]; + return [$no_active, $all_active, $postord_active, $conv_active, $starred_active]; } function network_query_get_sel_group(App $a) @@ -312,7 +298,7 @@ function network_content(App $a, $update = 0, $parent = 0) $arr = ['query' => DI::args()->getQueryString()]; Hook::callAll('network_content_init', $arr); - if (!empty($_GET['new']) || !empty($_GET['file'])) { + if (!empty($_GET['file'])) { $o = networkFlatView($a, $update); } else { $o = networkThreadedView($a, $update, $parent); @@ -468,7 +454,6 @@ function networkThreadedView(App $a, $update, $parent) $cid = intval($_GET['contactid'] ?? 0); $star = intval($_GET['star'] ?? 0); - $bmark = intval($_GET['bmark'] ?? 0); $conv = intval($_GET['conv'] ?? 0); $order = Strings::escapeTags(($_GET['order'] ?? '') ?: 'activity'); $nets = $_GET['nets'] ?? ''; @@ -543,7 +528,6 @@ function networkThreadedView(App $a, $update, $parent) $sql_post_table = ''; $sql_options = ($star ? " AND `thread`.`starred` " : ''); - $sql_options .= ($bmark ? sprintf(" AND `thread`.`post-type` = %d ", Item::PT_PAGE) : ''); $sql_extra = $sql_options; $sql_extra2 = ''; $sql_extra3 = ''; @@ -888,7 +872,7 @@ function network_tabs(App $a) // item filter tabs /// @TODO fix this logic, reduce duplication /// DI::page()['content'] .= '
'; - list($no_active, $all_active, $post_active, $conv_active, $new_active, $starred_active, $bookmarked_active) = network_query_get_sel_tab($a); + list($no_active, $all_active, $post_active, $conv_active, $starred_active) = network_query_get_sel_tab($a); // if no tabs are selected, defaults to activitys if ($no_active == 'active') { @@ -931,28 +915,6 @@ function network_tabs(App $a) 'accesskey' => 'r', ]; - if (Feature::isEnabled(local_user(), 'new_tab')) { - $tabs[] = [ - 'label' => DI::l10n()->t('New'), - 'url' => $cmd . '?' . http_build_query(array_merge($def_param, ['new' => true])), - 'sel' => $new_active, - 'title' => DI::l10n()->t('Activity Stream - by date'), - 'id' => 'activitiy-by-date-tab', - 'accesskey' => 'w', - ]; - } - - if (Feature::isEnabled(local_user(), 'link_tab')) { - $tabs[] = [ - 'label' => DI::l10n()->t('Shared Links'), - 'url' => $cmd . '?' . http_build_query(array_merge($def_param, ['bmark' => true])), - 'sel' => $bookmarked_active, - 'title' => DI::l10n()->t('Interesting Links'), - 'id' => 'shared-links-tab', - 'accesskey' => 'b', - ]; - } - $tabs[] = [ 'label' => DI::l10n()->t('Starred'), 'url' => $cmd . '?' . http_build_query(array_merge($def_param, ['star' => true])), @@ -965,7 +927,7 @@ function network_tabs(App $a) // save selected tab, but only if not in file mode if (empty($_GET['file'])) { DI::pConfig()->set(local_user(), 'network.view', 'tab.selected', [ - $all_active, $post_active, $conv_active, $new_active, $starred_active, $bookmarked_active + $all_active, $post_active, $conv_active, $starred_active ]); } diff --git a/src/Content/Feature.php b/src/Content/Feature.php index 880a6706bc..ee755a2b35 100644 --- a/src/Content/Feature.php +++ b/src/Content/Feature.php @@ -114,13 +114,6 @@ class Feature ['networks', DI::l10n()->t('Protocol Filter'), DI::l10n()->t('Enable widget to display Network posts only from selected protocols'), false, DI::config()->get('feature_lock', 'networks', false)], ], - // Network tabs - 'net_tabs' => [ - DI::l10n()->t('Network Tabs'), - ['new_tab', DI::l10n()->t('Network New Tab'), DI::l10n()->t("Enable tab to display only new Network posts \x28from the last 12 hours\x29"), false, DI::config()->get('feature_lock', 'new_tab', false)], - ['link_tab', DI::l10n()->t('Network Shared Links Tab'), DI::l10n()->t('Enable tab to display only Network posts with links in them'), false, DI::config()->get('feature_lock', 'link_tab', false)], - ], - // Item tools 'tools' => [ DI::l10n()->t('Post/Comment Tools'), From d11125d2341ec78847ca4c37789205cb576dc054 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sun, 12 Jul 2020 22:52:51 -0400 Subject: [PATCH 005/573] Move network sidebar widget additional features to core - Make Archives filter core - Make Protocol filter core --- src/Content/Feature.php | 7 ------- src/Content/Widget.php | 8 -------- 2 files changed, 15 deletions(-) diff --git a/src/Content/Feature.php b/src/Content/Feature.php index ee755a2b35..b35aa3f363 100644 --- a/src/Content/Feature.php +++ b/src/Content/Feature.php @@ -107,13 +107,6 @@ class Feature ['explicit_mentions', DI::l10n()->t('Explicit Mentions'), DI::l10n()->t('Add explicit mentions to comment box for manual control over who gets mentioned in replies.'), false, DI::config()->get('feature_lock', 'explicit_mentions', false)], ], - // Network sidebar widgets - 'widgets' => [ - DI::l10n()->t('Network Sidebar'), - ['archives', DI::l10n()->t('Archives'), DI::l10n()->t('Ability to select posts by date ranges'), false, DI::config()->get('feature_lock', 'archives', false)], - ['networks', DI::l10n()->t('Protocol Filter'), DI::l10n()->t('Enable widget to display Network posts only from selected protocols'), false, DI::config()->get('feature_lock', 'networks', false)], - ], - // Item tools 'tools' => [ DI::l10n()->t('Post/Comment Tools'), diff --git a/src/Content/Widget.php b/src/Content/Widget.php index 8c72f68f4c..ffc5debb33 100644 --- a/src/Content/Widget.php +++ b/src/Content/Widget.php @@ -269,10 +269,6 @@ class Widget return ''; } - if (!Feature::isEnabled(local_user(), 'networks')) { - return ''; - } - $extra_sql = self::unavailableNetworks(); $r = DBA::p("SELECT DISTINCT(`network`) FROM `contact` WHERE `uid` = ? AND NOT `deleted` AND `network` != '' $extra_sql ORDER BY `network`", @@ -497,10 +493,6 @@ class Widget { $o = ''; - if (!Feature::isEnabled($uid, 'archives')) { - return $o; - } - $visible_years = DI::pConfig()->get($uid, 'system', 'archive_visible_years', 5); /* arrange the list in years */ From 0816e0330f65e8559859aac4963f7ace48aed65e Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sun, 12 Jul 2020 23:08:38 -0400 Subject: [PATCH 006/573] Move export public calendar additional feature to core --- mod/cal.php | 7 ------- src/Content/Feature.php | 1 - src/Content/Widget/CalendarExport.php | 16 ---------------- 3 files changed, 24 deletions(-) diff --git a/mod/cal.php b/mod/cal.php index dcfb5db9c8..8db2237844 100644 --- a/mod/cal.php +++ b/mod/cal.php @@ -292,13 +292,6 @@ function cal_content(App $a) return; } - // Test permissions - // Respect the export feature setting for all other /cal pages if it's not the own profile - if ((local_user() !== $owner_uid) && !Feature::isEnabled($owner_uid, "export_calendar")) { - notice(DI::l10n()->t('Permission denied.') . EOL); - DI::baseUrl()->redirect('cal/' . $nick); - } - // Get the export data by uid $evexport = Event::exportListByUserId($owner_uid, $format); diff --git a/src/Content/Feature.php b/src/Content/Feature.php index b35aa3f363..0f3493ab29 100644 --- a/src/Content/Feature.php +++ b/src/Content/Feature.php @@ -96,7 +96,6 @@ class Feature DI::l10n()->t('General Features'), //array('expire', DI::l10n()->t('Content Expiration'), DI::l10n()->t('Remove old posts/comments after a period of time')), ['photo_location', DI::l10n()->t('Photo Location'), DI::l10n()->t("Photo metadata is normally stripped. This extracts the location \x28if present\x29 prior to stripping metadata and links it to a map."), false, DI::config()->get('feature_lock', 'photo_location', false)], - ['export_calendar', DI::l10n()->t('Export Public Calendar'), DI::l10n()->t('Ability for visitors to download the public calendar'), false, DI::config()->get('feature_lock', 'export_calendar', false)], ['trending_tags', DI::l10n()->t('Trending Tags'), DI::l10n()->t('Show a community page widget with a list of the most popular tags in recent public posts.'), false, DI::config()->get('feature_lock', 'trending_tags', false)], ], diff --git a/src/Content/Widget/CalendarExport.php b/src/Content/Widget/CalendarExport.php index dda3513fec..9f282d2642 100644 --- a/src/Content/Widget/CalendarExport.php +++ b/src/Content/Widget/CalendarExport.php @@ -54,22 +54,6 @@ class CalendarExport return; } - /* - * If it's a kind of profile page (intval($owner_uid)) return if the user not logged in and - * export feature isn't enabled. - */ - /* - * Cal logged in user (test permission at foreign profile page). - * If the $owner uid is available we know it is part of one of the profile pages (like /cal). - * So we have to test if if it's the own profile page of the logged in user - * or a foreign one. For foreign profile pages we need to check if the feature - * for exporting the cal is enabled (otherwise the widget would appear for logged in users - * on foreigen profile pages even if the widget is disabled). - */ - if (local_user() != $owner_uid && !Feature::isEnabled($owner_uid, "export_calendar")) { - return; - } - // $a->data is only available if the profile page is visited. If the visited page is not part // of the profile page it should be the personal /events page. So we can use $a->user. $user = ($a->data['user']['nickname'] ?? '') ?: $a->user['nickname']; From 27deb4d188d2a250c15e5df21c849ce114089105 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 13 Jul 2020 09:45:45 +0000 Subject: [PATCH 007/573] Module classes splitted --- src/Model/Nodeinfo.php | 106 ++++++++++++ src/Module/NodeInfo.php | 322 ------------------------------------- src/Module/NodeInfo110.php | 92 +++++++++++ src/Module/NodeInfo120.php | 86 ++++++++++ src/Module/NodeInfo210.php | 84 ++++++++++ static/routes.config.php | 5 +- 6 files changed, 371 insertions(+), 324 deletions(-) delete mode 100644 src/Module/NodeInfo.php create mode 100644 src/Module/NodeInfo110.php create mode 100644 src/Module/NodeInfo120.php create mode 100644 src/Module/NodeInfo210.php diff --git a/src/Model/Nodeinfo.php b/src/Model/Nodeinfo.php index 7ccfefd007..44181ac941 100644 --- a/src/Model/Nodeinfo.php +++ b/src/Model/Nodeinfo.php @@ -24,6 +24,7 @@ namespace Friendica\Model; use Friendica\Core\Addon; use Friendica\Database\DBA; use Friendica\DI; +use stdClass; /** * Model interaction for the nodeinfo @@ -70,4 +71,109 @@ class Nodeinfo } DBA::close($items); } + + /** + * Return the supported services + * + * @return Object with supported services + */ + public static function getUsage(bool $version2 = false) + { + $config = DI::config(); + + $usage = new stdClass(); + + if (!empty($config->get('system', 'nodeinfo'))) { + $usage->users = [ + 'total' => intval($config->get('nodeinfo', 'total_users')), + 'activeHalfyear' => intval($config->get('nodeinfo', 'active_users_halfyear')), + 'activeMonth' => intval($config->get('nodeinfo', 'active_users_monthly')) + ]; + $usage->localPosts = intval($config->get('nodeinfo', 'local_posts')); + $usage->localComments = intval($config->get('nodeinfo', 'local_comments')); + + if ($version2) { + $usage->users['activeWeek'] = intval($config->get('nodeinfo', 'active_users_weekly')); + } + } + + return $usage; + } + + /** + * Return the supported services + * + * @return array with supported services + */ + public static function getServices() + { + $services = [ + 'inbound' => [], + 'outbound' => [], + ]; + + if (Addon::isEnabled('blogger')) { + $services['outbound'][] = 'blogger'; + } + if (Addon::isEnabled('dwpost')) { + $services['outbound'][] = 'dreamwidth'; + } + if (Addon::isEnabled('statusnet')) { + $services['inbound'][] = 'gnusocial'; + $services['outbound'][] = 'gnusocial'; + } + if (Addon::isEnabled('ijpost')) { + $services['outbound'][] = 'insanejournal'; + } + if (Addon::isEnabled('libertree')) { + $services['outbound'][] = 'libertree'; + } + if (Addon::isEnabled('buffer')) { + $services['outbound'][] = 'linkedin'; + } + if (Addon::isEnabled('ljpost')) { + $services['outbound'][] = 'livejournal'; + } + if (Addon::isEnabled('buffer')) { + $services['outbound'][] = 'pinterest'; + } + if (Addon::isEnabled('posterous')) { + $services['outbound'][] = 'posterous'; + } + if (Addon::isEnabled('pumpio')) { + $services['inbound'][] = 'pumpio'; + $services['outbound'][] = 'pumpio'; + } + + $services['outbound'][] = 'smtp'; + + if (Addon::isEnabled('tumblr')) { + $services['outbound'][] = 'tumblr'; + } + if (Addon::isEnabled('twitter') || Addon::isEnabled('buffer')) { + $services['outbound'][] = 'twitter'; + } + if (Addon::isEnabled('wppost')) { + $services['outbound'][] = 'wordpress'; + } + + return $services; + } + + public static function getOrganization($config) + { + $organization = ['name' => null, 'contact' => null, 'account' => null]; + + if (!empty($config->get('config', 'admin_email'))) { + $adminList = explode(',', str_replace(' ', '', $config->get('config', 'admin_email'))); + $organization['contact'] = $adminList[0]; + $administrator = User::getByEmail($adminList[0], ['username', 'nickname']); + if (!empty($administrator)) { + $organization['name'] = $administrator['username']; + $organization['account'] = DI::baseUrl()->get() . '/profile/' . $administrator['nickname']; + } + } + + return $organization; + } } diff --git a/src/Module/NodeInfo.php b/src/Module/NodeInfo.php deleted file mode 100644 index eca0ae3e34..0000000000 --- a/src/Module/NodeInfo.php +++ /dev/null @@ -1,322 +0,0 @@ -. - * - */ - -namespace Friendica\Module; - -use Friendica\BaseModule; -use Friendica\Core\Addon; -use Friendica\DI; -use Friendica\Model\User; -use stdClass; - -/** - * Standardized way of exposing metadata about a server running one of the distributed social networks. - * @see https://github.com/jhass/nodeinfo/blob/master/PROTOCOL.md - */ -class NodeInfo extends BaseModule -{ - public static function rawContent(array $parameters = []) - { - if (empty($parameters['version'])) { - self::printNodeInfo2(); - } elseif ($parameters['version'] == '1.0') { - self::printNodeInfo1(); - } elseif ($parameters['version'] == '2.0') { - self::printNodeInfo20(); - } else { - throw new \Friendica\Network\HTTPException\NotFoundException(); - } - } - - /** - * Return the supported services - * - * @return Object with supported services - */ - private static function getUsage(bool $version2 = false) - { - $config = DI::config(); - - $usage = new stdClass(); - - if (!empty($config->get('system', 'nodeinfo'))) { - $usage->users = [ - 'total' => intval($config->get('nodeinfo', 'total_users')), - 'activeHalfyear' => intval($config->get('nodeinfo', 'active_users_halfyear')), - 'activeMonth' => intval($config->get('nodeinfo', 'active_users_monthly')) - ]; - $usage->localPosts = intval($config->get('nodeinfo', 'local_posts')); - $usage->localComments = intval($config->get('nodeinfo', 'local_comments')); - - if ($version2) { - $usage->users['activeWeek'] = intval($config->get('nodeinfo', 'active_users_weekly')); - } - } - - return $usage; - } - - /** - * Return the supported services - * - * @return array with supported services - */ - private static function getServices() - { - $services = [ - 'inbound' => [], - 'outbound' => [], - ]; - - if (Addon::isEnabled('blogger')) { - $services['outbound'][] = 'blogger'; - } - if (Addon::isEnabled('dwpost')) { - $services['outbound'][] = 'dreamwidth'; - } - if (Addon::isEnabled('statusnet')) { - $services['inbound'][] = 'gnusocial'; - $services['outbound'][] = 'gnusocial'; - } - if (Addon::isEnabled('ijpost')) { - $services['outbound'][] = 'insanejournal'; - } - if (Addon::isEnabled('libertree')) { - $services['outbound'][] = 'libertree'; - } - if (Addon::isEnabled('buffer')) { - $services['outbound'][] = 'linkedin'; - } - if (Addon::isEnabled('ljpost')) { - $services['outbound'][] = 'livejournal'; - } - if (Addon::isEnabled('buffer')) { - $services['outbound'][] = 'pinterest'; - } - if (Addon::isEnabled('posterous')) { - $services['outbound'][] = 'posterous'; - } - if (Addon::isEnabled('pumpio')) { - $services['inbound'][] = 'pumpio'; - $services['outbound'][] = 'pumpio'; - } - - $services['outbound'][] = 'smtp'; - - if (Addon::isEnabled('tumblr')) { - $services['outbound'][] = 'tumblr'; - } - if (Addon::isEnabled('twitter') || Addon::isEnabled('buffer')) { - $services['outbound'][] = 'twitter'; - } - if (Addon::isEnabled('wppost')) { - $services['outbound'][] = 'wordpress'; - } - - return $services; - } - - /** - * Print the nodeinfo version 1 - */ - private static function printNodeInfo1() - { - $config = DI::config(); - - $nodeinfo = [ - 'version' => '1.0', - 'software' => [ - 'name' => 'friendica', - 'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION, - ], - 'protocols' => [ - 'inbound' => [ - 'friendica' - ], - 'outbound' => [ - 'friendica' - ], - ], - 'services' => [], - 'usage' => [], - 'openRegistrations' => intval($config->get('config', 'register_policy')) !== Register::CLOSED, - 'metadata' => [ - 'nodeName' => $config->get('config', 'sitename'), - ], - ]; - - if (!empty($config->get('system', 'diaspora_enabled'))) { - $nodeinfo['protocols']['inbound'][] = 'diaspora'; - $nodeinfo['protocols']['outbound'][] = 'diaspora'; - } - - if (empty($config->get('system', 'ostatus_disabled'))) { - $nodeinfo['protocols']['inbound'][] = 'gnusocial'; - $nodeinfo['protocols']['outbound'][] = 'gnusocial'; - } - - $nodeinfo['usage'] = self::getUsage(); - - $nodeinfo['services'] = self::getServices(); - - $nodeinfo['metadata']['protocols'] = $nodeinfo['protocols']; - $nodeinfo['metadata']['protocols']['outbound'][] = 'atom1.0'; - $nodeinfo['metadata']['protocols']['inbound'][] = 'atom1.0'; - $nodeinfo['metadata']['protocols']['inbound'][] = 'rss2.0'; - - $nodeinfo['metadata']['services'] = $nodeinfo['services']; - - if (Addon::isEnabled('twitter')) { - $nodeinfo['metadata']['services']['inbound'][] = 'twitter'; - } - - $nodeinfo['metadata']['explicitContent'] = $config->get('system', 'explicit_content', false) == true; - - header('Content-type: application/json; charset=utf-8'); - echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); - exit; - } - - /** - * Print the nodeinfo version 2.0 - */ - private static function printNodeInfo20() - { - $config = DI::config(); - - $imap = (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only')); - - $nodeinfo = [ - 'version' => '2.0', - 'software' => [ - 'name' => 'friendica', - 'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION, - ], - 'protocols' => ['dfrn', 'activitypub'], - 'services' => [], - 'usage' => [], - 'openRegistrations' => intval($config->get('config', 'register_policy')) !== Register::CLOSED, - 'metadata' => [ - 'nodeName' => $config->get('config', 'sitename'), - ], - ]; - - if (!empty($config->get('system', 'diaspora_enabled'))) { - $nodeinfo['protocols'][] = 'diaspora'; - } - - if (empty($config->get('system', 'ostatus_disabled'))) { - $nodeinfo['protocols'][] = 'ostatus'; - } - - $nodeinfo['usage'] = self::getUsage(); - - $nodeinfo['services'] = self::getServices(); - - if (Addon::isEnabled('twitter')) { - $nodeinfo['services']['inbound'][] = 'twitter'; - } - - $nodeinfo['services']['inbound'][] = 'atom1.0'; - $nodeinfo['services']['inbound'][] = 'rss2.0'; - $nodeinfo['services']['outbound'][] = 'atom1.0'; - - if ($imap) { - $nodeinfo['services']['inbound'][] = 'imap'; - } - - $nodeinfo['metadata']['explicitContent'] = $config->get('system', 'explicit_content', false) == true; - - header('Content-type: application/json; charset=utf-8'); - echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); - exit; - } - - /** - * Print the nodeinfo version 2 - */ - private static function printNodeInfo2() - { - $config = DI::config(); - - $imap = (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only')); - - $nodeinfo = [ - 'version' => '1.0', - 'server' => [ - 'baseUrl' => DI::baseUrl()->get(), - 'name' => $config->get('config', 'sitename'), - 'software' => 'friendica', - 'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION, - ], - 'organization' => self::getOrganization($config), - 'protocols' => ['dfrn', 'activitypub'], - 'services' => [], - 'openRegistrations' => intval($config->get('config', 'register_policy')) !== Register::CLOSED, - 'usage' => [], - ]; - - if (!empty($config->get('system', 'diaspora_enabled'))) { - $nodeinfo['protocols'][] = 'diaspora'; - } - - if (empty($config->get('system', 'ostatus_disabled'))) { - $nodeinfo['protocols'][] = 'ostatus'; - } - - $nodeinfo['usage'] = self::getUsage(true); - - $nodeinfo['services'] = self::getServices(); - - if (Addon::isEnabled('twitter')) { - $nodeinfo['services']['inbound'][] = 'twitter'; - } - - $nodeinfo['services']['inbound'][] = 'atom1.0'; - $nodeinfo['services']['inbound'][] = 'rss2.0'; - $nodeinfo['services']['outbound'][] = 'atom1.0'; - - if ($imap) { - $nodeinfo['services']['inbound'][] = 'imap'; - } - - header('Content-type: application/json; charset=utf-8'); - echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); - exit; - } - - private static function getOrganization($config) - { - $organization = ['name' => null, 'contact' => null, 'account' => null]; - - if (!empty($config->get('config', 'admin_email'))) { - $adminList = explode(',', str_replace(' ', '', $config->get('config', 'admin_email'))); - $organization['contact'] = $adminList[0]; - $administrator = User::getByEmail($adminList[0], ['username', 'nickname']); - if (!empty($administrator)) { - $organization['name'] = $administrator['username']; - $organization['account'] = DI::baseUrl()->get() . '/profile/' . $administrator['nickname']; - } - } - - return $organization; - } -} diff --git a/src/Module/NodeInfo110.php b/src/Module/NodeInfo110.php new file mode 100644 index 0000000000..954f362190 --- /dev/null +++ b/src/Module/NodeInfo110.php @@ -0,0 +1,92 @@ +. + * + */ + +namespace Friendica\Module; + +use Friendica\BaseModule; +use Friendica\Core\Addon; +use Friendica\DI; +use Friendica\Model\Nodeinfo; + +/** + * Version 1.0 of Nodeinfo, a standardized way of exposing metadata about a server running one of the distributed social networks. + * @see https://github.com/jhass/nodeinfo/blob/master/PROTOCOL.md + */ +class NodeInfo110 extends BaseModule +{ + public static function rawContent(array $parameters = []) + { + $config = DI::config(); + + $nodeinfo = [ + 'version' => '1.0', + 'software' => [ + 'name' => 'friendica', + 'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION, + ], + 'protocols' => [ + 'inbound' => [ + 'friendica' + ], + 'outbound' => [ + 'friendica' + ], + ], + 'services' => [], + 'usage' => [], + 'openRegistrations' => intval($config->get('config', 'register_policy')) !== Register::CLOSED, + 'metadata' => [ + 'nodeName' => $config->get('config', 'sitename'), + ], + ]; + + if (!empty($config->get('system', 'diaspora_enabled'))) { + $nodeinfo['protocols']['inbound'][] = 'diaspora'; + $nodeinfo['protocols']['outbound'][] = 'diaspora'; + } + + if (empty($config->get('system', 'ostatus_disabled'))) { + $nodeinfo['protocols']['inbound'][] = 'gnusocial'; + $nodeinfo['protocols']['outbound'][] = 'gnusocial'; + } + + $nodeinfo['usage'] = Nodeinfo::getUsage(); + + $nodeinfo['services'] = Nodeinfo::getServices(); + + $nodeinfo['metadata']['protocols'] = $nodeinfo['protocols']; + $nodeinfo['metadata']['protocols']['outbound'][] = 'atom1.0'; + $nodeinfo['metadata']['protocols']['inbound'][] = 'atom1.0'; + $nodeinfo['metadata']['protocols']['inbound'][] = 'rss2.0'; + + $nodeinfo['metadata']['services'] = $nodeinfo['services']; + + if (Addon::isEnabled('twitter')) { + $nodeinfo['metadata']['services']['inbound'][] = 'twitter'; + } + + $nodeinfo['metadata']['explicitContent'] = $config->get('system', 'explicit_content', false) == true; + + header('Content-type: application/json; charset=utf-8'); + echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + exit; + } +} diff --git a/src/Module/NodeInfo120.php b/src/Module/NodeInfo120.php new file mode 100644 index 0000000000..330c5e10d5 --- /dev/null +++ b/src/Module/NodeInfo120.php @@ -0,0 +1,86 @@ +. + * + */ + +namespace Friendica\Module; + +use Friendica\BaseModule; +use Friendica\Core\Addon; +use Friendica\DI; +use Friendica\Model\Nodeinfo; + +/** + * Version 2.0 of Nodeinfo, a standardized way of exposing metadata about a server running one of the distributed social networks. + * @see https://github.com/jhass/nodeinfo/blob/master/PROTOCOL.md + */ +class NodeInfo120 extends BaseModule +{ + public static function rawContent(array $parameters = []) + { + $config = DI::config(); + + $imap = (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only')); + + $nodeinfo = [ + 'version' => '2.0', + 'software' => [ + 'name' => 'friendica', + 'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION, + ], + 'protocols' => ['dfrn', 'activitypub'], + 'services' => [], + 'usage' => [], + 'openRegistrations' => intval($config->get('config', 'register_policy')) !== Register::CLOSED, + 'metadata' => [ + 'nodeName' => $config->get('config', 'sitename'), + ], + ]; + + if (!empty($config->get('system', 'diaspora_enabled'))) { + $nodeinfo['protocols'][] = 'diaspora'; + } + + if (empty($config->get('system', 'ostatus_disabled'))) { + $nodeinfo['protocols'][] = 'ostatus'; + } + + $nodeinfo['usage'] = Nodeinfo::getUsage(); + + $nodeinfo['services'] = Nodeinfo::getServices(); + + if (Addon::isEnabled('twitter')) { + $nodeinfo['services']['inbound'][] = 'twitter'; + } + + $nodeinfo['services']['inbound'][] = 'atom1.0'; + $nodeinfo['services']['inbound'][] = 'rss2.0'; + $nodeinfo['services']['outbound'][] = 'atom1.0'; + + if ($imap) { + $nodeinfo['services']['inbound'][] = 'imap'; + } + + $nodeinfo['metadata']['explicitContent'] = $config->get('system', 'explicit_content', false) == true; + + header('Content-type: application/json; charset=utf-8'); + echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + exit; + } +} diff --git a/src/Module/NodeInfo210.php b/src/Module/NodeInfo210.php new file mode 100644 index 0000000000..63a1749832 --- /dev/null +++ b/src/Module/NodeInfo210.php @@ -0,0 +1,84 @@ +. + * + */ + +namespace Friendica\Module; + +use Friendica\BaseModule; +use Friendica\Core\Addon; +use Friendica\DI; +use Friendica\Model\Nodeinfo; + +/** + * Version 1.0 of Nodeinfo 2, a sStandardized way of exposing metadata about a server running one of the distributed social networks. + * @see https://github.com/jhass/nodeinfo/blob/master/PROTOCOL.md + */ +class NodeInfo210 extends BaseModule +{ + public static function rawContent(array $parameters = []) + { + $config = DI::config(); + + $imap = (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only')); + + $nodeinfo = [ + 'version' => '1.0', + 'server' => [ + 'baseUrl' => DI::baseUrl()->get(), + 'name' => $config->get('config', 'sitename'), + 'software' => 'friendica', + 'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION, + ], + 'organization' => Nodeinfo::getOrganization($config), + 'protocols' => ['dfrn', 'activitypub'], + 'services' => [], + 'openRegistrations' => intval($config->get('config', 'register_policy')) !== Register::CLOSED, + 'usage' => [], + ]; + + if (!empty($config->get('system', 'diaspora_enabled'))) { + $nodeinfo['protocols'][] = 'diaspora'; + } + + if (empty($config->get('system', 'ostatus_disabled'))) { + $nodeinfo['protocols'][] = 'ostatus'; + } + + $nodeinfo['usage'] = Nodeinfo::getUsage(true); + + $nodeinfo['services'] = Nodeinfo::getServices(); + + if (Addon::isEnabled('twitter')) { + $nodeinfo['services']['inbound'][] = 'twitter'; + } + + $nodeinfo['services']['inbound'][] = 'atom1.0'; + $nodeinfo['services']['inbound'][] = 'rss2.0'; + $nodeinfo['services']['outbound'][] = 'atom1.0'; + + if ($imap) { + $nodeinfo['services']['inbound'][] = 'imap'; + } + + header('Content-type: application/json; charset=utf-8'); + echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + exit; + } +} diff --git a/static/routes.config.php b/static/routes.config.php index cb569e01a0..ac78826939 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -37,7 +37,7 @@ return [ '/host-meta' => [Module\WellKnown\HostMeta::class, [R::GET]], '/nodeinfo' => [Module\WellKnown\NodeInfo::class, [R::GET]], '/webfinger' => [Module\Xrd::class, [R::GET]], - '/x-nodeinfo2' => [Module\NodeInfo::class, [R::GET]], + '/x-nodeinfo2' => [Module\NodeInfo210::class, [R::GET]], '/x-social-relay' => [Module\WellKnown\XSocialRelay::class, [R::GET]], ], @@ -200,7 +200,8 @@ return [ '/manifest' => [Module\Manifest::class, [R::GET]], '/modexp/{nick}' => [Module\PublicRSAKey::class, [R::GET]], '/newmember' => [Module\Welcome::class, [R::GET]], - '/nodeinfo/{version}' => [Module\NodeInfo::class, [R::GET]], + '/nodeinfo/1.0' => [Module\NodeInfo110::class, [R::GET]], + '/nodeinfo/2.0' => [Module\NodeInfo120::class, [R::GET]], '/nogroup' => [Module\Group::class, [R::GET]], '/noscrape' => [ From 5a6887fb2e4f3a78207547d1fe801403645c9a80 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 13 Jul 2020 13:26:09 +0000 Subject: [PATCH 008/573] Use "jsonexit" --- src/Core/System.php | 9 +++++---- src/Module/NodeInfo110.php | 5 ++--- src/Module/NodeInfo120.php | 5 ++--- src/Module/NodeInfo210.php | 5 ++--- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/Core/System.php b/src/Core/System.php index feed85e211..37e6d8a1cb 100644 --- a/src/Core/System.php +++ b/src/Core/System.php @@ -136,12 +136,13 @@ class System * and adds an application/json HTTP header to the output. * After finishing the process is getting killed. * - * @param mixed $x The input content. - * @param string $content_type Type of the input (Default: 'application/json'). + * @param mixed $x The input content. + * @param string $content_type Type of the input (Default: 'application/json'). + * @param integer $options JSON options */ - public static function jsonExit($x, $content_type = 'application/json') { + public static function jsonExit($x, $content_type = 'application/json', int $options = null) { header("Content-type: $content_type"); - echo json_encode($x); + echo json_encode($x, $options); exit(); } diff --git a/src/Module/NodeInfo110.php b/src/Module/NodeInfo110.php index 954f362190..79e215e4fb 100644 --- a/src/Module/NodeInfo110.php +++ b/src/Module/NodeInfo110.php @@ -23,6 +23,7 @@ namespace Friendica\Module; use Friendica\BaseModule; use Friendica\Core\Addon; +use Friendica\Core\System; use Friendica\DI; use Friendica\Model\Nodeinfo; @@ -85,8 +86,6 @@ class NodeInfo110 extends BaseModule $nodeinfo['metadata']['explicitContent'] = $config->get('system', 'explicit_content', false) == true; - header('Content-type: application/json; charset=utf-8'); - echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); - exit; + System::jsonExit($nodeinfo, 'application/json; charset=utf-8', JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); } } diff --git a/src/Module/NodeInfo120.php b/src/Module/NodeInfo120.php index 330c5e10d5..56054d7b67 100644 --- a/src/Module/NodeInfo120.php +++ b/src/Module/NodeInfo120.php @@ -23,6 +23,7 @@ namespace Friendica\Module; use Friendica\BaseModule; use Friendica\Core\Addon; +use Friendica\Core\System; use Friendica\DI; use Friendica\Model\Nodeinfo; @@ -79,8 +80,6 @@ class NodeInfo120 extends BaseModule $nodeinfo['metadata']['explicitContent'] = $config->get('system', 'explicit_content', false) == true; - header('Content-type: application/json; charset=utf-8'); - echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); - exit; + System::jsonExit($nodeinfo, 'application/json; charset=utf-8', JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); } } diff --git a/src/Module/NodeInfo210.php b/src/Module/NodeInfo210.php index 63a1749832..a95e37fa2e 100644 --- a/src/Module/NodeInfo210.php +++ b/src/Module/NodeInfo210.php @@ -23,6 +23,7 @@ namespace Friendica\Module; use Friendica\BaseModule; use Friendica\Core\Addon; +use Friendica\Core\System; use Friendica\DI; use Friendica\Model\Nodeinfo; @@ -77,8 +78,6 @@ class NodeInfo210 extends BaseModule $nodeinfo['services']['inbound'][] = 'imap'; } - header('Content-type: application/json; charset=utf-8'); - echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); - exit; + System::jsonExit($nodeinfo, 'application/json; charset=utf-8', JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); } } From baea99dee64b66a2062db5204bce69485835a951 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 13 Jul 2020 14:16:44 +0000 Subject: [PATCH 009/573] Unneeded variable removed --- src/Module/NodeInfo120.php | 4 +--- src/Module/NodeInfo210.php | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Module/NodeInfo120.php b/src/Module/NodeInfo120.php index 56054d7b67..9d02a4b54b 100644 --- a/src/Module/NodeInfo120.php +++ b/src/Module/NodeInfo120.php @@ -37,8 +37,6 @@ class NodeInfo120 extends BaseModule { $config = DI::config(); - $imap = (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only')); - $nodeinfo = [ 'version' => '2.0', 'software' => [ @@ -74,7 +72,7 @@ class NodeInfo120 extends BaseModule $nodeinfo['services']['inbound'][] = 'rss2.0'; $nodeinfo['services']['outbound'][] = 'atom1.0'; - if ($imap) { + if (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only')) { $nodeinfo['services']['inbound'][] = 'imap'; } diff --git a/src/Module/NodeInfo210.php b/src/Module/NodeInfo210.php index a95e37fa2e..8512f6d077 100644 --- a/src/Module/NodeInfo210.php +++ b/src/Module/NodeInfo210.php @@ -37,8 +37,6 @@ class NodeInfo210 extends BaseModule { $config = DI::config(); - $imap = (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only')); - $nodeinfo = [ 'version' => '1.0', 'server' => [ @@ -74,7 +72,7 @@ class NodeInfo210 extends BaseModule $nodeinfo['services']['inbound'][] = 'rss2.0'; $nodeinfo['services']['outbound'][] = 'atom1.0'; - if ($imap) { + if (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only')) { $nodeinfo['services']['inbound'][] = 'imap'; } From bbb2f1fcf53e6066aaae5e131bda445d63341fa4 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 13 Jul 2020 16:24:44 +0000 Subject: [PATCH 010/573] Use "0" as default --- src/Core/System.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/System.php b/src/Core/System.php index 37e6d8a1cb..67ee3a8038 100644 --- a/src/Core/System.php +++ b/src/Core/System.php @@ -140,7 +140,7 @@ class System * @param string $content_type Type of the input (Default: 'application/json'). * @param integer $options JSON options */ - public static function jsonExit($x, $content_type = 'application/json', int $options = null) { + public static function jsonExit($x, $content_type = 'application/json', int $options = 0) { header("Content-type: $content_type"); echo json_encode($x, $options); exit(); From 059087f99864d9790d965de712f18eb6f3b8d7fc Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 13 Jul 2020 17:22:21 -0400 Subject: [PATCH 011/573] Suppress all emails when Update::run is ran with $sendEmail = false - Address Renderer crash in the App-less context of Console --- src/Core/Update.php | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Core/Update.php b/src/Core/Update.php index 7a03b37695..827029d50e 100644 --- a/src/Core/Update.php +++ b/src/Core/Update.php @@ -127,7 +127,7 @@ class Update // run the pre_update_nnnn functions in update.php for ($x = $stored + 1; $x <= $current; $x++) { - $r = self::runUpdateFunction($x, 'pre_update'); + $r = self::runUpdateFunction($x, 'pre_update', $sendMail); if (!$r) { DI::config()->set('system', 'update', Update::FAILED); DI::lock()->release('dbupdate'); @@ -156,7 +156,7 @@ class Update // run the update_nnnn functions in update.php for ($x = $stored + 1; $x <= $current; $x++) { - $r = self::runUpdateFunction($x, 'update'); + $r = self::runUpdateFunction($x, 'update', $sendMail); if (!$r) { DI::config()->set('system', 'update', Update::FAILED); DI::lock()->release('dbupdate'); @@ -181,13 +181,14 @@ class Update /** * Executes a specific update function * - * @param int $x the DB version number of the function - * @param string $prefix the prefix of the function (update, pre_update) - * + * @param int $x the DB version number of the function + * @param string $prefix the prefix of the function (update, pre_update) + * @param bool $sendMail whether to send emails on success/failure + * @return bool true, if the update function worked * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function runUpdateFunction($x, $prefix) + public static function runUpdateFunction($x, $prefix, bool $sendMail = true) { $funcname = $prefix . '_' . $x; @@ -207,11 +208,13 @@ class Update $retval = $funcname(); if ($retval) { - //send the administrator an e-mail - self::updateFailed( - $x, - DI::l10n()->t('Update %s failed. See error logs.', $x) - ); + if ($sendMail) { + //send the administrator an e-mail + self::updateFailed( + $x, + DI::l10n()->t('Update %s failed. See error logs.', $x) + ); + } Logger::error('Update function ERROR.', ['function' => $funcname, 'retval' => $retval]); DI::lock()->release('dbupdate_function'); return false; From 291f11a8bbed6267fe6445da11d5190949451a79 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Tue, 14 Jul 2020 09:35:06 -0400 Subject: [PATCH 012/573] Fix critical bug in Crypto::unencapsulate - The direction of the sub-function has been corrected --- src/Util/Crypto.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Util/Crypto.php b/src/Util/Crypto.php index d44800e942..8adacf7104 100644 --- a/src/Util/Crypto.php +++ b/src/Util/Crypto.php @@ -461,11 +461,12 @@ class Crypto return; } - $alg = ((array_key_exists('alg', $data)) ? $data['alg'] : 'aes256cbc'); + $alg = $data['alg'] ?? 'aes256cbc'; if ($alg === 'aes256cbc') { - return self::encapsulateAes($data['data'], $prvkey); + return self::unencapsulateAes($data['data'], $prvkey); } - return self::encapsulateOther($data['data'], $prvkey, $alg); + + return self::unencapsulateOther($data, $prvkey, $alg); } /** From bf599be1f83b643dd20bb435e2d2f0f8aa7a4265 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Tue, 14 Jul 2020 09:48:04 -0400 Subject: [PATCH 013/573] Add support for token strings in HTTPSignature::parseSigheader - Only quoted strings were supported before --- src/Util/HTTPSignature.php | 99 +++++++++++++++----------------------- 1 file changed, 39 insertions(+), 60 deletions(-) diff --git a/src/Util/HTTPSignature.php b/src/Util/HTTPSignature.php index 8df4ecc414..d151516bea 100644 --- a/src/Util/HTTPSignature.php +++ b/src/Util/HTTPSignature.php @@ -191,8 +191,10 @@ class HTTPSignature /** * @param string $header - * @return array associate array with + * @return array associative array with * - \e string \b keyID + * - \e string \b created + * - \e string \b expires * - \e string \b algorithm * - \e array \b headers * - \e string \b signature @@ -200,78 +202,55 @@ class HTTPSignature */ public static function parseSigheader($header) { - $ret = []; + // Remove obsolete folds + $header = preg_replace('/\n\s+/', ' ', $header); + + $token = "[!#$%&'*+.^_`|~0-9A-Za-z-]"; + + $quotedString = '"(?:\\\\.|[^"\\\\])*"'; + + $regex = "/($token+)=($quotedString|$token+)/ism"; + $matches = []; + preg_match_all($regex, $header, $matches, PREG_SET_ORDER); + + $headers = []; + foreach ($matches as $match) { + $headers[$match[1]] = trim($match[2] ?: $match[3], '"'); + } // if the header is encrypted, decrypt with (default) site private key and continue - if (preg_match('/iv="(.*?)"/ism', $header, $matches)) { - $header = self::decryptSigheader($header); + if (!empty($headers['iv'])) { + $header = self::decryptSigheader($headers, DI::config()->get('system', 'prvkey')); + return self::parseSigheader($header); } - if (preg_match('/keyId="(.*?)"/ism', $header, $matches)) { - $ret['keyId'] = $matches[1]; + $return = [ + 'keyId' => $headers['keyId'] ?? '', + 'algorithm' => $headers['algorithm'] ?? 'rsa-sha256', + 'created' => $headers['created'] ?? null, + 'expires' => $headers['expires'] ?? null, + 'headers' => explode(' ', $headers['headers'] ?? ''), + 'signature' => base64_decode(preg_replace('/\s+/', '', $headers['signature'] ?? '')), + ]; + + if (!empty($return['signature']) && !empty($return['algorithm']) && empty($return['headers'])) { + $return['headers'] = ['date']; } - if (preg_match('/algorithm="(.*?)"/ism', $header, $matches)) { - $ret['algorithm'] = $matches[1]; - } else { - $ret['algorithm'] = 'rsa-sha256'; - } - - if (preg_match('/headers="(.*?)"/ism', $header, $matches)) { - $ret['headers'] = explode(' ', $matches[1]); - } - - if (preg_match('/signature="(.*?)"/ism', $header, $matches)) { - $ret['signature'] = base64_decode(preg_replace('/\s+/', '', $matches[1])); - } - - if (!empty($ret['signature']) && !empty($ret['algorithm']) && empty($ret['headers'])) { - $ret['headers'] = ['date']; - } - - return $ret; + return $return; } /** - * @param string $header - * @param string $prvkey (optional), if not set use site private key - * - * @return array|string associative array, empty string if failue - * - \e string \b iv - * - \e string \b key - * - \e string \b alg - * - \e string \b data + * @param array $headers Signature headers + * @param string $prvkey The site private key + * @return string Decrypted signature string * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - private static function decryptSigheader($header, $prvkey = null) + private static function decryptSigheader(array $headers, string $prvkey) { - $iv = $key = $alg = $data = null; - - if (!$prvkey) { - $prvkey = DI::config()->get('system', 'prvkey'); - } - - $matches = []; - - if (preg_match('/iv="(.*?)"/ism', $header, $matches)) { - $iv = $matches[1]; - } - - if (preg_match('/key="(.*?)"/ism', $header, $matches)) { - $key = $matches[1]; - } - - if (preg_match('/alg="(.*?)"/ism', $header, $matches)) { - $alg = $matches[1]; - } - - if (preg_match('/data="(.*?)"/ism', $header, $matches)) { - $data = $matches[1]; - } - - if ($iv && $key && $alg && $data) { - return Crypto::unencapsulate(['iv' => $iv, 'key' => $key, 'alg' => $alg, 'data' => $data], $prvkey); + if (!empty($headers['iv']) && !empty($headers['key']) && !empty($headers['data'])) { + return Crypto::unencapsulate($headers, $prvkey); } return ''; From a81e169f45866dfe572920e5a2285dbaae88c157 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Tue, 14 Jul 2020 09:48:34 -0400 Subject: [PATCH 014/573] Add unencrypted header test for HTTPSignature::parseSigheader --- tests/src/Util/HTTPSignatureTest.php | 55 ++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 tests/src/Util/HTTPSignatureTest.php diff --git a/tests/src/Util/HTTPSignatureTest.php b/tests/src/Util/HTTPSignatureTest.php new file mode 100644 index 0000000000..ba2f6ebbf2 --- /dev/null +++ b/tests/src/Util/HTTPSignatureTest.php @@ -0,0 +1,55 @@ +. + * + */ + +namespace Friendica\Test\src\Util; + +use Friendica\Util\HTTPSignature; +use PHPUnit\Framework\TestCase; + +/** + * HTTP Signature utility test class + */ +class HTTPSignatureTest extends TestCase +{ + public function testParseSigheader() + { + $header = 'keyId="test-key-a", algorithm="hs2019", + created=1402170695, + headers="(request-target) (created) host date content-type digest + content-length", + signature="KXUj1H3ZOhv3Nk4xlRLTn4bOMlMOmFiud3VXrMa9MaLCxnVmrqOX5B + ulRvB65YW/wQp0oT/nNQpXgOYeY8ovmHlpkRyz5buNDqoOpRsCpLGxsIJ9cX8 + XVsM9jy+Q1+RIlD9wfWoPHhqhoXt35ZkasuIDPF/AETuObs9QydlsqONwbK+T + dQguDK/8Va1Pocl6wK1uLwqcXlxhPEb55EmdYB9pddDyHTADING7K4qMwof2m + C3t8Pb0yoLZoZX5a4Or4FrCCKK/9BHAhq/RsVk0dTENMbTB4i7cHvKQu+o9xu + YWuxyvBa0Z6NdOb0di70cdrSDEsL5Gz7LBY5J2N9KdGg=="'; + + $headers = HTTPSignature::parseSigheader($header); + $this->assertSame([ + 'keyId' => 'test-key-a', + 'algorithm' => 'hs2019', + 'created' => '1402170695', + 'expires' => null, + 'headers' => ['(request-target)', '(created)', 'host', 'date', 'content-type', 'digest', 'content-length'], + 'signature' => base64_decode('KXUj1H3ZOhv3Nk4xlRLTn4bOMlMOmFiud3VXrMa9MaLCxnVmrqOX5BulRvB65YW/wQp0oT/nNQpXgOYeY8ovmHlpkRyz5buNDqoOpRsCpLGxsIJ9cX8XVsM9jy+Q1+RIlD9wfWoPHhqhoXt35ZkasuIDPF/AETuObs9QydlsqONwbK+TdQguDK/8Va1Pocl6wK1uLwqcXlxhPEb55EmdYB9pddDyHTADING7K4qMwof2mC3t8Pb0yoLZoZX5a4Or4FrCCKK/9BHAhq/RsVk0dTENMbTB4i7cHvKQu+o9xuYWuxyvBa0Z6NdOb0di70cdrSDEsL5Gz7LBY5J2N9KdGg=='), + ], $headers); + } +} From a1d62734fa3e6eb0f3af18c9bf38447ee0ef5d53 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Tue, 14 Jul 2020 10:14:05 -0400 Subject: [PATCH 015/573] Remove consume_feed in favor of Protocol\Feed::consume --- include/items.php | 9 --------- mod/pubsub.php | 5 +++-- src/Worker/OnePoll.php | 5 +++-- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/include/items.php b/include/items.php index 16fe897bed..ad87ecc313 100644 --- a/include/items.php +++ b/include/items.php @@ -63,12 +63,3 @@ function add_page_info_to_body($body, $texturl = false, $no_photos = false) { return \Friendica\Content\PageInfo::appendToBody($body, $texturl, $no_photos); } - -/** - * @deprecated since 2020.06 - * @see \Friendica\Protocol\Feed::consume - */ -function consume_feed($xml, array $importer, array $contact, &$hub) -{ - \Friendica\Protocol\Feed::consume($xml, $importer, $contact, $hub); -} diff --git a/mod/pubsub.php b/mod/pubsub.php index cae346493f..ece95dceab 100644 --- a/mod/pubsub.php +++ b/mod/pubsub.php @@ -25,6 +25,7 @@ use Friendica\Core\Protocol; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; +use Friendica\Protocol\Feed; use Friendica\Protocol\OStatus; use Friendica\Util\Strings; use Friendica\Util\Network; @@ -146,11 +147,11 @@ function pubsub_post(App $a) Logger::log('Import item for ' . $nick . ' from ' . $contact['nick'] . ' (' . $contact['id'] . ')'); $feedhub = ''; - consume_feed($xml, $importer, $contact, $feedhub); + Feed::consume($xml, $importer, $contact, $feedhub); // do it a second time for DFRN so that any children find their parents. if ($contact['network'] === Protocol::DFRN) { - consume_feed($xml, $importer, $contact, $feedhub); + Feed::consume($xml, $importer, $contact, $feedhub); } hub_post_return(); diff --git a/src/Worker/OnePoll.php b/src/Worker/OnePoll.php index fbe92215d1..a2659f4e76 100644 --- a/src/Worker/OnePoll.php +++ b/src/Worker/OnePoll.php @@ -31,6 +31,7 @@ use Friendica\Model\User; 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; @@ -173,11 +174,11 @@ class OnePoll Logger::log("Consume feed of contact ".$contact['id']); - consume_feed($xml, $importer, $contact, $hub); + Feed::consume($xml, $importer, $contact, $hub); // do it a second time for DFRN so that any children find their parents. if ($protocol === Protocol::DFRN) { - consume_feed($xml, $importer, $contact, $hub); + Feed::consume($xml, $importer, $contact, $hub); } $hubmode = 'subscribe'; From 3e25fc3a7297ea79d94c212d5ca6d3ccd6d9f9aa Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Tue, 14 Jul 2020 10:15:04 -0400 Subject: [PATCH 016/573] Replace *_page_info function calls with Content\PageInfo equivalent --- include/items.php | 45 -------------------------------------- mod/item.php | 3 ++- mod/parse_url.php | 3 ++- src/Module/Bookmarklet.php | 3 ++- src/Protocol/Diaspora.php | 5 +++-- src/Protocol/Feed.php | 7 +++--- src/Protocol/OStatus.php | 7 +++--- 7 files changed, 17 insertions(+), 56 deletions(-) diff --git a/include/items.php b/include/items.php index ad87ecc313..23b63ec114 100644 --- a/include/items.php +++ b/include/items.php @@ -18,48 +18,3 @@ * along with this program. If not, see . * */ - -/** - * @deprecated since 2020.06 - * @see \Friendica\Content\PageInfo::getFooterFromData - */ -function add_page_info_data(array $data, $no_photos = false) -{ - return "\n" . \Friendica\Content\PageInfo::getFooterFromData($data, $no_photos); -} - -/** - * @deprecated since 2020.06 - * @see \Friendica\Content\PageInfo::queryUrl - */ -function query_page_info($url, $photo = "", $keywords = false, $keyword_denylist = "") -{ - return \Friendica\Content\PageInfo::queryUrl($url, $photo, $keywords, $keyword_denylist); -} - -/** - * @deprecated since 2020.06 - * @see \Friendica\Content\PageInfo::getTagsFromUrl() - */ -function get_page_keywords($url, $photo = "", $keywords = false, $keyword_denylist = "") -{ - return $keywords ? \Friendica\Content\PageInfo::getTagsFromUrl($url, $photo, $keyword_denylist) : []; -} - -/** - * @deprecated since 2020.06 - * @see \Friendica\Content\PageInfo::getFooterFromUrl - */ -function add_page_info($url, $no_photos = false, $photo = "", $keywords = false, $keyword_denylist = "") -{ - return "\n" . \Friendica\Content\PageInfo::getFooterFromUrl($url, $no_photos, $photo, $keywords, $keyword_denylist); -} - -/** - * @deprecated since 2020.06 - * @see \Friendica\Content\PageInfo::appendToBody - */ -function add_page_info_to_body($body, $texturl = false, $no_photos = false) -{ - return \Friendica\Content\PageInfo::appendToBody($body, $texturl, $no_photos); -} diff --git a/mod/item.php b/mod/item.php index 6a3fd1896b..c30bbcc088 100644 --- a/mod/item.php +++ b/mod/item.php @@ -30,6 +30,7 @@ use Friendica\App; use Friendica\Content\Item as ItemHelper; +use Friendica\Content\PageInfo; use Friendica\Content\Text\BBCode; use Friendica\Core\Hook; use Friendica\Core\Logger; @@ -233,7 +234,7 @@ function item_post(App $a) { ]; } - $att_bbcode = add_page_info_data($attachment); + $att_bbcode = "\n" . PageInfo::getFooterFromData($attachment); $body .= $att_bbcode; } diff --git a/mod/parse_url.php b/mod/parse_url.php index b40ddf1d71..67610140b6 100644 --- a/mod/parse_url.php +++ b/mod/parse_url.php @@ -24,6 +24,7 @@ */ use Friendica\App; +use Friendica\Content\PageInfo; use Friendica\Core\Hook; use Friendica\Core\Logger; use Friendica\Core\System; @@ -177,7 +178,7 @@ function parse_url_content(App $a) } // Format it as BBCode attachment - $info = add_page_info_data($siteinfo); + $info = "\n" . PageInfo::getFooterFromData($siteinfo); echo $info; diff --git a/src/Module/Bookmarklet.php b/src/Module/Bookmarklet.php index 9ecce8ade1..e5b3ee4ade 100644 --- a/src/Module/Bookmarklet.php +++ b/src/Module/Bookmarklet.php @@ -22,6 +22,7 @@ namespace Friendica\Module; use Friendica\BaseModule; +use Friendica\Content\PageInfo; use Friendica\Core\ACL; use Friendica\DI; use Friendica\Module\Security\Login; @@ -55,7 +56,7 @@ class Bookmarklet extends BaseModule throw new HTTPException\BadRequestException(DI::l10n()->t('This page is missing a url parameter.')); } - $content = add_page_info($_REQUEST["url"]); + $content = "\n" . PageInfo::getFooterFromUrl($_REQUEST['url']); $x = [ 'is_owner' => true, diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index de8fa21777..25b0f66892 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -22,6 +22,7 @@ namespace Friendica\Protocol; use Friendica\Content\Feature; +use Friendica\Content\PageInfo; use Friendica\Content\Text\BBCode; use Friendica\Content\Text\Markdown; use Friendica\Core\Cache\Duration; @@ -2621,7 +2622,7 @@ class Diaspora $item["body"] = self::replacePeopleGuid($item["body"], $item["author-link"]); // Add OEmbed and other information to the body - $item["body"] = add_page_info_to_body($item["body"], false, true); + $item["body"] = PageInfo::appendToBody($item["body"], false, true); return $item; } else { @@ -2985,7 +2986,7 @@ class Diaspora // Add OEmbed and other information to the body if (!self::isHubzilla($contact["url"])) { - $body = add_page_info_to_body($body, false, true); + $body = PageInfo::appendToBody($body, false, true); } } diff --git a/src/Protocol/Feed.php b/src/Protocol/Feed.php index c3f6a4e0b7..6456132af7 100644 --- a/src/Protocol/Feed.php +++ b/src/Protocol/Feed.php @@ -23,6 +23,7 @@ namespace Friendica\Protocol; use DOMDocument; use DOMXPath; +use Friendica\Content\PageInfo; use Friendica\Content\Text\HTML; use Friendica\Core\Logger; use Friendica\Core\Protocol; @@ -532,8 +533,8 @@ class Feed // We always strip the title since it will be added in the page information $item["title"] = ""; - $item["body"] = $item["body"] . add_page_info($item["plink"], false, $preview, ($contact["fetch_further_information"] == 2), $contact["ffi_keyword_denylist"] ?? ''); - $taglist = get_page_keywords($item["plink"], $preview, ($contact["fetch_further_information"] == 2), $contact["ffi_keyword_denylist"]); + $item["body"] = $item["body"] . "\n" . PageInfo::getFooterFromUrl($item["plink"], false, $preview, ($contact["fetch_further_information"] == 2), $contact["ffi_keyword_denylist"] ?? ''); + $taglist = $contact["fetch_further_information"] == 2 ? PageInfo::getTagsFromUrl($item["plink"], $preview, $contact["ffi_keyword_denylist"]) : []; $item["object-type"] = Activity\ObjectType::BOOKMARK; unset($item["attach"]); } else { @@ -543,7 +544,7 @@ class Feed if (!empty($contact["fetch_further_information"]) && ($contact["fetch_further_information"] == 3)) { if (empty($taglist)) { - $taglist = get_page_keywords($item["plink"], $preview, true, $contact["ffi_keyword_denylist"]); + $taglist = PageInfo::getTagsFromUrl($item["plink"], $preview, $contact["ffi_keyword_denylist"]); } $item["body"] .= "\n" . self::tagToString($taglist); } else { diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index ef2515c1f5..90606af1c1 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -23,6 +23,7 @@ namespace Friendica\Protocol; use DOMDocument; use DOMXPath; +use Friendica\Content\PageInfo; use Friendica\Content\Text\BBCode; use Friendica\Content\Text\HTML; use Friendica\Core\Cache\Duration; @@ -697,7 +698,7 @@ class OStatus // Only add additional data when there is no picture in the post if (!strstr($item["body"], '[/img]')) { - $item["body"] = add_page_info_to_body($item["body"]); + $item["body"] = PageInfo::appendToBody($item["body"]); } Tag::storeFromBody($item['uri-id'], $item['body']); @@ -1120,7 +1121,7 @@ class OStatus if (($item["object-type"] == Activity\ObjectType::QUESTION) || ($item["object-type"] == Activity\ObjectType::EVENT) ) { - $item["body"] .= add_page_info($attribute['href']); + $item["body"] .= "\n" . PageInfo::getFooterFromUrl($attribute['href']); } break; case "ostatus:conversation": @@ -1153,7 +1154,7 @@ class OStatus } $link_data['related'] = $attribute['href']; } else { - $item["body"] .= add_page_info($attribute['href']); + $item["body"] .= "\n" . PageInfo::getFooterFromUrl($attribute['href']); } break; case "self": From a13e004df0d18c80fe8f6c84e3b7a1f179094005 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Tue, 14 Jul 2020 10:15:19 -0400 Subject: [PATCH 017/573] Delete include/items.php - Remove all references --- composer.json | 1 - doc/Addons.md | 4 ---- doc/Message-Flow.md | 2 -- doc/de/Addons.md | 4 ---- doc/de/Message-Flow.md | 2 -- include/items.php | 20 -------------------- mod/item.php | 2 -- src/Worker/Notifier.php | 2 -- 8 files changed, 37 deletions(-) delete mode 100644 include/items.php diff --git a/composer.json b/composer.json index 2cb863c980..b3dd0ec903 100644 --- a/composer.json +++ b/composer.json @@ -82,7 +82,6 @@ "include/conversation.php", "include/dba.php", "include/enotify.php", - "include/items.php", "boot.php" ] }, diff --git a/doc/Addons.md b/doc/Addons.md index bff707fa83..54363cb1d7 100644 --- a/doc/Addons.md +++ b/doc/Addons.md @@ -518,10 +518,6 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep- Hook::callAll('item_photo_menu', $args); Hook::callAll('jot_tool', $jotplugins); -### include/items.php - - Hook::callAll('page_info_data', $data); - ### mod/directory.php Hook::callAll('directory_item', $arr); diff --git a/doc/Message-Flow.md b/doc/Message-Flow.md index 69a10b2324..e967985695 100644 --- a/doc/Message-Flow.md +++ b/doc/Message-Flow.md @@ -6,8 +6,6 @@ There are multiple paths, using multiple protocols and message formats. Those attempting to understand these message flows should become familiar with (at the minimum) the [DFRN protocol document](https://github.com/friendica/friendica/blob/stable/spec/dfrn2.pdf) and the message passing elements of the OStatus stack (salmon and Pubsubhubbub). -Most message passing involves the file include/items.php, which has functions for several feed-related import/export activities. - When a message is posted, all immediate deliveries to all networks are made using include/notifier.php, which chooses how (and to whom) to deliver the message. This file also invokes the local side of all deliveries including DFRN-notify. diff --git a/doc/de/Addons.md b/doc/de/Addons.md index b54f011bfe..745010ff4e 100644 --- a/doc/de/Addons.md +++ b/doc/de/Addons.md @@ -226,10 +226,6 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap Hook::callAll('item_photo_menu', $args); Hook::callAll('jot_tool', $jotplugins); -### include/items.php - - Hook::callAll('page_info_data', $data); - ### mod/directory.php Hook::callAll('directory_item', $arr); diff --git a/doc/de/Message-Flow.md b/doc/de/Message-Flow.md index 0a78d69173..ef2a0a2715 100644 --- a/doc/de/Message-Flow.md +++ b/doc/de/Message-Flow.md @@ -8,8 +8,6 @@ Es gibt verschiedene Pfade, die verschiedene Protokolle und Nachrichtenformate n Diejenigen, die den Nachrichtenfluss genauer verstehen wollen, sollten sich mindestens mit dem DFRN-Protokoll ([Dokument mit den DFRN Spezifikationen](https://github.com/friendica/friendica/blob/stable/spec/dfrn2.pdf)) und den Elementen zur Nachrichtenverarbeitung des OStatus Stack informieren (salmon und Pubsubhubbub). -Der Großteil der Nachrichtenverarbeitung nutzt die Datei include/items.php, welche Funktionen für verschiedene Feed-bezogene Import-/Exportaktivitäten liefert. - Wenn eine Nachricht veröffentlicht wird, werden alle Übermittlungen an alle Netzwerke mit include/notifier.php durchgeführt, welche entscheidet, wie und an wen die Nachricht geliefert wird. Diese Datei bindet dabei die lokale Bearbeitung aller Übertragungen ein inkl. dfrn-notify. diff --git a/include/items.php b/include/items.php deleted file mode 100644 index 23b63ec114..0000000000 --- a/include/items.php +++ /dev/null @@ -1,20 +0,0 @@ -. - * - */ diff --git a/mod/item.php b/mod/item.php index c30bbcc088..e2d47ae2fa 100644 --- a/mod/item.php +++ b/mod/item.php @@ -58,8 +58,6 @@ use Friendica\Util\Security; use Friendica\Util\Strings; use Friendica\Worker\Delivery; -require_once __DIR__ . '/../include/items.php'; - function item_post(App $a) { if (!Session::isAuthenticated()) { throw new HTTPException\ForbiddenException(); diff --git a/src/Worker/Notifier.php b/src/Worker/Notifier.php index 8bcc0d3e35..c7b61dacca 100644 --- a/src/Worker/Notifier.php +++ b/src/Worker/Notifier.php @@ -42,8 +42,6 @@ use Friendica\Protocol\Diaspora; use Friendica\Protocol\OStatus; use Friendica\Protocol\Salmon; -require_once 'include/items.php'; - /* * The notifier is typically called with: * From 5ba8b4a58a77589c5ce12fbe9b00c42e67d20b14 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Tue, 14 Jul 2020 14:50:52 -0400 Subject: [PATCH 018/573] Add ffi_keyword_denylist key check to match previous call in Protocol\Feed --- src/Protocol/Feed.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Protocol/Feed.php b/src/Protocol/Feed.php index 6456132af7..86b76d309b 100644 --- a/src/Protocol/Feed.php +++ b/src/Protocol/Feed.php @@ -534,7 +534,7 @@ class Feed // We always strip the title since it will be added in the page information $item["title"] = ""; $item["body"] = $item["body"] . "\n" . PageInfo::getFooterFromUrl($item["plink"], false, $preview, ($contact["fetch_further_information"] == 2), $contact["ffi_keyword_denylist"] ?? ''); - $taglist = $contact["fetch_further_information"] == 2 ? PageInfo::getTagsFromUrl($item["plink"], $preview, $contact["ffi_keyword_denylist"]) : []; + $taglist = $contact["fetch_further_information"] == 2 ? PageInfo::getTagsFromUrl($item["plink"], $preview, $contact["ffi_keyword_denylist"] ?? '') : []; $item["object-type"] = Activity\ObjectType::BOOKMARK; unset($item["attach"]); } else { From d9c6a46ffee7f0544a8da195243f524623ddc107 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 15 Jul 2020 04:42:04 +0000 Subject: [PATCH 019/573] Replaced "getDetailsByURL" with "getByURL/getByURLForUser" --- mod/common.php | 4 +- mod/display.php | 2 +- mod/editpost.php | 2 +- mod/match.php | 2 +- mod/message.php | 4 +- mod/ping.php | 2 +- mod/suggest.php | 2 +- mod/unfollow.php | 2 +- src/Content/Item.php | 13 +- src/Content/Text/BBCode.php | 6 +- src/Content/Text/Markdown.php | 2 +- src/Core/Search.php | 6 +- src/Factory/Notification/Introduction.php | 4 +- src/Model/Contact.php | 243 +++------------------- src/Model/GContact.php | 2 +- src/Module/AllFriends.php | 4 +- src/Module/Contact.php | 4 +- src/Module/Contact/Advanced.php | 2 +- src/Module/Contact/Hovercard.php | 25 +-- src/Module/Contact/Poke.php | 2 +- src/Module/Profile/Contacts.php | 2 +- src/Module/Search/Acl.php | 2 +- src/Module/Search/Index.php | 6 +- src/Object/Post.php | 2 +- src/Protocol/ActivityPub/Processor.php | 4 +- src/Protocol/ActivityPub/Transmitter.php | 6 +- src/Protocol/DFRN.php | 2 +- src/Protocol/Diaspora.php | 4 +- src/Util/Profiler.php | 2 +- 29 files changed, 78 insertions(+), 285 deletions(-) diff --git a/mod/common.php b/mod/common.php index 0ccad42387..a54342ca0c 100644 --- a/mod/common.php +++ b/mod/common.php @@ -57,7 +57,7 @@ function common_content(App $a) if (DBA::isResult($contact)) { DI::page()['aside'] = ""; - Model\Profile::load($a, "", Model\Contact::getDetailsByURL($contact["url"])); + Model\Profile::load($a, "", Model\Contact::getByURLForUser($contact["url"], 0, [], false)); } } else { $contact = DBA::selectFirst('contact', ['name', 'url', 'photo', 'uid', 'id'], ['self' => true, 'uid' => $uid]); @@ -124,7 +124,7 @@ function common_content(App $a) $entries = []; foreach ($common_friends as $common_friend) { //get further details of the contact - $contact_details = Model\Contact::getDetailsByURL($common_friend['url'], $uid); + $contact_details = Model\Contact::getByURLForUser($common_friend['url'], $uid, [], false); // $rr['id'] is needed to use contact_photo_menu() /// @TODO Adding '/" here avoids E_NOTICE on missing constants diff --git a/mod/display.php b/mod/display.php index 02e838f55d..d630d94a04 100644 --- a/mod/display.php +++ b/mod/display.php @@ -164,7 +164,7 @@ function display_fetchauthor($a, $item) $profiledata["about"] = ""; } - $profiledata = Contact::getDetailsByURL($profiledata["url"], local_user(), $profiledata); + $profiledata = array_merge($profiledata, Contact::getByURLForUser($profiledata["url"], local_user(), [], false)); if (!empty($profiledata["photo"])) { $profiledata["photo"] = DI::baseUrl()->remove($profiledata["photo"]); diff --git a/mod/editpost.php b/mod/editpost.php index 7cccfdb2d4..8e7d3e7f53 100644 --- a/mod/editpost.php +++ b/mod/editpost.php @@ -145,7 +145,7 @@ function undo_post_tagging($s) { if ($cnt) { foreach ($matches as $mtch) { if (in_array($mtch[1], ['!', '@'])) { - $contact = Contact::getDetailsByURL($mtch[2]); + $contact = Contact::getByURL($mtch[2], 0, ['addr'], false); $mtch[3] = empty($contact['addr']) ? $mtch[2] : $contact['addr']; } $s = str_replace($mtch[0], $mtch[1] . $mtch[3],$s); diff --git a/mod/match.php b/mod/match.php index 47d9879794..f6dbe6aba7 100644 --- a/mod/match.php +++ b/mod/match.php @@ -102,7 +102,7 @@ function match_content(App $a) 'follow' => [DI::l10n()->t("Connect/Follow"), $connlnk] ]; - $contact_details = Contact::getDetailsByURL($profile->url, 0); + $contact_details = Contact::getByURL($profile->url, 0, [], false); $entry = [ 'url' => Contact::magicLink($profile->url), diff --git a/mod/message.php b/mod/message.php index c024cbe144..1a0fa1a482 100644 --- a/mod/message.php +++ b/mod/message.php @@ -396,7 +396,7 @@ function message_content(App $a) $body_e = BBCode::convert($message['body']); $to_name_e = $message['name']; - $contact = Contact::getDetailsByURL($message['from-url']); + $contact = Contact::getByURL($message['from-url'], 0, ['thumb', 'addr'], false); if (isset($contact["thumb"])) { $from_photo = $contact["thumb"]; } else { @@ -528,7 +528,7 @@ function render_messages(array $msg, $t) $body_e = $rr['body']; $to_name_e = $rr['name']; - $contact = Contact::getDetailsByURL($rr['url']); + $contact = Contact::getByURL($rr['url'], 0, ['thumb', 'addr'], false); if (isset($contact["thumb"])) { $from_photo = $contact["thumb"]; } else { diff --git a/mod/ping.php b/mod/ping.php index 6b3b015ac8..c64b045411 100644 --- a/mod/ping.php +++ b/mod/ping.php @@ -331,7 +331,7 @@ function ping_init(App $a) if (DBA::isResult($notifs)) { foreach ($notifs as $notif) { - $contact = Contact::getDetailsByURL($notif['url']); + $contact = Contact::getByURL($notif['url'], 0, ['micro'], false); if (isset($contact['micro'])) { $notif['photo'] = ProxyUtils::proxifyUrl($contact['micro'], false, ProxyUtils::SIZE_MICRO); } else { diff --git a/mod/suggest.php b/mod/suggest.php index 5fb9bdcff7..7db49c9ba5 100644 --- a/mod/suggest.php +++ b/mod/suggest.php @@ -104,7 +104,7 @@ function suggest_content(App $a) 'hide' => [DI::l10n()->t('Ignore/Hide'), $ignlnk] ]; - $contact_details = Contact::getDetailsByURL($rr["url"], local_user(), $rr); + $contact_details = array_merge($rr, Contact::getByURLForUser($rr["url"], local_user(), [], false)); $entry = [ 'url' => Contact::magicLink($rr['url']), diff --git a/mod/unfollow.php b/mod/unfollow.php index c754b384d3..370f13d876 100644 --- a/mod/unfollow.php +++ b/mod/unfollow.php @@ -146,7 +146,7 @@ function unfollow_content(App $a) ]); DI::page()['aside'] = ''; - Profile::load($a, '', Contact::getDetailsByURL($contact['url'])); + Profile::load($a, '', Contact::getByURL($contact['url'], 0, [], false)); $o .= Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), ['$title' => DI::l10n()->t('Status Messages and Posts')]); diff --git a/src/Content/Item.php b/src/Content/Item.php index f39a8fa19d..052670bc7f 100644 --- a/src/Content/Item.php +++ b/src/Content/Item.php @@ -130,7 +130,7 @@ class Item // Checking for the alias that is used for OStatus $pattern = '/[@!]\[url\=(.*?)\](.*?)\[\/url\]/ism'; if (preg_match($pattern, $tag, $matches)) { - $data = Contact::getDetailsByURL($matches[1]); + $data = Contact::getByURL($matches[1], 0, ['alias', 'nick'], false); if ($data['alias'] != '') { $newtag = '@[url=' . $data['alias'] . ']' . $data['nick'] . '[/url]'; @@ -149,15 +149,8 @@ class Item $name = $nameparts[0]; // Try to detect the contact in various ways - if (strpos($name, 'http://')) { - // At first we have to ensure that the contact exists - Contact::getIdForURL($name); - - // Now we should have something - $contact = Contact::getDetailsByURL($name, $profile_uid); - } elseif (strpos($name, '@')) { - // This function automatically probes when no entry was found - $contact = Contact::getDetailsByAddr($name, $profile_uid); + if (strpos($name, 'http://') || strpos($name, '@')) { + $contact = Contact::getByURLForUser($name, $profile_uid, []); } else { $contact = false; $fields = ['id', 'url', 'nick', 'name', 'alias', 'network', 'forum', 'prv']; diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index d1a50da209..d460fefd28 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -1975,11 +1975,7 @@ class BBCode */ private static function bbCodeMention2DiasporaCallback($match) { - $contact = Contact::getDetailsByURL($match[3]); - - if (empty($contact['addr'])) { - $contact = Probe::uri($match[3]); - } + $contact = Contact::getByURL($match[3], 0, ['addr']); if (empty($contact['addr'])) { return $match[0]; diff --git a/src/Content/Text/Markdown.php b/src/Content/Text/Markdown.php index cfd83a38d8..1b3f8ec710 100644 --- a/src/Content/Text/Markdown.php +++ b/src/Content/Text/Markdown.php @@ -83,7 +83,7 @@ class Markdown return ''; } - $data = Contact::getDetailsByAddr($matches[3]); + $data = Contact::getByURL($matches[3]); if (empty($data)) { return ''; diff --git a/src/Core/Search.php b/src/Core/Search.php index a1931fffc9..26531a1a30 100644 --- a/src/Core/Search.php +++ b/src/Core/Search.php @@ -77,7 +77,7 @@ class Search // Ensure that we do have a contact entry Contact::getIdForURL($user_data['url'] ?? ''); - $contactDetails = Contact::getDetailsByURL($user_data['url'] ?? '', local_user()); + $contactDetails = Contact::getByURLForUser($user_data['url'] ?? '', local_user(), [], false); $result = new ContactResult( $user_data['name'] ?? '', @@ -143,7 +143,7 @@ class Search foreach ($profiles as $profile) { $profile_url = $profile['url'] ?? ''; - $contactDetails = Contact::getDetailsByURL($profile_url, local_user()); + $contactDetails = Contact::getByURLForUser($profile_url, local_user(), [], false); $result = new ContactResult( $profile['name'] ?? '', @@ -232,7 +232,7 @@ class Search continue; } - $contact = Contact::getDetailsByURL($row["nurl"], local_user()); + $contact = Contact::getByURLForUser($row["nurl"], local_user(), [], false); if ($contact["name"] == "") { $contact["name"] = end(explode("/", $urlParts["path"])); diff --git a/src/Factory/Notification/Introduction.php b/src/Factory/Notification/Introduction.php index ddfb56948a..8e97498b3d 100644 --- a/src/Factory/Notification/Introduction.php +++ b/src/Factory/Notification/Introduction.php @@ -99,7 +99,7 @@ class Introduction extends BaseFactory $formattedNotifications = []; try { - /// @todo Fetch contact details by "Contact::getDetailsByUrl" instead of queries to contact, fcontact and gcontact + /// @todo Fetch contact details by "Contact::getByUrl" instead of queries to contact, fcontact and gcontact $stmtNotifications = $this->dba->p( "SELECT `intro`.`id` AS `intro_id`, `intro`.*, `contact`.*, `fcontact`.`name` AS `fname`, `fcontact`.`url` AS `furl`, `fcontact`.`addr` AS `faddr`, @@ -213,7 +213,7 @@ class Introduction extends BaseFactory // If the network and addr is still not available // get the missing data data from other sources if (empty($intro['gnetwork']) || empty($intro['gaddr'])) { - $ret = Contact::getDetailsByURL($intro['url']); + $ret = Contact::getByURL($intro['url'], 0, ['network', 'addr'], false); if (empty($intro['gnetwork']) && !empty($ret['network'])) { $intro['gnetwork'] = $ret['network']; diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 46104aaeae..3b6e0ac658 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -228,6 +228,37 @@ class Contact return $contact; } + /** + * Fetches a contact for a given user by a given url. + * In difference to "getByURL" the function will fetch a public contact when no user contact had been found. + * + * @param string $url profile url + * @param integer $uid User ID of the contact + * @param array $fields Field list + * @param boolean $update true = always update, false = never update, null = update when not found or outdated + * @return array contact array + */ + public static function getByURLForUser(string $url, int $uid = 0, array $fields = [], $update = null) + { + if ($uid != 0) { + $contact = self::getByURL($url, $uid, $fields, $update); + if (!empty($contact)) { + if (!empty($contact['id'])) { + $contact['cid'] = $contact['id']; + $contact['zid'] = 0; + } + return $contact; + } + } + + $contact = self::getByURL($url, 0, $fields, $update); + if (!empty($contact['id'])) { + $contact['cid'] = 0; + $contact['zid'] = $contact['id']; + } + return $contact; + } + /** * Tests if the given contact is a follower * @@ -1005,218 +1036,6 @@ class Contact GContact::updateFromPublicContactURL($contact['url']); } - /** - * Get contact data for a given profile link - * - * The function looks at several places (contact table and gcontact table) for the contact - * It caches its result for the same script execution to prevent duplicate calls - * - * @param string $url The profile link - * @param int $uid User id - * @param array $default If not data was found take this data as default value - * - * @return array Contact data - * @throws HTTPException\InternalServerErrorException - */ - public static function getDetailsByURL($url, $uid = -1, array $default = []) - { - static $cache = []; - - if ($url == '') { - return $default; - } - - if ($uid == -1) { - $uid = local_user(); - } - - if (isset($cache[$url][$uid])) { - return $cache[$url][$uid]; - } - - $ssl_url = str_replace('http://', 'https://', $url); - - $nurl = Strings::normaliseLink($url); - - // Fetch contact data from the contact table for the given user - $s = DBA::p("SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`, - `keywords`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`, `rel`, `pending` - FROM `contact` WHERE `nurl` = ? AND `uid` = ?", $nurl, $uid); - $r = DBA::toArray($s); - - // Fetch contact data from the contact table for the given user, checking with the alias - if (!DBA::isResult($r)) { - $s = DBA::p("SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`, - `keywords`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`, `rel`, `pending` - FROM `contact` WHERE `alias` IN (?, ?, ?) AND `uid` = ?", $nurl, $url, $ssl_url, $uid); - $r = DBA::toArray($s); - } - - // Fetch the data from the contact table with "uid=0" (which is filled automatically) - if (!DBA::isResult($r)) { - $s = DBA::p("SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`, - `keywords`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`, `rel`, `pending` - FROM `contact` WHERE `nurl` = ? AND `uid` = 0", $nurl); - $r = DBA::toArray($s); - } - - // Fetch the data from the contact table with "uid=0" (which is filled automatically) - checked with the alias - if (!DBA::isResult($r)) { - $s = DBA::p("SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`, - `keywords`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`, `rel`, `pending` - FROM `contact` WHERE `alias` IN (?, ?, ?) AND `uid` = 0", $nurl, $url, $ssl_url); - $r = DBA::toArray($s); - } - - // Fetch the data from the gcontact table - if (!DBA::isResult($r)) { - $s = DBA::p("SELECT 0 AS `id`, 0 AS `cid`, `id` AS `gid`, 0 AS `zid`, 0 AS `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, '' AS `xmpp`, - `keywords`, `photo`, `photo` AS `thumb`, `photo` AS `micro`, 0 AS `forum`, 0 AS `prv`, `community`, `contact-type`, `birthday`, 0 AS `self`, 2 AS `rel`, 0 AS `pending` - FROM `gcontact` WHERE `nurl` = ?", $nurl); - $r = DBA::toArray($s); - } - - if (DBA::isResult($r)) { - // If there is more than one entry we filter out the connector networks - if (count($r) > 1) { - foreach ($r as $id => $result) { - if (!in_array($result["network"], Protocol::NATIVE_SUPPORT)) { - unset($r[$id]); - } - } - } - - $profile = array_shift($r); - } - - if (!empty($profile)) { - $authoritativeResult = true; - // "bd" always contains the upcoming birthday of a contact. - // "birthday" might contain the birthday including the year of birth. - if ($profile["birthday"] > DBA::NULL_DATE) { - $bd_timestamp = strtotime($profile["birthday"]); - $month = date("m", $bd_timestamp); - $day = date("d", $bd_timestamp); - - $current_timestamp = time(); - $current_year = date("Y", $current_timestamp); - $current_month = date("m", $current_timestamp); - $current_day = date("d", $current_timestamp); - - $profile["bd"] = $current_year . "-" . $month . "-" . $day; - $current = $current_year . "-" . $current_month . "-" . $current_day; - - if ($profile["bd"] < $current) { - $profile["bd"] = ( ++$current_year) . "-" . $month . "-" . $day; - } - } else { - $profile["bd"] = DBA::NULL_DATE; - } - } else { - $authoritativeResult = false; - $profile = $default; - } - - if (empty($profile["photo"]) && isset($default["photo"])) { - $profile["photo"] = $default["photo"]; - } - - if (empty($profile["name"]) && isset($default["name"])) { - $profile["name"] = $default["name"]; - } - - if (empty($profile["network"]) && isset($default["network"])) { - $profile["network"] = $default["network"]; - } - - if (empty($profile["thumb"]) && isset($profile["photo"])) { - $profile["thumb"] = $profile["photo"]; - } - - if (empty($profile["micro"]) && isset($profile["thumb"])) { - $profile["micro"] = $profile["thumb"]; - } - - if ((empty($profile["addr"]) || empty($profile["name"])) && !empty($profile["gid"]) - && in_array($profile["network"], Protocol::FEDERATED) - ) { - Worker::add(PRIORITY_LOW, "UpdateGContact", $url); - } - - // Show contact details of Diaspora contacts only if connected - if (empty($profile["cid"]) && ($profile["network"] ?? "") == Protocol::DIASPORA) { - $profile["location"] = ""; - $profile["about"] = ""; - $profile["birthday"] = DBA::NULL_DATE; - } - - // Only cache the result if it came from the DB since this method is used in widely different contexts - // @see display_fetch_author for an example of $default parameter diverging from the DB result - if ($authoritativeResult) { - $cache[$url][$uid] = $profile; - } - - return $profile; - } - - /** - * Get contact data for a given address - * - * The function looks at several places (contact table and gcontact table) for the contact - * - * @param string $addr The profile link - * @param int $uid User id - * - * @return array Contact data - * @throws HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function getDetailsByAddr($addr, $uid = -1) - { - if ($addr == '') { - return []; - } - - if ($uid == -1) { - $uid = local_user(); - } - - // Fetch contact data from the contact table for the given user - $r = q("SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`, - `keywords`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`, `rel`, `pending`,`baseurl` - FROM `contact` WHERE `addr` = '%s' AND `uid` = %d AND NOT `deleted`", - DBA::escape($addr), - intval($uid) - ); - // Fetch the data from the contact table with "uid=0" (which is filled automatically) - if (!DBA::isResult($r)) { - $r = q("SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`, - `keywords`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`, `rel`, `pending`, `baseurl` - FROM `contact` WHERE `addr` = '%s' AND `uid` = 0 AND NOT `deleted`", - DBA::escape($addr) - ); - } - - // Fetch the data from the gcontact table - if (!DBA::isResult($r)) { - $r = q("SELECT 0 AS `id`, 0 AS `cid`, `id` AS `gid`, 0 AS `zid`, 0 AS `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, '' AS `xmpp`, - `keywords`, `photo`, `photo` AS `thumb`, `photo` AS `micro`, `community` AS `forum`, 0 AS `prv`, `community`, `contact-type`, `birthday`, 0 AS `self`, 2 AS `rel`, 0 AS `pending`, `server_url` AS `baseurl` - FROM `gcontact` WHERE `addr` = '%s'", - DBA::escape($addr) - ); - } - - if (!DBA::isResult($r)) { - $data = Probe::uri($addr); - - $profile = self::getDetailsByURL($data['url'], $uid, $data); - } else { - $profile = $r[0]; - } - - return $profile; - } - /** * Returns the data array for the photo menu of a given contact * diff --git a/src/Model/GContact.php b/src/Model/GContact.php index 912bd2c241..b876dacf4c 100644 --- a/src/Model/GContact.php +++ b/src/Model/GContact.php @@ -111,7 +111,7 @@ class GContact continue; } - $gcontacts[] = Contact::getDetailsByURL($result['nurl'], local_user()); + $gcontacts[] = Contact::getByURLForUser($result['nurl'], local_user(), [], false); } DBA::close($results); return $gcontacts; diff --git a/src/Module/AllFriends.php b/src/Module/AllFriends.php index 5d73f53cb5..5bade0c579 100644 --- a/src/Module/AllFriends.php +++ b/src/Module/AllFriends.php @@ -63,7 +63,7 @@ class AllFriends extends BaseModule } DI::page()['aside'] = ""; - Model\Profile::load($app, "", Model\Contact::getDetailsByURL($contact["url"])); + Model\Profile::load($app, "", Model\Contact::getByURL($contact["url"], 0, [], false)); $total = Model\GContact::countAllFriends(local_user(), $cid); @@ -79,7 +79,7 @@ class AllFriends extends BaseModule $entries = []; foreach ($friends as $friend) { //get further details of the contact - $contactDetails = Model\Contact::getDetailsByURL($friend['url'], $uid, $friend); + $contactDetails = array_merge($friend, Model\Contact::getByURLForUser($friend['url'], $uid, [], false)); $connlnk = ''; // $friend[cid] is only available for common contacts. So if the contact is a common one, use contact_photo_menu to generate the photoMenu diff --git a/src/Module/Contact.php b/src/Module/Contact.php index 9f34715404..fc54f1ea2a 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -971,7 +971,7 @@ class Contact extends BaseModule if (DBA::isResult($contact)) { DI::page()['aside'] = ''; - $profiledata = Model\Contact::getDetailsByURL($contact['url']); + $profiledata = Model\Contact::getByURL($contact['url'], 0, [], false); Model\Profile::load($a, '', $profiledata, true); @@ -994,7 +994,7 @@ class Contact extends BaseModule if (DBA::isResult($contact)) { DI::page()['aside'] = ''; - $profiledata = Model\Contact::getDetailsByURL($contact['url']); + $profiledata = Model\Contact::getByURL($contact['url'], 0, [], false); if (local_user() && in_array($profiledata['network'], Protocol::FEDERATED)) { $profiledata['remoteconnect'] = DI::baseUrl() . '/follow?url=' . urlencode($profiledata['url']); diff --git a/src/Module/Contact/Advanced.php b/src/Module/Contact/Advanced.php index cc9fdcf3d2..4ec122ce87 100644 --- a/src/Module/Contact/Advanced.php +++ b/src/Module/Contact/Advanced.php @@ -108,7 +108,7 @@ class Advanced extends BaseModule throw new BadRequestException(DI::l10n()->t('Contact not found.')); } - Model\Profile::load(DI::app(), "", Model\Contact::getDetailsByURL($contact["url"])); + Model\Profile::load(DI::app(), "", Model\Contact::getByURL($contact["url"], 0, [], false)); $warning = DI::l10n()->t('WARNING: This is highly advanced and if you enter incorrect information your communications with this contact may stop working.'); $info = DI::l10n()->t('Please use your browser \'Back\' button now if you are uncertain what to do on this page.'); diff --git a/src/Module/Contact/Hovercard.php b/src/Module/Contact/Hovercard.php index 4ef8162400..f0111b14d0 100644 --- a/src/Module/Contact/Hovercard.php +++ b/src/Module/Contact/Hovercard.php @@ -58,31 +58,16 @@ class Hovercard extends BaseModule $contact = []; // if it's the url containing https it should be converted to http - $contact_nurl = Strings::normaliseLink(GContact::cleanContactUrl($contact_url)); - if (!$contact_nurl) { + if (!$contact_url) { throw new HTTPException\BadRequestException(); } // Search for contact data // Look if the local user has got the contact if (Session::isAuthenticated()) { - $contact = Contact::getDetailsByURL($contact_nurl, local_user()); - } - - // If not then check the global user - if (!count($contact)) { - $contact = Contact::getDetailsByURL($contact_nurl); - } - - // Feeds url could have been destroyed through "cleanContactUrl", so we now use the original url - if (!count($contact) && Session::isAuthenticated()) { - $contact_nurl = Strings::normaliseLink($contact_url); - $contact = Contact::getDetailsByURL($contact_nurl, local_user()); - } - - if (!count($contact)) { - $contact_nurl = Strings::normaliseLink($contact_url); - $contact = Contact::getDetailsByURL($contact_nurl); + $contact = Contact::getByURLForUser($contact_url, local_user(), [], false); + } else { + $contact = Contact::getByURL($contact_url, 0, [], false); } if (!count($contact)) { @@ -110,7 +95,7 @@ class Hovercard extends BaseModule 'about' => $contact['about'], 'network_link' => Strings::formatNetworkName($contact['network'], $contact['url']), 'tags' => $contact['keywords'], - 'bd' => $contact['birthday'] <= DBA::NULL_DATE ? '' : $contact['birthday'], + 'bd' => $contact['bd'] <= DBA::NULL_DATE ? '' : $contact['bd'], 'account_type' => Contact::getAccountType($contact), 'actions' => $actions, ], diff --git a/src/Module/Contact/Poke.php b/src/Module/Contact/Poke.php index 9975ac1f28..0f289b5291 100644 --- a/src/Module/Contact/Poke.php +++ b/src/Module/Contact/Poke.php @@ -138,7 +138,7 @@ class Poke extends BaseModule throw new HTTPException\NotFoundException(); } - Model\Profile::load(DI::app(), '', Model\Contact::getDetailsByURL($contact["url"])); + Model\Profile::load(DI::app(), '', Model\Contact::getByURL($contact["url"], 0, [], false)); $verbs = []; foreach (DI::l10n()->getPokeVerbs() as $verb => $translations) { diff --git a/src/Module/Profile/Contacts.php b/src/Module/Profile/Contacts.php index 3a42b0d311..8c1129448d 100644 --- a/src/Module/Profile/Contacts.php +++ b/src/Module/Profile/Contacts.php @@ -103,7 +103,7 @@ class Contacts extends BaseProfile continue; } - $contact_details = Contact::getDetailsByURL($contact['url'], $a->profile['uid'], $contact); + $contact_details = array_merge($contact, Contact::getByURLForUser($contact['url'], $a->profile['uid'], [], false)); $contacts[] = [ 'id' => $contact['id'], diff --git a/src/Module/Search/Acl.php b/src/Module/Search/Acl.php index cc8df3eab2..e684d25e9b 100644 --- a/src/Module/Search/Acl.php +++ b/src/Module/Search/Acl.php @@ -350,7 +350,7 @@ class Acl extends BaseModule continue; } - $contact = Contact::getDetailsByURL($author); + $contact = Contact::getByURL($author, 0, ['micro', 'name', 'id', 'network', 'nick', 'addr', 'url', 'forum'], false); if (count($contact) > 0) { $unknown_contacts[] = [ diff --git a/src/Module/Search/Index.php b/src/Module/Search/Index.php index aca2934f65..1be3e3a796 100644 --- a/src/Module/Search/Index.php +++ b/src/Module/Search/Index.php @@ -237,13 +237,13 @@ class Index extends BaseSearch } else { // Cheaper local lookup for anonymous users, no probe if ($isAddr) { - $contact = Contact::selectFirst(['id' => 'cid'], ['addr' => $search, 'uid' => 0]); + $contact = Contact::selectFirst(['id'], ['addr' => $search, 'uid' => 0]); } else { - $contact = Contact::getDetailsByURL($search, 0, ['cid' => 0]); + $contact = array_merge(['id' => 0], Contact::getByURL($search, 0, ['id'])); } if (DBA::isResult($contact)) { - $contact_id = $contact['cid']; + $contact_id = $contact['id']; } } diff --git a/src/Object/Post.php b/src/Object/Post.php index 0a68bbbe2b..ba949dace3 100644 --- a/src/Object/Post.php +++ b/src/Object/Post.php @@ -877,7 +877,7 @@ class Post $terms = Tag::getByURIId($item['uri-id'], [Tag::MENTION, Tag::IMPLICIT_MENTION, Tag::EXCLUSIVE_MENTION]); foreach ($terms as $term) { - $profile = Contact::getDetailsByURL($term['url']); + $profile = Contact::getByURL($term['url'], 0, ['addr', 'contact-type'], false); if (!empty($profile['addr']) && ((($profile['contact-type'] ?? '') ?: Contact::TYPE_UNKNOWN) != Contact::TYPE_COMMUNITY) && ($profile['addr'] != $owner['addr']) && !strstr($text, $profile['addr'])) { $text .= '@' . $profile['addr'] . ' '; diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 0627c9ad34..6b7b7a3835 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -987,7 +987,7 @@ class Processor { $parent_terms = Tag::getByURIId($parent['uri-id'], [Tag::MENTION, Tag::IMPLICIT_MENTION, Tag::EXCLUSIVE_MENTION]); - $parent_author = Contact::getDetailsByURL($parent['author-link'], 0); + $parent_author = Contact::getByURL($parent['author-link'], 0, ['url', 'nurl', 'alias'], false); $implicit_mentions = []; if (empty($parent_author['url'])) { @@ -1003,7 +1003,7 @@ class Processor } foreach ($parent_terms as $term) { - $contact = Contact::getDetailsByURL($term['url'], 0); + $contact = Contact::getByURL($term['url'], 0, ['url', 'nurl', 'alias'], false); if (!empty($contact['url'])) { $implicit_mentions[] = $contact['url']; $implicit_mentions[] = $contact['nurl']; diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index 4bd3ccd4a4..5e60a2131e 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -1008,7 +1008,7 @@ class Transmitter $url = DI::baseUrl() . '/search?tag=' . urlencode($term['name']); $tags[] = ['type' => 'Hashtag', 'href' => $url, 'name' => '#' . $term['name']]; } else { - $contact = Contact::getDetailsByURL($term['url']); + $contact = Contact::getByURL($term['url'], 0, ['addr'], false); if (!empty($contact['addr'])) { $mention = '@' . $contact['addr']; } else { @@ -1141,7 +1141,7 @@ class Transmitter return ''; } - $data = Contact::getDetailsByURL($match[1]); + $data = Contact::getByURL($match[1], 0, ['url', 'nick'], false); if (empty($data['nick'])) { return $match[0]; } @@ -1861,7 +1861,7 @@ class Transmitter $mentions = []; foreach (Tag::getByURIId($uriid, [Tag::IMPLICIT_MENTION]) as $tag) { - $profile = Contact::getDetailsByURL($tag['url']); + $profile = Contact::getByURL($tag['url'], 0, ['addr', 'contact-type', 'nick'], false); if (!empty($profile['addr']) && $profile['contact-type'] != Contact::TYPE_COMMUNITY && !strstr($body, $profile['addr']) diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index 95780bec70..bf82e04278 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -755,7 +755,7 @@ class DFRN { $author = $doc->createElement($element); - $contact = Contact::getDetailsByURL($contact_url, $item["uid"]); + $contact = Contact::getByURLForUser($contact_url, $item["uid"], ['url', 'name', 'addr', 'photo']); if (!empty($contact)) { XML::addElement($doc, $author, "name", $contact["name"]); XML::addElement($doc, $author, "uri", $contact["url"]); diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index de8fa21777..ae2c0e23eb 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -1568,7 +1568,7 @@ class Diaspora */ private static function plink($addr, $guid, $parent_guid = '') { - $contact = Contact::getDetailsByAddr($addr); + $contact = Contact::getByURL($addr); if (empty($contact)) { Logger::info('No contact data for address', ['addr' => $addr]); return ''; @@ -3729,7 +3729,7 @@ class Diaspora private static function prependParentAuthorMention($body, $profile_url) { - $profile = Contact::getDetailsByURL($profile_url); + $profile = Contact::getByURL($profile_url, 0, ['addr', 'name', 'contact-type'], false); if (!empty($profile['addr']) && $profile['contact-type'] != Contact::TYPE_COMMUNITY && !strstr($body, $profile['addr']) diff --git a/src/Util/Profiler.php b/src/Util/Profiler.php index 240273bde3..1a5cd99922 100644 --- a/src/Util/Profiler.php +++ b/src/Util/Profiler.php @@ -177,7 +177,7 @@ class Profiler implements ContainerInterface $output .= "\nDatabase Read:\n"; foreach ($this->callstack["database"] as $func => $time) { $time = round($time, 3); - if ($time > 0) { + if ($time > 0.001) { $output .= $func . ": " . $time . "\n"; } } From fc0312451d3b8f8d10fc01701216fdd3a5139102 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 15 Jul 2020 17:06:48 +0000 Subject: [PATCH 020/573] Changed parameter order for getByURL --- mod/editpost.php | 2 +- mod/follow.php | 2 +- mod/match.php | 2 +- mod/message.php | 4 ++-- mod/ping.php | 2 +- mod/unfollow.php | 2 +- src/Content/Item.php | 2 +- src/Content/Text/BBCode.php | 6 +++--- src/Factory/Notification/Introduction.php | 2 +- src/Model/Contact.php | 8 ++++---- src/Module/AllFriends.php | 2 +- src/Module/Contact.php | 4 ++-- src/Module/Contact/Advanced.php | 2 +- src/Module/Contact/Hovercard.php | 2 +- src/Module/Contact/Poke.php | 2 +- src/Module/Search/Acl.php | 2 +- src/Module/Search/Index.php | 2 +- src/Object/Post.php | 2 +- src/Protocol/ActivityPub/Processor.php | 4 ++-- src/Protocol/ActivityPub/Transmitter.php | 6 +++--- src/Protocol/Diaspora.php | 2 +- src/Protocol/OStatus.php | 2 +- 22 files changed, 32 insertions(+), 32 deletions(-) diff --git a/mod/editpost.php b/mod/editpost.php index 8e7d3e7f53..8bde032933 100644 --- a/mod/editpost.php +++ b/mod/editpost.php @@ -145,7 +145,7 @@ function undo_post_tagging($s) { if ($cnt) { foreach ($matches as $mtch) { if (in_array($mtch[1], ['!', '@'])) { - $contact = Contact::getByURL($mtch[2], 0, ['addr'], false); + $contact = Contact::getByURL($mtch[2], false, ['addr']); $mtch[3] = empty($contact['addr']) ? $mtch[2] : $contact['addr']; } $s = str_replace($mtch[0], $mtch[1] . $mtch[3],$s); diff --git a/mod/follow.php b/mod/follow.php index 97bf9fcf9a..141fa9fdba 100644 --- a/mod/follow.php +++ b/mod/follow.php @@ -107,7 +107,7 @@ function follow_content(App $a) } } - $contact = Contact::getByURL($url, 0, [], true); + $contact = Contact::getByURL($url, true); if (empty($contact)) { // Possibly it is a remote item and not an account follow_remote_item($url); diff --git a/mod/match.php b/mod/match.php index f6dbe6aba7..b54be01347 100644 --- a/mod/match.php +++ b/mod/match.php @@ -102,7 +102,7 @@ function match_content(App $a) 'follow' => [DI::l10n()->t("Connect/Follow"), $connlnk] ]; - $contact_details = Contact::getByURL($profile->url, 0, [], false); + $contact_details = Contact::getByURL($profile->url, false); $entry = [ 'url' => Contact::magicLink($profile->url), diff --git a/mod/message.php b/mod/message.php index 1a0fa1a482..438f96030b 100644 --- a/mod/message.php +++ b/mod/message.php @@ -396,7 +396,7 @@ function message_content(App $a) $body_e = BBCode::convert($message['body']); $to_name_e = $message['name']; - $contact = Contact::getByURL($message['from-url'], 0, ['thumb', 'addr'], false); + $contact = Contact::getByURL($message['from-url'], false, ['thumb', 'addr']); if (isset($contact["thumb"])) { $from_photo = $contact["thumb"]; } else { @@ -528,7 +528,7 @@ function render_messages(array $msg, $t) $body_e = $rr['body']; $to_name_e = $rr['name']; - $contact = Contact::getByURL($rr['url'], 0, ['thumb', 'addr'], false); + $contact = Contact::getByURL($rr['url'], false, ['thumb', 'addr']); if (isset($contact["thumb"])) { $from_photo = $contact["thumb"]; } else { diff --git a/mod/ping.php b/mod/ping.php index c64b045411..d1983e8036 100644 --- a/mod/ping.php +++ b/mod/ping.php @@ -331,7 +331,7 @@ function ping_init(App $a) if (DBA::isResult($notifs)) { foreach ($notifs as $notif) { - $contact = Contact::getByURL($notif['url'], 0, ['micro'], false); + $contact = Contact::getByURL($notif['url'], false, ['micro']); if (isset($contact['micro'])) { $notif['photo'] = ProxyUtils::proxifyUrl($contact['micro'], false, ProxyUtils::SIZE_MICRO); } else { diff --git a/mod/unfollow.php b/mod/unfollow.php index 370f13d876..09466ba802 100644 --- a/mod/unfollow.php +++ b/mod/unfollow.php @@ -146,7 +146,7 @@ function unfollow_content(App $a) ]); DI::page()['aside'] = ''; - Profile::load($a, '', Contact::getByURL($contact['url'], 0, [], false)); + Profile::load($a, '', Contact::getByURL($contact['url'], false)); $o .= Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), ['$title' => DI::l10n()->t('Status Messages and Posts')]); diff --git a/src/Content/Item.php b/src/Content/Item.php index 052670bc7f..d93b0c1b6d 100644 --- a/src/Content/Item.php +++ b/src/Content/Item.php @@ -130,7 +130,7 @@ class Item // Checking for the alias that is used for OStatus $pattern = '/[@!]\[url\=(.*?)\](.*?)\[\/url\]/ism'; if (preg_match($pattern, $tag, $matches)) { - $data = Contact::getByURL($matches[1], 0, ['alias', 'nick'], false); + $data = Contact::getByURL($matches[1], false, ['alias', 'nick']); if ($data['alias'] != '') { $newtag = '@[url=' . $data['alias'] . ']' . $data['nick'] . '[/url]'; diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index d460fefd28..ce34d58ac5 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -983,7 +983,7 @@ class BBCode $attributes[$field] = html_entity_decode($matches[2] ?? '', ENT_QUOTES, 'UTF-8'); } - $author_contact = Contact::getByURL($attributes['profile'], 0, ['url', 'addr', 'name', 'micro'], false); + $author_contact = Contact::getByURL($attributes['profile'], false, ['url', 'addr', 'name', 'micro']); $author_contact['url'] = ($author_contact['url'] ?? $attributes['profile']); $author_contact['addr'] = ($author_contact['addr'] ?? '') ?: Protocol::getAddrFromProfileUrl($attributes['profile']); @@ -1061,7 +1061,7 @@ class BBCode default: $text = ($is_quote_share? "\n" : ''); - $contact = Contact::getByURL($attributes['profile'], 0, ['network'], false); + $contact = Contact::getByURL($attributes['profile'], false, ['network']); $network = $contact['network'] ?? Protocol::PHANTOM; $tpl = Renderer::getMarkupTemplate('shared_content.tpl'); @@ -1975,7 +1975,7 @@ class BBCode */ private static function bbCodeMention2DiasporaCallback($match) { - $contact = Contact::getByURL($match[3], 0, ['addr']); + $contact = Contact::getByURL($match[3], null, ['addr']); if (empty($contact['addr'])) { return $match[0]; diff --git a/src/Factory/Notification/Introduction.php b/src/Factory/Notification/Introduction.php index 8e97498b3d..21aef9297a 100644 --- a/src/Factory/Notification/Introduction.php +++ b/src/Factory/Notification/Introduction.php @@ -213,7 +213,7 @@ class Introduction extends BaseFactory // If the network and addr is still not available // get the missing data data from other sources if (empty($intro['gnetwork']) || empty($intro['gaddr'])) { - $ret = Contact::getByURL($intro['url'], 0, ['network', 'addr'], false); + $ret = Contact::getByURL($intro['url'], false, ['network', 'addr']); if (empty($intro['gnetwork']) && !empty($ret['network'])) { $intro['gnetwork'] = $ret['network']; diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 3b6e0ac658..7c8dec3895 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -199,7 +199,7 @@ class Contact * @param boolean $update true = always update, false = never update, null = update when not found or outdated * @return array contact array */ - public static function getByURL(string $url, int $uid = 0, array $fields = [], $update = null) + public static function getByURL(string $url, $update = null, array $fields = [], int $uid = 0) { if ($update || is_null($update)) { $cid = self::getIdForURL($url, $uid, !($update ?? false)); @@ -241,7 +241,7 @@ class Contact public static function getByURLForUser(string $url, int $uid = 0, array $fields = [], $update = null) { if ($uid != 0) { - $contact = self::getByURL($url, $uid, $fields, $update); + $contact = self::getByURL($url, $update, $fields, $uid); if (!empty($contact)) { if (!empty($contact['id'])) { $contact['cid'] = $contact['id']; @@ -251,7 +251,7 @@ class Contact } } - $contact = self::getByURL($url, 0, $fields, $update); + $contact = self::getByURL($url, $update, $fields); if (!empty($contact['id'])) { $contact['cid'] = 0; $contact['zid'] = $contact['id']; @@ -1318,7 +1318,7 @@ class Contact return 0; } - $contact = self::getByURL($url, $uid, ['id', 'avatar', 'updated', 'network'], false); + $contact = self::getByURL($url, false, ['id', 'avatar', 'updated', 'network'], $uid); if (!empty($contact)) { $contact_id = $contact["id"]; diff --git a/src/Module/AllFriends.php b/src/Module/AllFriends.php index 5bade0c579..3f5cf7c839 100644 --- a/src/Module/AllFriends.php +++ b/src/Module/AllFriends.php @@ -63,7 +63,7 @@ class AllFriends extends BaseModule } DI::page()['aside'] = ""; - Model\Profile::load($app, "", Model\Contact::getByURL($contact["url"], 0, [], false)); + Model\Profile::load($app, "", Model\Contact::getByURL($contact["url"], false)); $total = Model\GContact::countAllFriends(local_user(), $cid); diff --git a/src/Module/Contact.php b/src/Module/Contact.php index fc54f1ea2a..f63d42c0ea 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -971,7 +971,7 @@ class Contact extends BaseModule if (DBA::isResult($contact)) { DI::page()['aside'] = ''; - $profiledata = Model\Contact::getByURL($contact['url'], 0, [], false); + $profiledata = Model\Contact::getByURL($contact['url'], false); Model\Profile::load($a, '', $profiledata, true); @@ -994,7 +994,7 @@ class Contact extends BaseModule if (DBA::isResult($contact)) { DI::page()['aside'] = ''; - $profiledata = Model\Contact::getByURL($contact['url'], 0, [], false); + $profiledata = Model\Contact::getByURL($contact['url'], false); if (local_user() && in_array($profiledata['network'], Protocol::FEDERATED)) { $profiledata['remoteconnect'] = DI::baseUrl() . '/follow?url=' . urlencode($profiledata['url']); diff --git a/src/Module/Contact/Advanced.php b/src/Module/Contact/Advanced.php index 4ec122ce87..d29d0609a9 100644 --- a/src/Module/Contact/Advanced.php +++ b/src/Module/Contact/Advanced.php @@ -108,7 +108,7 @@ class Advanced extends BaseModule throw new BadRequestException(DI::l10n()->t('Contact not found.')); } - Model\Profile::load(DI::app(), "", Model\Contact::getByURL($contact["url"], 0, [], false)); + Model\Profile::load(DI::app(), "", Model\Contact::getByURL($contact["url"], false)); $warning = DI::l10n()->t('WARNING: This is highly advanced and if you enter incorrect information your communications with this contact may stop working.'); $info = DI::l10n()->t('Please use your browser \'Back\' button now if you are uncertain what to do on this page.'); diff --git a/src/Module/Contact/Hovercard.php b/src/Module/Contact/Hovercard.php index f0111b14d0..655fc9e2d5 100644 --- a/src/Module/Contact/Hovercard.php +++ b/src/Module/Contact/Hovercard.php @@ -67,7 +67,7 @@ class Hovercard extends BaseModule if (Session::isAuthenticated()) { $contact = Contact::getByURLForUser($contact_url, local_user(), [], false); } else { - $contact = Contact::getByURL($contact_url, 0, [], false); + $contact = Contact::getByURL($contact_url, false); } if (!count($contact)) { diff --git a/src/Module/Contact/Poke.php b/src/Module/Contact/Poke.php index 0f289b5291..b4adff46d3 100644 --- a/src/Module/Contact/Poke.php +++ b/src/Module/Contact/Poke.php @@ -138,7 +138,7 @@ class Poke extends BaseModule throw new HTTPException\NotFoundException(); } - Model\Profile::load(DI::app(), '', Model\Contact::getByURL($contact["url"], 0, [], false)); + Model\Profile::load(DI::app(), '', Model\Contact::getByURL($contact["url"], false)); $verbs = []; foreach (DI::l10n()->getPokeVerbs() as $verb => $translations) { diff --git a/src/Module/Search/Acl.php b/src/Module/Search/Acl.php index e684d25e9b..8a5c9faf58 100644 --- a/src/Module/Search/Acl.php +++ b/src/Module/Search/Acl.php @@ -350,7 +350,7 @@ class Acl extends BaseModule continue; } - $contact = Contact::getByURL($author, 0, ['micro', 'name', 'id', 'network', 'nick', 'addr', 'url', 'forum'], false); + $contact = Contact::getByURL($author, false, ['micro', 'name', 'id', 'network', 'nick', 'addr', 'url', 'forum']); if (count($contact) > 0) { $unknown_contacts[] = [ diff --git a/src/Module/Search/Index.php b/src/Module/Search/Index.php index 1be3e3a796..34085e339e 100644 --- a/src/Module/Search/Index.php +++ b/src/Module/Search/Index.php @@ -239,7 +239,7 @@ class Index extends BaseSearch if ($isAddr) { $contact = Contact::selectFirst(['id'], ['addr' => $search, 'uid' => 0]); } else { - $contact = array_merge(['id' => 0], Contact::getByURL($search, 0, ['id'])); + $contact = array_merge(['id' => 0], Contact::getByURL($search, null, ['id'])); } if (DBA::isResult($contact)) { diff --git a/src/Object/Post.php b/src/Object/Post.php index ba949dace3..ee886c1576 100644 --- a/src/Object/Post.php +++ b/src/Object/Post.php @@ -877,7 +877,7 @@ class Post $terms = Tag::getByURIId($item['uri-id'], [Tag::MENTION, Tag::IMPLICIT_MENTION, Tag::EXCLUSIVE_MENTION]); foreach ($terms as $term) { - $profile = Contact::getByURL($term['url'], 0, ['addr', 'contact-type'], false); + $profile = Contact::getByURL($term['url'], false, ['addr', 'contact-type']); if (!empty($profile['addr']) && ((($profile['contact-type'] ?? '') ?: Contact::TYPE_UNKNOWN) != Contact::TYPE_COMMUNITY) && ($profile['addr'] != $owner['addr']) && !strstr($text, $profile['addr'])) { $text .= '@' . $profile['addr'] . ' '; diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 6b7b7a3835..0e4aca4a5a 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -987,7 +987,7 @@ class Processor { $parent_terms = Tag::getByURIId($parent['uri-id'], [Tag::MENTION, Tag::IMPLICIT_MENTION, Tag::EXCLUSIVE_MENTION]); - $parent_author = Contact::getByURL($parent['author-link'], 0, ['url', 'nurl', 'alias'], false); + $parent_author = Contact::getByURL($parent['author-link'], false, ['url', 'nurl', 'alias']); $implicit_mentions = []; if (empty($parent_author['url'])) { @@ -1003,7 +1003,7 @@ class Processor } foreach ($parent_terms as $term) { - $contact = Contact::getByURL($term['url'], 0, ['url', 'nurl', 'alias'], false); + $contact = Contact::getByURL($term['url'], false, ['url', 'nurl', 'alias']); if (!empty($contact['url'])) { $implicit_mentions[] = $contact['url']; $implicit_mentions[] = $contact['nurl']; diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index 5e60a2131e..bd6f67128f 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -1008,7 +1008,7 @@ class Transmitter $url = DI::baseUrl() . '/search?tag=' . urlencode($term['name']); $tags[] = ['type' => 'Hashtag', 'href' => $url, 'name' => '#' . $term['name']]; } else { - $contact = Contact::getByURL($term['url'], 0, ['addr'], false); + $contact = Contact::getByURL($term['url'], false, ['addr']); if (!empty($contact['addr'])) { $mention = '@' . $contact['addr']; } else { @@ -1141,7 +1141,7 @@ class Transmitter return ''; } - $data = Contact::getByURL($match[1], 0, ['url', 'nick'], false); + $data = Contact::getByURL($match[1], false, ['url', 'nick']); if (empty($data['nick'])) { return $match[0]; } @@ -1861,7 +1861,7 @@ class Transmitter $mentions = []; foreach (Tag::getByURIId($uriid, [Tag::IMPLICIT_MENTION]) as $tag) { - $profile = Contact::getByURL($tag['url'], 0, ['addr', 'contact-type', 'nick'], false); + $profile = Contact::getByURL($tag['url'], false, ['addr', 'contact-type', 'nick']); if (!empty($profile['addr']) && $profile['contact-type'] != Contact::TYPE_COMMUNITY && !strstr($body, $profile['addr']) diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index ae2c0e23eb..c9da351f75 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -3729,7 +3729,7 @@ class Diaspora private static function prependParentAuthorMention($body, $profile_url) { - $profile = Contact::getByURL($profile_url, 0, ['addr', 'name', 'contact-type'], false); + $profile = Contact::getByURL($profile_url, false, ['addr', 'name', 'contact-type']); if (!empty($profile['addr']) && $profile['contact-type'] != Contact::TYPE_COMMUNITY && !strstr($body, $profile['addr']) diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index ef2515c1f5..0b39c3deee 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -2105,7 +2105,7 @@ class OStatus $mentioned = $newmentions; foreach ($mentioned as $mention) { - $contact = Contact::getByURL($mention, 0, ['contact-type']); + $contact = Contact::getByURL($mention, ['contact-type']); if (!empty($contact) && ($contact['contact-type'] == Contact::TYPE_COMMUNITY)) { XML::addElement($doc, $entry, "link", "", [ From e374aecc46c74f9c499a9abddf9d111fdd0b6984 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 15 Jul 2020 17:22:12 +0000 Subject: [PATCH 021/573] Changed parameter order for "getbyURLForUser" --- mod/common.php | 4 ++-- mod/display.php | 2 +- mod/suggest.php | 2 +- src/Content/Item.php | 2 +- src/Core/Search.php | 6 +++--- src/Model/Contact.php | 2 +- src/Model/GContact.php | 2 +- src/Module/AllFriends.php | 2 +- src/Module/Contact/Hovercard.php | 2 +- src/Module/Profile/Contacts.php | 2 +- src/Protocol/DFRN.php | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/mod/common.php b/mod/common.php index a54342ca0c..9b327f1bf8 100644 --- a/mod/common.php +++ b/mod/common.php @@ -57,7 +57,7 @@ function common_content(App $a) if (DBA::isResult($contact)) { DI::page()['aside'] = ""; - Model\Profile::load($a, "", Model\Contact::getByURLForUser($contact["url"], 0, [], false)); + Model\Profile::load($a, "", Model\Contact::getByURL($contact["url"], false)); } } else { $contact = DBA::selectFirst('contact', ['name', 'url', 'photo', 'uid', 'id'], ['self' => true, 'uid' => $uid]); @@ -124,7 +124,7 @@ function common_content(App $a) $entries = []; foreach ($common_friends as $common_friend) { //get further details of the contact - $contact_details = Model\Contact::getByURLForUser($common_friend['url'], $uid, [], false); + $contact_details = Model\Contact::getByURLForUser($common_friend['url'], $uid, false); // $rr['id'] is needed to use contact_photo_menu() /// @TODO Adding '/" here avoids E_NOTICE on missing constants diff --git a/mod/display.php b/mod/display.php index d630d94a04..ddc78d6eae 100644 --- a/mod/display.php +++ b/mod/display.php @@ -164,7 +164,7 @@ function display_fetchauthor($a, $item) $profiledata["about"] = ""; } - $profiledata = array_merge($profiledata, Contact::getByURLForUser($profiledata["url"], local_user(), [], false)); + $profiledata = array_merge($profiledata, Contact::getByURLForUser($profiledata["url"], local_user(), false)); if (!empty($profiledata["photo"])) { $profiledata["photo"] = DI::baseUrl()->remove($profiledata["photo"]); diff --git a/mod/suggest.php b/mod/suggest.php index 7db49c9ba5..2b7b1f6743 100644 --- a/mod/suggest.php +++ b/mod/suggest.php @@ -104,7 +104,7 @@ function suggest_content(App $a) 'hide' => [DI::l10n()->t('Ignore/Hide'), $ignlnk] ]; - $contact_details = array_merge($rr, Contact::getByURLForUser($rr["url"], local_user(), [], false)); + $contact_details = array_merge($rr, Contact::getByURLForUser($rr["url"], local_user(), false)); $entry = [ 'url' => Contact::magicLink($rr['url']), diff --git a/src/Content/Item.php b/src/Content/Item.php index d93b0c1b6d..c0b1d3b49a 100644 --- a/src/Content/Item.php +++ b/src/Content/Item.php @@ -150,7 +150,7 @@ class Item // Try to detect the contact in various ways if (strpos($name, 'http://') || strpos($name, '@')) { - $contact = Contact::getByURLForUser($name, $profile_uid, []); + $contact = Contact::getByURLForUser($name, $profile_uid); } else { $contact = false; $fields = ['id', 'url', 'nick', 'name', 'alias', 'network', 'forum', 'prv']; diff --git a/src/Core/Search.php b/src/Core/Search.php index 26531a1a30..208633272b 100644 --- a/src/Core/Search.php +++ b/src/Core/Search.php @@ -77,7 +77,7 @@ class Search // Ensure that we do have a contact entry Contact::getIdForURL($user_data['url'] ?? ''); - $contactDetails = Contact::getByURLForUser($user_data['url'] ?? '', local_user(), [], false); + $contactDetails = Contact::getByURLForUser($user_data['url'] ?? '', local_user(), false); $result = new ContactResult( $user_data['name'] ?? '', @@ -143,7 +143,7 @@ class Search foreach ($profiles as $profile) { $profile_url = $profile['url'] ?? ''; - $contactDetails = Contact::getByURLForUser($profile_url, local_user(), [], false); + $contactDetails = Contact::getByURLForUser($profile_url, local_user(), false); $result = new ContactResult( $profile['name'] ?? '', @@ -232,7 +232,7 @@ class Search continue; } - $contact = Contact::getByURLForUser($row["nurl"], local_user(), [], false); + $contact = Contact::getByURLForUser($row["nurl"], local_user(), false); if ($contact["name"] == "") { $contact["name"] = end(explode("/", $urlParts["path"])); diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 7c8dec3895..afec920070 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -238,7 +238,7 @@ class Contact * @param boolean $update true = always update, false = never update, null = update when not found or outdated * @return array contact array */ - public static function getByURLForUser(string $url, int $uid = 0, array $fields = [], $update = null) + public static function getByURLForUser(string $url, int $uid = 0, $update = null, array $fields = []) { if ($uid != 0) { $contact = self::getByURL($url, $update, $fields, $uid); diff --git a/src/Model/GContact.php b/src/Model/GContact.php index b876dacf4c..e8cd4768a6 100644 --- a/src/Model/GContact.php +++ b/src/Model/GContact.php @@ -111,7 +111,7 @@ class GContact continue; } - $gcontacts[] = Contact::getByURLForUser($result['nurl'], local_user(), [], false); + $gcontacts[] = Contact::getByURLForUser($result['nurl'], local_user(), false); } DBA::close($results); return $gcontacts; diff --git a/src/Module/AllFriends.php b/src/Module/AllFriends.php index 3f5cf7c839..d2d2b3b0cf 100644 --- a/src/Module/AllFriends.php +++ b/src/Module/AllFriends.php @@ -79,7 +79,7 @@ class AllFriends extends BaseModule $entries = []; foreach ($friends as $friend) { //get further details of the contact - $contactDetails = array_merge($friend, Model\Contact::getByURLForUser($friend['url'], $uid, [], false)); + $contactDetails = array_merge($friend, Model\Contact::getByURLForUser($friend['url'], $uid, false)); $connlnk = ''; // $friend[cid] is only available for common contacts. So if the contact is a common one, use contact_photo_menu to generate the photoMenu diff --git a/src/Module/Contact/Hovercard.php b/src/Module/Contact/Hovercard.php index 655fc9e2d5..29329b2296 100644 --- a/src/Module/Contact/Hovercard.php +++ b/src/Module/Contact/Hovercard.php @@ -65,7 +65,7 @@ class Hovercard extends BaseModule // Search for contact data // Look if the local user has got the contact if (Session::isAuthenticated()) { - $contact = Contact::getByURLForUser($contact_url, local_user(), [], false); + $contact = Contact::getByURLForUser($contact_url, local_user(), false); } else { $contact = Contact::getByURL($contact_url, false); } diff --git a/src/Module/Profile/Contacts.php b/src/Module/Profile/Contacts.php index 8c1129448d..60ba2b92e4 100644 --- a/src/Module/Profile/Contacts.php +++ b/src/Module/Profile/Contacts.php @@ -103,7 +103,7 @@ class Contacts extends BaseProfile continue; } - $contact_details = array_merge($contact, Contact::getByURLForUser($contact['url'], $a->profile['uid'], [], false)); + $contact_details = array_merge($contact, Contact::getByURLForUser($contact['url'], $a->profile['uid'], false)); $contacts[] = [ 'id' => $contact['id'], diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index bf82e04278..c9a76fef09 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -755,7 +755,7 @@ class DFRN { $author = $doc->createElement($element); - $contact = Contact::getByURLForUser($contact_url, $item["uid"], ['url', 'name', 'addr', 'photo']); + $contact = Contact::getByURLForUser($contact_url, $item["uid"], null, ['url', 'name', 'addr', 'photo']); if (!empty($contact)) { XML::addElement($doc, $author, "name", $contact["name"]); XML::addElement($doc, $author, "uri", $contact["url"]); From 6d3949d54af9ad09c4308c7032a91bd76a24fd1c Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 15 Jul 2020 17:29:52 +0000 Subject: [PATCH 022/573] Changed default value --- mod/common.php | 2 +- mod/display.php | 2 +- mod/suggest.php | 2 +- src/Core/Search.php | 6 +++--- src/Model/Contact.php | 2 +- src/Model/GContact.php | 2 +- src/Module/AllFriends.php | 2 +- src/Module/Contact/Hovercard.php | 2 +- src/Module/Profile/Contacts.php | 2 +- src/Protocol/DFRN.php | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mod/common.php b/mod/common.php index 9b327f1bf8..9231d090b8 100644 --- a/mod/common.php +++ b/mod/common.php @@ -124,7 +124,7 @@ function common_content(App $a) $entries = []; foreach ($common_friends as $common_friend) { //get further details of the contact - $contact_details = Model\Contact::getByURLForUser($common_friend['url'], $uid, false); + $contact_details = Model\Contact::getByURLForUser($common_friend['url'], $uid); // $rr['id'] is needed to use contact_photo_menu() /// @TODO Adding '/" here avoids E_NOTICE on missing constants diff --git a/mod/display.php b/mod/display.php index ddc78d6eae..61e1529e4d 100644 --- a/mod/display.php +++ b/mod/display.php @@ -164,7 +164,7 @@ function display_fetchauthor($a, $item) $profiledata["about"] = ""; } - $profiledata = array_merge($profiledata, Contact::getByURLForUser($profiledata["url"], local_user(), false)); + $profiledata = array_merge($profiledata, Contact::getByURLForUser($profiledata["url"], local_user())); if (!empty($profiledata["photo"])) { $profiledata["photo"] = DI::baseUrl()->remove($profiledata["photo"]); diff --git a/mod/suggest.php b/mod/suggest.php index 2b7b1f6743..068b9e366b 100644 --- a/mod/suggest.php +++ b/mod/suggest.php @@ -104,7 +104,7 @@ function suggest_content(App $a) 'hide' => [DI::l10n()->t('Ignore/Hide'), $ignlnk] ]; - $contact_details = array_merge($rr, Contact::getByURLForUser($rr["url"], local_user(), false)); + $contact_details = array_merge($rr, Contact::getByURLForUser($rr["url"], local_user())); $entry = [ 'url' => Contact::magicLink($rr['url']), diff --git a/src/Core/Search.php b/src/Core/Search.php index 208633272b..d6ed9b6bfb 100644 --- a/src/Core/Search.php +++ b/src/Core/Search.php @@ -77,7 +77,7 @@ class Search // Ensure that we do have a contact entry Contact::getIdForURL($user_data['url'] ?? ''); - $contactDetails = Contact::getByURLForUser($user_data['url'] ?? '', local_user(), false); + $contactDetails = Contact::getByURLForUser($user_data['url'] ?? '', local_user()); $result = new ContactResult( $user_data['name'] ?? '', @@ -143,7 +143,7 @@ class Search foreach ($profiles as $profile) { $profile_url = $profile['url'] ?? ''; - $contactDetails = Contact::getByURLForUser($profile_url, local_user(), false); + $contactDetails = Contact::getByURLForUser($profile_url, local_user()); $result = new ContactResult( $profile['name'] ?? '', @@ -232,7 +232,7 @@ class Search continue; } - $contact = Contact::getByURLForUser($row["nurl"], local_user(), false); + $contact = Contact::getByURLForUser($row["nurl"], local_user()); if ($contact["name"] == "") { $contact["name"] = end(explode("/", $urlParts["path"])); diff --git a/src/Model/Contact.php b/src/Model/Contact.php index afec920070..15e4e57035 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -238,7 +238,7 @@ class Contact * @param boolean $update true = always update, false = never update, null = update when not found or outdated * @return array contact array */ - public static function getByURLForUser(string $url, int $uid = 0, $update = null, array $fields = []) + public static function getByURLForUser(string $url, int $uid = 0, $update = false, array $fields = []) { if ($uid != 0) { $contact = self::getByURL($url, $update, $fields, $uid); diff --git a/src/Model/GContact.php b/src/Model/GContact.php index e8cd4768a6..5ef74dcf77 100644 --- a/src/Model/GContact.php +++ b/src/Model/GContact.php @@ -111,7 +111,7 @@ class GContact continue; } - $gcontacts[] = Contact::getByURLForUser($result['nurl'], local_user(), false); + $gcontacts[] = Contact::getByURLForUser($result['nurl'], local_user()); } DBA::close($results); return $gcontacts; diff --git a/src/Module/AllFriends.php b/src/Module/AllFriends.php index d2d2b3b0cf..2932930627 100644 --- a/src/Module/AllFriends.php +++ b/src/Module/AllFriends.php @@ -79,7 +79,7 @@ class AllFriends extends BaseModule $entries = []; foreach ($friends as $friend) { //get further details of the contact - $contactDetails = array_merge($friend, Model\Contact::getByURLForUser($friend['url'], $uid, false)); + $contactDetails = array_merge($friend, Model\Contact::getByURLForUser($friend['url'], $uid)); $connlnk = ''; // $friend[cid] is only available for common contacts. So if the contact is a common one, use contact_photo_menu to generate the photoMenu diff --git a/src/Module/Contact/Hovercard.php b/src/Module/Contact/Hovercard.php index 29329b2296..750b856bc3 100644 --- a/src/Module/Contact/Hovercard.php +++ b/src/Module/Contact/Hovercard.php @@ -65,7 +65,7 @@ class Hovercard extends BaseModule // Search for contact data // Look if the local user has got the contact if (Session::isAuthenticated()) { - $contact = Contact::getByURLForUser($contact_url, local_user(), false); + $contact = Contact::getByURLForUser($contact_url, local_user()); } else { $contact = Contact::getByURL($contact_url, false); } diff --git a/src/Module/Profile/Contacts.php b/src/Module/Profile/Contacts.php index 60ba2b92e4..508fe37624 100644 --- a/src/Module/Profile/Contacts.php +++ b/src/Module/Profile/Contacts.php @@ -103,7 +103,7 @@ class Contacts extends BaseProfile continue; } - $contact_details = array_merge($contact, Contact::getByURLForUser($contact['url'], $a->profile['uid'], false)); + $contact_details = array_merge($contact, Contact::getByURLForUser($contact['url'], $a->profile['uid'])); $contacts[] = [ 'id' => $contact['id'], diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index c9a76fef09..b7c3204b79 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -755,7 +755,7 @@ class DFRN { $author = $doc->createElement($element); - $contact = Contact::getByURLForUser($contact_url, $item["uid"], null, ['url', 'name', 'addr', 'photo']); + $contact = Contact::getByURLForUser($contact_url, $item["uid"], false, ['url', 'name', 'addr', 'photo']); if (!empty($contact)) { XML::addElement($doc, $author, "name", $contact["name"]); XML::addElement($doc, $author, "uri", $contact["url"]); From b0086a49e2c28c528e178bcdd28203207feceef6 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 15 Jul 2020 21:08:42 +0000 Subject: [PATCH 023/573] in "getidforurl" "no update" is now "update" --- boot.php | 4 +- include/api.php | 10 ++-- include/conversation.php | 4 +- mod/match.php | 2 +- src/Content/Widget.php | 2 +- src/Database/PostUpdate.php | 4 +- src/Model/Contact.php | 69 +++++++++++++----------- src/Model/Item.php | 6 +-- src/Model/Tag.php | 2 +- src/Module/Debug/Feed.php | 3 +- src/Protocol/ActivityPub/Processor.php | 10 ++-- src/Protocol/ActivityPub/Transmitter.php | 2 +- src/Protocol/OStatus.php | 4 +- update.php | 4 +- 14 files changed, 66 insertions(+), 60 deletions(-) diff --git a/boot.php b/boot.php index b07ad8483f..512238b52e 100644 --- a/boot.php +++ b/boot.php @@ -253,10 +253,10 @@ function public_contact() if (!$public_contact_id && !empty($_SESSION['authenticated'])) { if (!empty($_SESSION['my_address'])) { // Local user - $public_contact_id = intval(Contact::getIdForURL($_SESSION['my_address'], 0, true)); + $public_contact_id = intval(Contact::getIdForURL($_SESSION['my_address'], 0, false)); } elseif (!empty($_SESSION['visitor_home'])) { // Remote user - $public_contact_id = intval(Contact::getIdForURL($_SESSION['visitor_home'], 0, true)); + $public_contact_id = intval(Contact::getIdForURL($_SESSION['visitor_home'], 0, false)); } } elseif (empty($_SESSION['authenticated'])) { $public_contact_id = false; diff --git a/include/api.php b/include/api.php index 53d00b27de..86cda0b1bb 100644 --- a/include/api.php +++ b/include/api.php @@ -654,8 +654,8 @@ function api_get_user(App $a, $contact_id = null) 'notifications' => false, 'statusnet_profile_url' => $contact["url"], 'uid' => 0, - 'cid' => Contact::getIdForURL($contact["url"], api_user(), true), - 'pid' => Contact::getIdForURL($contact["url"], 0, true), + 'cid' => Contact::getIdForURL($contact["url"], api_user(), false), + 'pid' => Contact::getIdForURL($contact["url"], 0, false), 'self' => 0, 'network' => $contact["network"], ]; @@ -679,7 +679,7 @@ function api_get_user(App $a, $contact_id = null) $countfollowers = 0; $starred = 0; - $pcontact_id = Contact::getIdForURL($uinfo[0]['url'], 0, true); + $pcontact_id = Contact::getIdForURL($uinfo[0]['url'], 0, false); if (!empty($profile['about'])) { $description = $profile['about']; @@ -731,7 +731,7 @@ function api_get_user(App $a, $contact_id = null) 'statusnet_profile_url' => $uinfo[0]['url'], 'uid' => intval($uinfo[0]['uid']), 'cid' => intval($uinfo[0]['cid']), - 'pid' => Contact::getIdForURL($uinfo[0]["url"], 0, true), + 'pid' => Contact::getIdForURL($uinfo[0]["url"], 0, false), 'self' => $uinfo[0]['self'], 'network' => $uinfo[0]['network'], ]; @@ -5052,7 +5052,7 @@ function api_share_as_retweet(&$item) $reshared_item["share-pre-body"] = $reshared['comment']; $reshared_item["body"] = $reshared['shared']; - $reshared_item["author-id"] = Contact::getIdForURL($reshared['profile'], 0, true); + $reshared_item["author-id"] = Contact::getIdForURL($reshared['profile'], 0, false); $reshared_item["author-name"] = $reshared['author']; $reshared_item["author-link"] = $reshared['profile']; $reshared_item["author-avatar"] = $reshared['avatar']; diff --git a/include/conversation.php b/include/conversation.php index 6e024fe20f..8507c5ba97 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -325,7 +325,7 @@ function conv_get_blocklist() foreach (explode(',', $str_blocked) as $entry) { // The 4th parameter guarantees that there always will be a public contact entry - $cid = Contact::getIdForURL(trim($entry), 0, true, ['url' => trim($entry)]); + $cid = Contact::getIdForURL(trim($entry), 0, false, ['url' => trim($entry)]); if (!empty($cid)) { $blocklist[] = $cid; } @@ -837,7 +837,7 @@ function item_photo_menu($item) { $sparkle = (strpos($profile_link, 'redir/') === 0); $cid = 0; - $pcid = Contact::getIdForURL($item['author-link'], 0, true); + $pcid = Contact::getIdForURL($item['author-link'], 0, false); $network = ''; $rel = 0; $condition = ['uid' => local_user(), 'nurl' => Strings::normaliseLink($item['author-link'])]; diff --git a/mod/match.php b/mod/match.php index b54be01347..747e0b2f03 100644 --- a/mod/match.php +++ b/mod/match.php @@ -89,7 +89,7 @@ function match_content(App $a) $profile = $msearch->results[$i]; // Already known contact - if (!$profile || Contact::getIdForURL($profile->url, local_user(), true)) { + if (!$profile || Contact::getIdForURL($profile->url, local_user(), false)) { continue; } diff --git a/src/Content/Widget.php b/src/Content/Widget.php index ffc5debb33..fad863fbeb 100644 --- a/src/Content/Widget.php +++ b/src/Content/Widget.php @@ -471,7 +471,7 @@ class Widget } if (Feature::isEnabled($uid, 'tagadelic')) { - $owner_id = Contact::getIdForURL($a->profile['url'], 0, true); + $owner_id = Contact::getIdForURL($a->profile['url'], 0, false); if (!$owner_id) { return ''; diff --git a/src/Database/PostUpdate.php b/src/Database/PostUpdate.php index 0ceae07f70..6d8b197db1 100644 --- a/src/Database/PostUpdate.php +++ b/src/Database/PostUpdate.php @@ -245,14 +245,14 @@ class PostUpdate $default = ['url' => $item['author-link'], 'name' => $item['author-name'], 'photo' => $item['author-avatar'], 'network' => $item['network']]; - $item['author-id'] = Contact::getIdForURL($item["author-link"], 0, false, $default); + $item['author-id'] = Contact::getIdForURL($item["author-link"], 0, null, $default); } if (empty($item['owner-id'])) { $default = ['url' => $item['owner-link'], 'name' => $item['owner-name'], 'photo' => $item['owner-avatar'], 'network' => $item['network']]; - $item['owner-id'] = Contact::getIdForURL($item["owner-link"], 0, false, $default); + $item['owner-id'] = Contact::getIdForURL($item["owner-link"], 0, null, $default); } if (empty($item['psid'])) { diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 15e4e57035..179126ff10 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -194,21 +194,30 @@ class Contact * Fetches a contact by a given url * * @param string $url profile url - * @param integer $uid User ID of the contact - * @param array $fields Field list * @param boolean $update true = always update, false = never update, null = update when not found or outdated + * @param array $fields Field list + * @param integer $uid User ID of the contact * @return array contact array */ public static function getByURL(string $url, $update = null, array $fields = [], int $uid = 0) { if ($update || is_null($update)) { - $cid = self::getIdForURL($url, $uid, !($update ?? false)); + $cid = self::getIdForURL($url, $uid, $update); if (empty($cid)) { return []; } return self::getById($cid, $fields); } + // Add internal fields + $removal = []; + foreach (['id', 'updated', 'network'] as $internal) { + if (!in_array($internal, $fields)) { + $fields[] = $internal; + $removal[] = $internal; + } + } + // We first try the nurl (http://server.tld/nick), most common case $options = ['order' => ['id']]; $contact = DBA::selectFirst('contact', $fields, ['nurl' => Strings::normaliseLink($url), 'uid' => $uid, 'deleted' => false], $options); @@ -225,6 +234,18 @@ class Contact $condition = ['`alias` IN (?, ?, ?) AND `uid` = ? AND NOT `deleted`', $url, Strings::normaliseLink($url), $ssl_url, $uid]; $contact = DBA::selectFirst('contact', $fields, $condition, $options); } + + // Update the contact in the background if needed + if ((($contact['updated'] < DateTimeFormat::utc('now -7 days')) || empty($contact['avatar'])) && + in_array($contact['network'], Protocol::FEDERATED)) { + Worker::add(PRIORITY_LOW, "UpdateContact", $contact['id'], ($uid == 0 ? 'force' : '')); + } + + // Remove the internal fields + foreach ($removal as $internal) { + unset($contact[$internal]); + } + return $contact; } @@ -234,8 +255,8 @@ class Contact * * @param string $url profile url * @param integer $uid User ID of the contact - * @param array $fields Field list * @param boolean $update true = always update, false = never update, null = update when not found or outdated + * @param array $fields Field list * @return array contact array */ public static function getByURLForUser(string $url, int $uid = 0, $update = false, array $fields = []) @@ -296,7 +317,7 @@ class Contact */ public static function isFollowerByURL($url, $uid) { - $cid = self::getIdForURL($url, $uid, true); + $cid = self::getIdForURL($url, $uid, false); if (empty($cid)) { return false; @@ -342,7 +363,7 @@ class Contact */ public static function isSharingByURL($url, $uid) { - $cid = self::getIdForURL($url, $uid, true); + $cid = self::getIdForURL($url, $uid, false); if (empty($cid)) { return false; @@ -437,7 +458,7 @@ class Contact if (!DBA::isResult($self)) { return false; } - return self::getIdForURL($self['url'], 0, true); + return self::getIdForURL($self['url'], 0, false); } /** @@ -467,14 +488,14 @@ class Contact } if ($contact['uid'] != 0) { - $pcid = Contact::getIdForURL($contact['url'], 0, true, ['url' => $contact['url']]); + $pcid = Contact::getIdForURL($contact['url'], 0, false, ['url' => $contact['url']]); if (empty($pcid)) { return []; } $ucid = $contact['id']; } else { $pcid = $contact['id']; - $ucid = Contact::getIdForURL($contact['url'], $uid, true); + $ucid = Contact::getIdForURL($contact['url'], $uid, false); } return ['public' => $pcid, 'user' => $ucid]; @@ -1300,7 +1321,7 @@ class Contact * * @param string $url Contact URL * @param integer $uid The user id for the contact (0 = public contact) - * @param boolean $no_update Don't update the contact + * @param boolean $update true = always update, false = never update, null = update when not found or outdated * @param array $default Default value for creating the contact when every else fails * @param boolean $in_loop Internally used variable to prevent an endless loop * @@ -1308,7 +1329,7 @@ class Contact * @throws HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function getIdForURL($url, $uid = 0, $no_update = false, $default = [], $in_loop = false) + public static function getIdForURL($url, $uid = 0, $update = null, $default = [], $in_loop = false) { Logger::info('Get contact data', ['url' => $url, 'user' => $uid]); @@ -1322,17 +1343,8 @@ class Contact if (!empty($contact)) { $contact_id = $contact["id"]; - $update_contact = false; - // Update the contact every 7 days (Don't update mail or feed contacts) - if (in_array($contact['network'], Protocol::FEDERATED)) { - $update_contact = ($contact['updated'] < DateTimeFormat::utc('now -7 days')); - - // We force the update if the avatar is empty - if (empty($contact['avatar'])) { - $update_contact = true; - } - } elseif (empty($default) && in_array($contact['network'], [Protocol::MAIL, Protocol::PHANTOM]) && ($uid == 0)) { + if (empty($default) && in_array($contact['network'], [Protocol::MAIL, Protocol::PHANTOM]) && ($uid == 0)) { // Update public mail accounts via their user's accounts $fields = ['network', 'addr', 'name', 'nick', 'avatar', 'photo', 'thumb', 'micro']; $mailcontact = DBA::selectFirst('contact', $fields, ["`addr` = ? AND `network` = ? AND `uid` != 0", $url, Protocol::MAIL]); @@ -1345,12 +1357,7 @@ class Contact } } - // Update the contact in the background if needed but it is called by the frontend - if ($update_contact && $no_update && in_array($contact['network'], Protocol::NATIVE_SUPPORT)) { - Worker::add(PRIORITY_LOW, "UpdateContact", $contact_id, ($uid == 0 ? 'force' : '')); - } - - if (!$update_contact || $no_update) { + if (empty($update)) { return $contact_id; } } elseif ($uid != 0) { @@ -1358,11 +1365,11 @@ class Contact return 0; } - if ($no_update && empty($default)) { + if (!$update && empty($default)) { // When we don't want to update, we look if we know this contact in any way $data = self::getProbeDataFromDatabase($url, $contact_id); $background_update = true; - } elseif ($no_update && !empty($default['network'])) { + } elseif (!$update && !empty($default['network'])) { // If there are default values, take these $data = $default; $background_update = false; @@ -1371,7 +1378,7 @@ class Contact $background_update = false; } - if (empty($data)) { + if ((empty($data) && is_null($update)) || $update) { $data = Probe::uri($url, "", $uid); } @@ -1394,7 +1401,7 @@ class Contact } if (!$contact_id && !empty($data['alias']) && ($data['alias'] != $data['url']) && !$in_loop) { - $contact_id = self::getIdForURL($data["alias"], $uid, true, $default, true); + $contact_id = self::getIdForURL($data["alias"], $uid, false, $default, true); } if (!$contact_id) { diff --git a/src/Model/Item.php b/src/Model/Item.php index 4fff365562..332c734fa5 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -1680,11 +1680,11 @@ class Item $default = ['url' => $item['author-link'], 'name' => $item['author-name'], 'photo' => $item['author-avatar'], 'network' => $item['network']]; - $item['author-id'] = ($item['author-id'] ?? 0) ?: Contact::getIdForURL($item['author-link'], 0, false, $default); + $item['author-id'] = ($item['author-id'] ?? 0) ?: Contact::getIdForURL($item['author-link'], 0, null, $default); $default = ['url' => $item['owner-link'], 'name' => $item['owner-name'], 'photo' => $item['owner-avatar'], 'network' => $item['network']]; - $item['owner-id'] = ($item['owner-id'] ?? 0) ?: Contact::getIdForURL($item['owner-link'], 0, false, $default); + $item['owner-id'] = ($item['owner-id'] ?? 0) ?: Contact::getIdForURL($item['owner-link'], 0, null, $default); // The contact-id should be set before "self::insert" was called - but there seems to be issues sometimes $item["contact-id"] = self::contactId($item); @@ -2976,7 +2976,7 @@ class Item if (local_user() == $uid) { $item_contact_id = $owner_self_contact['id']; } else { - $item_contact_id = Contact::getIdForURL($author_contact['url'], $uid, true); + $item_contact_id = Contact::getIdForURL($author_contact['url'], $uid, false); $item_contact = DBA::selectFirst('contact', [], ['id' => $item_contact_id]); if (!DBA::isResult($item_contact)) { Logger::log('like: unknown item contact ' . $item_contact_id); diff --git a/src/Model/Tag.php b/src/Model/Tag.php index d8c252ca2b..3424a23771 100644 --- a/src/Model/Tag.php +++ b/src/Model/Tag.php @@ -111,7 +111,7 @@ class Tag } } } else { - $cid = Contact::getIdForURL($url, 0, true); + $cid = Contact::getIdForURL($url, 0, false); Logger::info('Got id by probing', ['cid' => $cid, 'url' => $url]); } diff --git a/src/Module/Debug/Feed.php b/src/Module/Debug/Feed.php index 4f17b70e68..e969de9cc3 100644 --- a/src/Module/Debug/Feed.php +++ b/src/Module/Debug/Feed.php @@ -47,8 +47,7 @@ class Feed extends BaseModule if (!empty($_REQUEST['url'])) { $url = $_REQUEST['url']; - $contact_id = Model\Contact::getIdForURL($url, local_user(), true); - $contact = Model\Contact::getById($contact_id); + $contact = Model\Contact::getByURLForUser($url, local_user(), false); $xml = Network::fetchUrl($contact['poll']); diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 0e4aca4a5a..745a56c2a5 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -445,9 +445,9 @@ class Processor $item['network'] = Protocol::ACTIVITYPUB; $item['author-link'] = $activity['author']; - $item['author-id'] = Contact::getIdForURL($activity['author'], 0, true); + $item['author-id'] = Contact::getIdForURL($activity['author'], 0, false); $item['owner-link'] = $activity['actor']; - $item['owner-id'] = Contact::getIdForURL($activity['actor'], 0, true); + $item['owner-id'] = Contact::getIdForURL($activity['actor'], 0, false); if (in_array(0, $activity['receiver']) && !empty($activity['unlisted'])) { $item['private'] = Item::UNLISTED; @@ -511,13 +511,13 @@ class Processor $item['uid'] = $receiver; if ($isForum) { - $item['contact-id'] = Contact::getIdForURL($activity['actor'], $receiver, true); + $item['contact-id'] = Contact::getIdForURL($activity['actor'], $receiver, false); } else { - $item['contact-id'] = Contact::getIdForURL($activity['author'], $receiver, true); + $item['contact-id'] = Contact::getIdForURL($activity['author'], $receiver, false); } if (($receiver != 0) && empty($item['contact-id'])) { - $item['contact-id'] = Contact::getIdForURL($activity['author'], 0, true); + $item['contact-id'] = Contact::getIdForURL($activity['author'], 0, false); } if (!empty($activity['directmessage'])) { diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index bd6f67128f..6bf507ed8f 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -150,7 +150,7 @@ class Transmitter */ public static function getOutbox($owner, $page = null) { - $public_contact = Contact::getIdForURL($owner['url'], 0, true); + $public_contact = Contact::getIdForURL($owner['url'], 0, false); $condition = ['uid' => 0, 'contact-id' => $public_contact, 'author-id' => $public_contact, 'private' => [Item::PUBLIC, Item::UNLISTED], 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT], diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index dc9182009a..1ab8f9b800 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -221,7 +221,7 @@ class OStatus } // Ensure that we are having this contact (with uid=0) - $cid = Contact::getIdForURL($aliaslink, 0, true); + $cid = Contact::getIdForURL($aliaslink, 0, false); if ($cid) { $fields = ['url', 'nurl', 'name', 'nick', 'alias', 'about', 'location']; @@ -2220,7 +2220,7 @@ class OStatus } $check_date = $feed_mode ? '' : DateTimeFormat::utc($last_update); - $authorid = Contact::getIdForURL($owner["url"], 0, true); + $authorid = Contact::getIdForURL($owner["url"], 0, false); $condition = ["`uid` = ? AND `received` > ? AND NOT `deleted` AND `private` != ? AND `visible` AND `wall` AND `parent-network` IN (?, ?)", diff --git a/update.php b/update.php index 83011108b5..82c34f9dda 100644 --- a/update.php +++ b/update.php @@ -203,7 +203,7 @@ function update_1260() while ($item = DBA::fetch($items)) { $contact = ['url' => $item['owner-link'], 'name' => $item['owner-name'], 'photo' => $item['owner-avatar'], 'network' => $item['network']]; - $cid = Contact::getIdForURL($item['owner-link'], 0, false, $contact); + $cid = Contact::getIdForURL($item['owner-link'], 0, null, $contact); if (empty($cid)) { continue; } @@ -219,7 +219,7 @@ function update_1260() while ($item = DBA::fetch($items)) { $contact = ['url' => $item['author-link'], 'name' => $item['author-name'], 'photo' => $item['author-avatar'], 'network' => $item['network']]; - $cid = Contact::getIdForURL($item['author-link'], 0, false, $contact); + $cid = Contact::getIdForURL($item['author-link'], 0, null, $contact); if (empty($cid)) { continue; } From caf548e1a704e9576706e63f70392d8e908893e5 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 16 Jul 2020 03:52:18 +0000 Subject: [PATCH 024/573] Fix fetching contacts --- src/Model/Contact.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 179126ff10..646da57caa 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -211,11 +211,13 @@ class Contact // Add internal fields $removal = []; - foreach (['id', 'updated', 'network'] as $internal) { - if (!in_array($internal, $fields)) { - $fields[] = $internal; - $removal[] = $internal; - } + if (!empty($fields)) { + foreach (['id', 'updated', 'network'] as $internal) { + if (!in_array($internal, $fields)) { + $fields[] = $internal; + $removal[] = $internal; + } + } } // We first try the nurl (http://server.tld/nick), most common case From b8682190de8bce2cebb80635a2fa03482d37eeae Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 16 Jul 2020 04:18:33 +0000 Subject: [PATCH 025/573] Fix fallback on unknown contact --- mod/display.php | 2 +- mod/suggest.php | 2 +- src/Core/Search.php | 8 ++++---- src/Module/AllFriends.php | 2 +- src/Module/Profile/Contacts.php | 2 +- src/Module/Search/Index.php | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mod/display.php b/mod/display.php index 61e1529e4d..1a429948a9 100644 --- a/mod/display.php +++ b/mod/display.php @@ -164,7 +164,7 @@ function display_fetchauthor($a, $item) $profiledata["about"] = ""; } - $profiledata = array_merge($profiledata, Contact::getByURLForUser($profiledata["url"], local_user())); + $profiledata = Contact::getByURLForUser($profiledata["url"], local_user()) ?: $profiledata; if (!empty($profiledata["photo"])) { $profiledata["photo"] = DI::baseUrl()->remove($profiledata["photo"]); diff --git a/mod/suggest.php b/mod/suggest.php index 068b9e366b..9cd2fb1cd9 100644 --- a/mod/suggest.php +++ b/mod/suggest.php @@ -104,7 +104,7 @@ function suggest_content(App $a) 'hide' => [DI::l10n()->t('Ignore/Hide'), $ignlnk] ]; - $contact_details = array_merge($rr, Contact::getByURLForUser($rr["url"], local_user())); + $contact_details = Contact::getByURLForUser($rr["url"], local_user()) ?: $rr; $entry = [ 'url' => Contact::magicLink($rr['url']), diff --git a/src/Core/Search.php b/src/Core/Search.php index d6ed9b6bfb..60137e66f7 100644 --- a/src/Core/Search.php +++ b/src/Core/Search.php @@ -202,7 +202,7 @@ class Search return $resultList; } - $data = DBA::select('gcontact', ['nurl'], [ + $data = DBA::select('gcontact', ['nurl', 'name', 'addr', 'url', 'photo', 'network', 'keywords'], [ 'NOT `hide` AND `network` IN (?, ?, ?, ?) AND ((`last_contact` >= `last_failure`) OR (`updated` >= `last_failure`)) @@ -232,7 +232,7 @@ class Search continue; } - $contact = Contact::getByURLForUser($row["nurl"], local_user()); + $contact = Contact::getByURLForUser($row["nurl"], local_user()) ?: $row; if ($contact["name"] == "") { $contact["name"] = end(explode("/", $urlParts["path"])); @@ -245,8 +245,8 @@ class Search $contact["url"], $contact["photo"], $contact["network"], - $contact["cid"], - $contact["zid"], + $contact["cid"] ?? 0, + $contact["zid"] ?? 0, $contact["keywords"] ); diff --git a/src/Module/AllFriends.php b/src/Module/AllFriends.php index 2932930627..0a95256172 100644 --- a/src/Module/AllFriends.php +++ b/src/Module/AllFriends.php @@ -79,7 +79,7 @@ class AllFriends extends BaseModule $entries = []; foreach ($friends as $friend) { //get further details of the contact - $contactDetails = array_merge($friend, Model\Contact::getByURLForUser($friend['url'], $uid)); + $contactDetails = Model\Contact::getByURLForUser($friend['url'], $uid) ?: $friend; $connlnk = ''; // $friend[cid] is only available for common contacts. So if the contact is a common one, use contact_photo_menu to generate the photoMenu diff --git a/src/Module/Profile/Contacts.php b/src/Module/Profile/Contacts.php index 508fe37624..3d55c57f48 100644 --- a/src/Module/Profile/Contacts.php +++ b/src/Module/Profile/Contacts.php @@ -103,7 +103,7 @@ class Contacts extends BaseProfile continue; } - $contact_details = array_merge($contact, Contact::getByURLForUser($contact['url'], $a->profile['uid'])); + $contact_details = Contact::getByURLForUser($contact['url'], $a->profile['uid']) ?: $contact; $contacts[] = [ 'id' => $contact['id'], diff --git a/src/Module/Search/Index.php b/src/Module/Search/Index.php index 34085e339e..23f12d2638 100644 --- a/src/Module/Search/Index.php +++ b/src/Module/Search/Index.php @@ -239,7 +239,7 @@ class Index extends BaseSearch if ($isAddr) { $contact = Contact::selectFirst(['id'], ['addr' => $search, 'uid' => 0]); } else { - $contact = array_merge(['id' => 0], Contact::getByURL($search, null, ['id'])); + $contact = Contact::getByURL($search, null, ['id']) ?: ['id' => 0]; } if (DBA::isResult($contact)) { From c352af8edae5a17af4093ee8dd7da3fb2eb5f9d7 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 16 Jul 2020 04:45:12 +0000 Subject: [PATCH 026/573] Reverting accidentally commited test --- src/Util/Profiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Util/Profiler.php b/src/Util/Profiler.php index 1a5cd99922..240273bde3 100644 --- a/src/Util/Profiler.php +++ b/src/Util/Profiler.php @@ -177,7 +177,7 @@ class Profiler implements ContainerInterface $output .= "\nDatabase Read:\n"; foreach ($this->callstack["database"] as $func => $time) { $time = round($time, 3); - if ($time > 0.001) { + if ($time > 0) { $output .= $func . ": " . $time . "\n"; } } From 4a550ddcd81e705bdf5e2b7cc4a39e2ea586d26f Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 16 Jul 2020 06:13:47 +0000 Subject: [PATCH 027/573] Prevent "null" value when calling "getTagsFromUrl" --- src/Protocol/Feed.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Protocol/Feed.php b/src/Protocol/Feed.php index 86b76d309b..63bcb9fb9e 100644 --- a/src/Protocol/Feed.php +++ b/src/Protocol/Feed.php @@ -544,7 +544,7 @@ class Feed if (!empty($contact["fetch_further_information"]) && ($contact["fetch_further_information"] == 3)) { if (empty($taglist)) { - $taglist = PageInfo::getTagsFromUrl($item["plink"], $preview, $contact["ffi_keyword_denylist"]); + $taglist = PageInfo::getTagsFromUrl($item["plink"], $preview, $contact["ffi_keyword_denylist"] ?? ''); } $item["body"] .= "\n" . self::tagToString($taglist); } else { From d6bf7f2cdad51a11080a3285193c5cd09a6d768d Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 16 Jul 2020 10:22:14 +0000 Subject: [PATCH 028/573] Replace "Probe::uri" with "Contact::getByURL" --- mod/dfrn_request.php | 2 +- mod/ostatus_subscribe.php | 5 ++--- src/Model/GServer.php | 5 ++--- src/Model/Mail.php | 3 +-- src/Module/Acctlink.php | 6 +++--- src/Module/RemoteFollow.php | 5 +++-- src/Worker/Notifier.php | 12 ++++++------ 7 files changed, 18 insertions(+), 20 deletions(-) diff --git a/mod/dfrn_request.php b/mod/dfrn_request.php index f5716e8ff5..f8e4c90236 100644 --- a/mod/dfrn_request.php +++ b/mod/dfrn_request.php @@ -294,7 +294,7 @@ function dfrn_request_post(App $a) $hcard = ''; // Detect the network - $data = Probe::uri($url); + $data = Contact::getByURL($url); $network = $data["network"]; // Canonicalize email-style profile locator diff --git a/mod/ostatus_subscribe.php b/mod/ostatus_subscribe.php index 64774eead9..751afcc731 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\Probe; use Friendica\Util\Network; function ostatus_subscribe_content(App $a) @@ -47,7 +46,7 @@ function ostatus_subscribe_content(App $a) return $o . DI::l10n()->t('No contact provided.'); } - $contact = Probe::uri($_REQUEST['url']); + $contact = Contact::getByURL($_REQUEST['url']); if (!$contact) { DI::pConfig()->delete($uid, 'ostatus', 'legacy_contact'); return $o . DI::l10n()->t('Couldn\'t fetch information for contact.'); @@ -88,7 +87,7 @@ function ostatus_subscribe_content(App $a) $o .= '

' . $counter . '/' . $total . ': ' . $url; - $probed = Probe::uri($url); + $probed = Contact::getByURL($url); if ($probed['network'] == Protocol::OSTATUS) { $result = Contact::createFromProbe($a->user, $probed['url'], true, Protocol::OSTATUS); if ($result['success']) { diff --git a/src/Model/GServer.php b/src/Model/GServer.php index 704d091a66..fdbc775098 100644 --- a/src/Model/GServer.php +++ b/src/Model/GServer.php @@ -37,7 +37,6 @@ use Friendica\Core\Logger; use Friendica\Core\System; use Friendica\Protocol\PortableContact; use Friendica\Protocol\Diaspora; -use Friendica\Network\Probe; /** * This class handles GServer related functions @@ -980,8 +979,8 @@ class GServer } foreach ($contacts as $contact) { - $probed = Probe::uri($contact); - if (in_array($probed['network'], Protocol::FEDERATED)) { + $probed = Contact::getByURL($contact); + if (!empty($probed) && in_array($probed['network'], Protocol::FEDERATED)) { $serverdata['network'] = $probed['network']; break; } diff --git a/src/Model/Mail.php b/src/Model/Mail.php index 4056352151..67d5d1ddca 100644 --- a/src/Model/Mail.php +++ b/src/Model/Mail.php @@ -27,7 +27,6 @@ use Friendica\Core\Worker; use Friendica\DI; use Friendica\Database\DBA; use Friendica\Model\Notify\Type; -use Friendica\Network\Probe; use Friendica\Protocol\Activity; use Friendica\Util\DateTimeFormat; use Friendica\Worker\Delivery; @@ -267,7 +266,7 @@ class Mail $guid = System::createUUID(); $uri = Item::newURI(local_user(), $guid); - $me = Probe::uri($replyto); + $me = Contact::getByURL($replyto); if (!$me['name']) { return -2; } diff --git a/src/Module/Acctlink.php b/src/Module/Acctlink.php index f80ea4c73c..bdcc3cf6ff 100644 --- a/src/Module/Acctlink.php +++ b/src/Module/Acctlink.php @@ -22,8 +22,8 @@ namespace Friendica\Module; use Friendica\BaseModule; -use Friendica\Network\Probe; use Friendica\Core\System; +use Friendica\Model\Contact; /** * Redirects to another URL based on the parameter 'addr' @@ -35,9 +35,9 @@ class Acctlink extends BaseModule $addr = trim($_GET['addr'] ?? ''); if ($addr) { - $url = Probe::uri($addr)['url'] ?? ''; + $url = Contact::getByURL($addr)['url'] ?? ''; if ($url) { - System::externalRedirect($url); + System::externalRedirect($url['url']); exit(); } } diff --git a/src/Module/RemoteFollow.php b/src/Module/RemoteFollow.php index bf71b077fd..274ed3d06d 100644 --- a/src/Module/RemoteFollow.php +++ b/src/Module/RemoteFollow.php @@ -28,6 +28,7 @@ use Friendica\Core\Protocol; use Friendica\Core\Renderer; use Friendica\Core\Search; use Friendica\Core\System; +use Friendica\Model\Contact; use Friendica\Model\Profile; use Friendica\Network\Probe; @@ -61,8 +62,8 @@ class RemoteFollow extends BaseModule } // Detect the network, make sure the provided URL is valid - $data = Probe::uri($url); - if ($data['network'] == Protocol::PHANTOM) { + $data = Contact::getByURL($url); + if (!$data) { notice(DI::l10n()->t("The provided profile link doesn't seem to be valid")); return; } diff --git a/src/Worker/Notifier.php b/src/Worker/Notifier.php index c7b61dacca..99b97adc8d 100644 --- a/src/Worker/Notifier.php +++ b/src/Worker/Notifier.php @@ -354,23 +354,23 @@ class Notifier // Send a salmon to the parent author $probed_contact = DBA::selectFirst('contact', ['url', 'notify'], ['id' => $thr_parent['author-id']]); if (DBA::isResult($probed_contact) && !empty($probed_contact["notify"])) { - Logger::log('Notify parent author '.$probed_contact["url"].': '.$probed_contact["notify"]); + Logger::notice('Notify parent author', ['url' => $probed_contact["url"], 'notify' => $probed_contact["notify"]]); $url_recipients[$probed_contact["notify"]] = $probed_contact["notify"]; } // Send a salmon to the parent owner $probed_contact = DBA::selectFirst('contact', ['url', 'notify'], ['id' => $thr_parent['owner-id']]); if (DBA::isResult($probed_contact) && !empty($probed_contact["notify"])) { - Logger::log('Notify parent owner '.$probed_contact["url"].': '.$probed_contact["notify"]); + Logger::notice('Notify parent owner', ['url' => $probed_contact["url"], 'notify' => $probed_contact["notify"]]); $url_recipients[$probed_contact["notify"]] = $probed_contact["notify"]; } // Send a salmon notification to every person we mentioned in the post foreach (Tag::getByURIId($target_item['uri-id'], [Tag::MENTION, Tag::EXCLUSIVE_MENTION, Tag::IMPLICIT_MENTION]) as $tag) { - $probed_contact = Probe::uri($tag['url']); - if ($probed_contact["notify"] != "") { - Logger::log('Notify mentioned user '.$probed_contact["url"].': '.$probed_contact["notify"]); - $url_recipients[$probed_contact["notify"]] = $probed_contact["notify"]; + $probed_contact = Contact::getByURL($tag['url']); + if (!empty($probed_contact['notify'])) { + Logger::notice('Notify mentioned user', ['url' => $probed_contact["url"], 'notify' => $probed_contact["notify"]]); + $url_recipients[$probed_contact['notify']] = $probed_contact['notify']; } } From 84a340a064017587dde0d8d4db346117e6dcb985 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 16 Jul 2020 10:23:56 +0000 Subject: [PATCH 029/573] Update: Don't throw an error if the target field already exists --- update.php | 1 + 1 file changed, 1 insertion(+) diff --git a/update.php b/update.php index 83011108b5..1448d818c8 100644 --- a/update.php +++ b/update.php @@ -513,6 +513,7 @@ function update_1351() function pre_update_1354() { if (DBStructure::existsColumn('contact', ['ffi_keyword_blacklist']) + && !DBStructure::existsColumn('contact', ['ffi_keyword_denylist']) && !DBA::e("ALTER TABLE `contact` CHANGE `ffi_keyword_blacklist` `ffi_keyword_denylist` text null")) { return Update::FAILED; } From bc5c19192d3703972100f31cd9f3543c0a287446 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 16 Jul 2020 18:30:20 +0000 Subject: [PATCH 030/573] Adding a post update to clean up the contact table --- update.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/update.php b/update.php index 1448d818c8..3e74efd29f 100644 --- a/update.php +++ b/update.php @@ -517,6 +517,21 @@ function pre_update_1354() && !DBA::e("ALTER TABLE `contact` CHANGE `ffi_keyword_blacklist` `ffi_keyword_denylist` text null")) { return Update::FAILED; } - + return Update::SUCCESS; +} + +function update_1354() +{ + if (DBStructure::existsColumn('contact', ['ffi_keyword_blacklist']) + && DBStructure::existsColumn('contact', ['ffi_keyword_denylist'])) { + if (!DBA::e("UPDATE `contact` SET `ffi_keyword_denylist` = `ffi_keyword_blacklist`")) { + return Update::FAILED; + } + + // When the data had been copied then the main task is done. + // Having the old field removed is only beauty but not crucial. + // So we don't care if this was successful or not. + DBA::e("ALTER TABLE `contact` DROP `ffi_keyword_blacklist`"); + } return Update::SUCCESS; } From a2ed86cdbe70f59ce802db8f5f9f20619cc8ee41 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 16 Jul 2020 19:21:52 +0000 Subject: [PATCH 031/573] Replaced more calls of "Probe::uri" with "Contact::getByURL" --- src/Core/Search.php | 10 ++---- src/Protocol/OStatus.php | 59 ++-------------------------------- src/Worker/SearchDirectory.php | 4 +-- 3 files changed, 7 insertions(+), 66 deletions(-) diff --git a/src/Core/Search.php b/src/Core/Search.php index 60137e66f7..ea979610e6 100644 --- a/src/Core/Search.php +++ b/src/Core/Search.php @@ -26,10 +26,8 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\GContact; use Friendica\Network\HTTPException; -use Friendica\Network\Probe; use Friendica\Object\Search\ContactResult; use Friendica\Object\Search\ResultList; -use Friendica\Protocol\PortableContact; use Friendica\Util\Network; use Friendica\Util\Strings; @@ -64,8 +62,7 @@ class Search if ((filter_var($user, FILTER_VALIDATE_EMAIL) && Network::isEmailDomainValid($user)) || (substr(Strings::normaliseLink($user), 0, 7) == "http://")) { - /// @todo Possibly use "getIdForURL" instead? - $user_data = Probe::uri($user); + $user_data = Contact::getByURL($user); if (empty($user_data)) { return $emptyResultList; } @@ -74,9 +71,6 @@ class Search return $emptyResultList; } - // Ensure that we do have a contact entry - Contact::getIdForURL($user_data['url'] ?? ''); - $contactDetails = Contact::getByURLForUser($user_data['url'] ?? '', local_user()); $result = new ContactResult( @@ -87,7 +81,7 @@ class Search $user_data['photo'] ?? '', $user_data['network'] ?? '', $contactDetails['id'] ?? 0, - 0, + $user_data['id'] ?? 0, $user_data['tags'] ?? '' ); diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 1ab8f9b800..0b95a3a19d 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -1617,59 +1617,6 @@ class OStatus return $source; } - /** - * Fetches contact data from the contact or the gcontact table - * - * @param string $url URL of the contact - * @param array $owner Contact data of the poster - * - * @return array Contact array - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - private static function contactEntry($url, array $owner) - { - $r = q( - "SELECT * FROM `contact` WHERE `nurl` = '%s' AND `uid` IN (0, %d) ORDER BY `uid` DESC LIMIT 1", - DBA::escape(Strings::normaliseLink($url)), - intval($owner["uid"]) - ); - if (DBA::isResult($r)) { - $contact = $r[0]; - $contact["uid"] = -1; - } - - if (!DBA::isResult($r)) { - $gcontact = DBA::selectFirst('gcontact', [], ['nurl' => Strings::normaliseLink($url)]); - if (DBA::isResult($r)) { - $contact = $gcontact; - $contact["uid"] = -1; - $contact["success_update"] = $contact["updated"]; - } - } - - if (!DBA::isResult($r)) { - $contact = $owner; - } - - if (!isset($contact["poll"])) { - $data = Probe::uri($url); - $contact["poll"] = $data["poll"]; - - if (!$contact["alias"]) { - $contact["alias"] = $data["alias"]; - } - } - - if (!isset($contact["alias"])) { - $contact["alias"] = $contact["url"]; - } - - $contact['account-type'] = $owner['account-type']; - - return $contact; - } - /** * Adds an entry element with reshared content * @@ -1699,7 +1646,7 @@ class OStatus return false; } - $contact = self::contactEntry($repeated_item['author-link'], $owner); + $contact = Contact::getByURL($repeated_item['author-link']) ?: $owner; $title = $owner["nick"]." repeated a notice by ".$contact["nick"]; @@ -1841,7 +1788,7 @@ class OStatus $item["created"] = $item["edited"] = date("c"); $item["private"] = Item::PRIVATE; - $contact = Probe::uri($item['follow']); + $contact = Contact::getByURL($item['follow']); $item['follow'] = $contact['url']; if ($contact['alias']) { @@ -1948,7 +1895,7 @@ class OStatus $entry = $doc->createElement("entry"); if ($owner['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) { - $contact = self::contactEntry($item['author-link'], $owner); + $contact = Contact::getByURL($item['author-link']) ?: $owner; $author = self::addAuthor($doc, $contact, false); $entry->appendChild($author); } diff --git a/src/Worker/SearchDirectory.php b/src/Worker/SearchDirectory.php index c099a5e28a..2ffe6120ec 100644 --- a/src/Worker/SearchDirectory.php +++ b/src/Worker/SearchDirectory.php @@ -27,9 +27,9 @@ use Friendica\Core\Protocol; use Friendica\Core\Search; use Friendica\Database\DBA; use Friendica\DI; +use Friendica\Model\Contact; use Friendica\Model\GContact; use Friendica\Model\GServer; -use Friendica\Network\Probe; use Friendica\Util\Network; use Friendica\Util\Strings; @@ -81,7 +81,7 @@ class SearchDirectory Logger::info('Friendica server seems to be okay.', ['server' => $server_url]); } - $data = Probe::uri($jj->url); + $data = Contact::getByURL($jj->url); if ($data['network'] == Protocol::DFRN) { Logger::info('Add profile to local directory', ['profile' => $jj->url]); From 70699878ee57790fee98f4d66301a68c89ac643e Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 16 Jul 2020 19:22:38 +0000 Subject: [PATCH 032/573] Replaced more calls of "Probe::uri" with "Contact::getByURL" --- src/Core/Search.php | 10 ++---- src/Protocol/OStatus.php | 59 ++-------------------------------- src/Worker/SearchDirectory.php | 4 +-- 3 files changed, 7 insertions(+), 66 deletions(-) diff --git a/src/Core/Search.php b/src/Core/Search.php index 60137e66f7..ea979610e6 100644 --- a/src/Core/Search.php +++ b/src/Core/Search.php @@ -26,10 +26,8 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\GContact; use Friendica\Network\HTTPException; -use Friendica\Network\Probe; use Friendica\Object\Search\ContactResult; use Friendica\Object\Search\ResultList; -use Friendica\Protocol\PortableContact; use Friendica\Util\Network; use Friendica\Util\Strings; @@ -64,8 +62,7 @@ class Search if ((filter_var($user, FILTER_VALIDATE_EMAIL) && Network::isEmailDomainValid($user)) || (substr(Strings::normaliseLink($user), 0, 7) == "http://")) { - /// @todo Possibly use "getIdForURL" instead? - $user_data = Probe::uri($user); + $user_data = Contact::getByURL($user); if (empty($user_data)) { return $emptyResultList; } @@ -74,9 +71,6 @@ class Search return $emptyResultList; } - // Ensure that we do have a contact entry - Contact::getIdForURL($user_data['url'] ?? ''); - $contactDetails = Contact::getByURLForUser($user_data['url'] ?? '', local_user()); $result = new ContactResult( @@ -87,7 +81,7 @@ class Search $user_data['photo'] ?? '', $user_data['network'] ?? '', $contactDetails['id'] ?? 0, - 0, + $user_data['id'] ?? 0, $user_data['tags'] ?? '' ); diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 1ab8f9b800..0b95a3a19d 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -1617,59 +1617,6 @@ class OStatus return $source; } - /** - * Fetches contact data from the contact or the gcontact table - * - * @param string $url URL of the contact - * @param array $owner Contact data of the poster - * - * @return array Contact array - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - private static function contactEntry($url, array $owner) - { - $r = q( - "SELECT * FROM `contact` WHERE `nurl` = '%s' AND `uid` IN (0, %d) ORDER BY `uid` DESC LIMIT 1", - DBA::escape(Strings::normaliseLink($url)), - intval($owner["uid"]) - ); - if (DBA::isResult($r)) { - $contact = $r[0]; - $contact["uid"] = -1; - } - - if (!DBA::isResult($r)) { - $gcontact = DBA::selectFirst('gcontact', [], ['nurl' => Strings::normaliseLink($url)]); - if (DBA::isResult($r)) { - $contact = $gcontact; - $contact["uid"] = -1; - $contact["success_update"] = $contact["updated"]; - } - } - - if (!DBA::isResult($r)) { - $contact = $owner; - } - - if (!isset($contact["poll"])) { - $data = Probe::uri($url); - $contact["poll"] = $data["poll"]; - - if (!$contact["alias"]) { - $contact["alias"] = $data["alias"]; - } - } - - if (!isset($contact["alias"])) { - $contact["alias"] = $contact["url"]; - } - - $contact['account-type'] = $owner['account-type']; - - return $contact; - } - /** * Adds an entry element with reshared content * @@ -1699,7 +1646,7 @@ class OStatus return false; } - $contact = self::contactEntry($repeated_item['author-link'], $owner); + $contact = Contact::getByURL($repeated_item['author-link']) ?: $owner; $title = $owner["nick"]." repeated a notice by ".$contact["nick"]; @@ -1841,7 +1788,7 @@ class OStatus $item["created"] = $item["edited"] = date("c"); $item["private"] = Item::PRIVATE; - $contact = Probe::uri($item['follow']); + $contact = Contact::getByURL($item['follow']); $item['follow'] = $contact['url']; if ($contact['alias']) { @@ -1948,7 +1895,7 @@ class OStatus $entry = $doc->createElement("entry"); if ($owner['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) { - $contact = self::contactEntry($item['author-link'], $owner); + $contact = Contact::getByURL($item['author-link']) ?: $owner; $author = self::addAuthor($doc, $contact, false); $entry->appendChild($author); } diff --git a/src/Worker/SearchDirectory.php b/src/Worker/SearchDirectory.php index c099a5e28a..2ffe6120ec 100644 --- a/src/Worker/SearchDirectory.php +++ b/src/Worker/SearchDirectory.php @@ -27,9 +27,9 @@ use Friendica\Core\Protocol; use Friendica\Core\Search; use Friendica\Database\DBA; use Friendica\DI; +use Friendica\Model\Contact; use Friendica\Model\GContact; use Friendica\Model\GServer; -use Friendica\Network\Probe; use Friendica\Util\Network; use Friendica\Util\Strings; @@ -81,7 +81,7 @@ class SearchDirectory Logger::info('Friendica server seems to be okay.', ['server' => $server_url]); } - $data = Probe::uri($jj->url); + $data = Contact::getByURL($jj->url); if ($data['network'] == Protocol::DFRN) { Logger::info('Add profile to local directory', ['profile' => $jj->url]); From ecf6018b89cff66b2a26e48fcac6f3c6258b7aab Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 17 Jul 2020 04:40:20 +0000 Subject: [PATCH 033/573] Atom feed generation is moved to the feed class --- src/Module/Feed.php | 4 +- src/Protocol/Feed.php | 369 +++++++++++++++++++++++++++++++++++++++ src/Protocol/OStatus.php | 321 ++++++++++++++++------------------ 3 files changed, 518 insertions(+), 176 deletions(-) diff --git a/src/Module/Feed.php b/src/Module/Feed.php index ff0abdb2ab..0ccffbb96b 100644 --- a/src/Module/Feed.php +++ b/src/Module/Feed.php @@ -23,7 +23,7 @@ namespace Friendica\Module; use Friendica\BaseModule; use Friendica\DI; -use Friendica\Protocol\OStatus; +use Friendica\Protocol\Feed as ProtocolFeed; /** * Provides public Atom feeds @@ -75,7 +75,7 @@ class Feed extends BaseModule // @TODO: Replace with parameter from router $nickname = $a->argv[1]; header("Content-type: application/atom+xml; charset=utf-8"); - echo OStatus::feed($nickname, $last_update, 10, $type, $nocache, true); + echo ProtocolFeed::atom($nickname, $last_update, 10, $type, $nocache, true); exit(); } } diff --git a/src/Protocol/Feed.php b/src/Protocol/Feed.php index 63bcb9fb9e..1899f93bee 100644 --- a/src/Protocol/Feed.php +++ b/src/Protocol/Feed.php @@ -24,15 +24,21 @@ namespace Friendica\Protocol; use DOMDocument; use DOMXPath; use Friendica\Content\PageInfo; +use Friendica\Content\Text\BBCode; use Friendica\Content\Text\HTML; +use Friendica\Core\Cache\Duration; use Friendica\Core\Logger; use Friendica\Core\Protocol; use Friendica\Database\DBA; use Friendica\DI; +use Friendica\Model\Contact; use Friendica\Model\Item; use Friendica\Model\Tag; +use Friendica\Model\User; +use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; use Friendica\Util\ParseUrl; +use Friendica\Util\Strings; use Friendica\Util\XML; /** @@ -638,4 +644,367 @@ class Feed } return ($title == $body); } + + /** + * Creates the Atom feed for a given nickname + * + * Supported filters: + * - activity (default): all the public posts + * - posts: all the public top-level posts + * - comments: all the public replies + * + * Updates the provided last_update parameter if the result comes from the + * cache or it is empty + * + * @param string $owner_nick Nickname of the feed owner + * @param string $last_update Date of the last update + * @param integer $max_items Number of maximum items to fetch + * @param string $filter Feed items filter (activity, posts or comments) + * @param boolean $nocache Wether to bypass caching + * + * @return string Atom feed + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \ImagickException + */ + public static function atom($owner_nick, $last_update, $max_items = 300, $filter = 'activity', $nocache = false) + { + $stamp = microtime(true); + + $owner = User::getOwnerDataByNick($owner_nick); + if (!$owner) { + return; + } + + $cachekey = "feed:feed:" . $owner_nick . ":" . $filter . ":" . $last_update; + + $previous_created = $last_update; + + // Don't cache when the last item was posted less then 15 minutes ago (Cache duration) + if ((time() - strtotime($owner['last-item'])) < 15*60) { + $result = DI::cache()->get($cachekey); + if (!$nocache && !is_null($result)) { + Logger::info('Cached feed duration', ['seconds' => number_format(microtime(true) - $stamp, 3), 'nick' => $owner_nick, 'filter' => $filter, 'created' => $previous_created]); + return $result['feed']; + } + } + + $check_date = empty($last_update) ? '' : DateTimeFormat::utc($last_update); + $authorid = Contact::getIdForURL($owner["url"], 0, false); + + $condition = ["`uid` = ? AND `received` > ? AND NOT `deleted` AND `gravity` IN (?, ?) + AND `private` != ? AND `visible` AND `wall` AND `parent-network` IN (?, ?, ?, ?)", + $owner["uid"], $check_date, GRAVITY_PARENT, GRAVITY_COMMENT, + Item::PRIVATE, Protocol::ACTIVITYPUB, + Protocol::OSTATUS, Protocol::DFRN, Protocol::DIASPORA]; + + if ($filter === 'comments') { + $condition[0] .= " AND `object-type` = ? "; + $condition[] = Activity\ObjectType::COMMENT; + } + + if ($owner['account-type'] != User::ACCOUNT_TYPE_COMMUNITY) { + $condition[0] .= " AND `contact-id` = ? AND `author-id` = ?"; + $condition[] = $owner["id"]; + $condition[] = $authorid; + } + + $params = ['order' => ['received' => true], 'limit' => $max_items]; + + if ($filter === 'posts') { + $ret = Item::selectThread([], $condition, $params); + } else { + $ret = Item::select([], $condition, $params); + } + + $items = Item::inArray($ret); + + $doc = new DOMDocument('1.0', 'utf-8'); + $doc->formatOutput = true; + + $root = self::addHeader($doc, $owner, $filter); + + foreach ($items as $item) { + $entry = self::entry($doc, $item, $owner); + $root->appendChild($entry); + + if ($last_update < $item['created']) { + $last_update = $item['created']; + } + } + + $feeddata = trim($doc->saveXML()); + + $msg = ['feed' => $feeddata, 'last_update' => $last_update]; + DI::cache()->set($cachekey, $msg, Duration::QUARTER_HOUR); + + Logger::info('Feed duration', ['seconds' => number_format(microtime(true) - $stamp, 3), 'nick' => $owner_nick, 'filter' => $filter, 'created' => $previous_created]); + + return $feeddata; + } + + /** + * Adds the header elements to the XML document + * + * @param DOMDocument $doc XML document + * @param array $owner Contact data of the poster + * @param string $filter The related feed filter (activity, posts or comments) + * + * @return object header root element + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + private static function addHeader(DOMDocument $doc, array $owner, $filter) + { + $root = $doc->createElementNS(ActivityNamespace::ATOM1, 'feed'); + $doc->appendChild($root); + + $title = ''; + $selfUri = '/feed/' . $owner["nick"] . '/'; + switch ($filter) { + case 'activity': + $title = DI::l10n()->t('%s\'s timeline', $owner['name']); + $selfUri .= $filter; + break; + case 'posts': + $title = DI::l10n()->t('%s\'s posts', $owner['name']); + break; + case 'comments': + $title = DI::l10n()->t('%s\'s comments', $owner['name']); + $selfUri .= $filter; + break; + } + + $attributes = ["uri" => "https://friendi.ca", "version" => FRIENDICA_VERSION . "-" . DB_UPDATE_VERSION]; + XML::addElement($doc, $root, "generator", FRIENDICA_PLATFORM, $attributes); + XML::addElement($doc, $root, "id", DI::baseUrl() . "/profile/" . $owner["nick"]); + XML::addElement($doc, $root, "title", $title); + XML::addElement($doc, $root, "subtitle", sprintf("Updates from %s on %s", $owner["name"], DI::config()->get('config', 'sitename'))); + XML::addElement($doc, $root, "logo", $owner["photo"]); + XML::addElement($doc, $root, "updated", DateTimeFormat::utcNow(DateTimeFormat::ATOM)); + + $author = self::addAuthor($doc, $owner); + $root->appendChild($author); + + $attributes = ["href" => $owner["url"], "rel" => "alternate", "type" => "text/html"]; + XML::addElement($doc, $root, "link", "", $attributes); + + OStatus::hublinks($doc, $root, $owner["nick"]); + + $attributes = ["href" => DI::baseUrl() . $selfUri, "rel" => "self", "type" => "application/atom+xml"]; + XML::addElement($doc, $root, "link", "", $attributes); + + return $root; + } + + /** + * Adds the author element to the XML document + * + * @param DOMDocument $doc XML document + * @param array $owner Contact data of the poster + * + * @return \DOMElement author element + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + private static function addAuthor(DOMDocument $doc, array $owner) + { + $author = $doc->createElement("author"); + XML::addElement($doc, $author, "uri", $owner["url"]); + XML::addElement($doc, $author, "name", $owner["nick"]); + XML::addElement($doc, $author, "email", $owner["addr"]); + + return $author; + } + + /** + * Adds an entry element to the XML document + * + * @param DOMDocument $doc XML document + * @param array $item Data of the item that is to be posted + * @param array $owner Contact data of the poster + * @param bool $toplevel optional default false + * + * @return \DOMElement Entry element + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \ImagickException + */ + private static function entry(DOMDocument $doc, array $item, array $owner) + { + $xml = null; + + $repeated_guid = OStatus::getResharedGuid($item); + if ($repeated_guid != "") { + $xml = self::reshareEntry($doc, $item, $owner, $repeated_guid); + } + + if ($xml) { + return $xml; + } + + return self::noteEntry($doc, $item, $owner); + } + + /** + * Adds an entry element with reshared content + * + * @param DOMDocument $doc XML document + * @param array $item Data of the item that is to be posted + * @param array $owner Contact data of the poster + * @param string $repeated_guid guid + * @param bool $toplevel Is it for en entry element (false) or a feed entry (true)? + * + * @return bool Entry element + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \ImagickException + */ + private static function reshareEntry(DOMDocument $doc, array $item, array $owner, $repeated_guid) + { + if (($item['gravity'] != GRAVITY_PARENT) && (Strings::normaliseLink($item["author-link"]) != Strings::normaliseLink($owner["url"]))) { + Logger::info('Feed entry author does not match feed owner', ['owner' => $owner["url"], 'author' => $item["author-link"]]); + } + + $entry = OStatus::entryHeader($doc, $owner, $item, false); + + $condition = ['uid' => $owner["uid"], 'guid' => $repeated_guid, 'private' => [Item::PUBLIC, Item::UNLISTED], + 'network' => Protocol::FEDERATED]; + $repeated_item = Item::selectFirst([], $condition); + if (!DBA::isResult($repeated_item)) { + return false; + } + + $contact = Contact::getByURL($repeated_item['author-link']) ?: $owner; + + $title = $owner["nick"]." repeated a notice by ".$contact["nick"]; + + self::entryContent($doc, $entry, $item, $owner, $title, Activity::SHARE, false); + + self::entryFooter($doc, $entry, $item, $owner); + + return $entry; + } + + /** + * Adds a regular entry element + * + * @param DOMDocument $doc XML document + * @param array $item Data of the item that is to be posted + * @param array $owner Contact data of the poster + * @param bool $toplevel Is it for en entry element (false) or a feed entry (true)? + * + * @return \DOMElement Entry element + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \ImagickException + */ + private static function noteEntry(DOMDocument $doc, array $item, array $owner) + { + if (($item['gravity'] != GRAVITY_PARENT) && (Strings::normaliseLink($item["author-link"]) != Strings::normaliseLink($owner["url"]))) { + Logger::info('Feed entry author does not match feed owner', ['owner' => $owner["url"], 'author' => $item["author-link"]]); + } + + if (!empty($item['title'])) { + $title = BBCode::convert($item['title'], false, BBCode::OSTATUS); + } else { + $title = sprintf("New note by %s", $owner["nick"]); + } + + $entry = OStatus::entryHeader($doc, $owner, $item, false); + + self::entryContent($doc, $entry, $item, $title, '', true); + + self::entryFooter($doc, $entry, $item, $owner); + + return $entry; + } + + /** + * Adds elements to the XML document + * + * @param DOMDocument $doc XML document + * @param \DOMElement $entry Entry element where the content is added + * @param array $item Data of the item that is to be posted + * @param array $owner Contact data of the poster + * @param string $title Title for the post + * @param string $verb The activity verb + * @param bool $complete Add the "status_net" element? + * @param bool $feed_mode Behave like a regular feed for users if true + * @return void + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + private static function entryContent(DOMDocument $doc, \DOMElement $entry, array $item, $title, $verb = "", $complete = true) + { + if ($verb == "") { + $verb = OStatus::constructVerb($item); + } + + XML::addElement($doc, $entry, "id", $item["uri"]); + XML::addElement($doc, $entry, "title", html_entity_decode($title, ENT_QUOTES, 'UTF-8')); + + $body = OStatus::formatPicturePost($item['body']); + + $body = BBCode::convert($body, false, BBCode::OSTATUS); + + XML::addElement($doc, $entry, "content", $body, ["type" => "html"]); + + XML::addElement($doc, $entry, "link", "", ["rel" => "alternate", "type" => "text/html", + "href" => DI::baseUrl()."/display/".$item["guid"]] + ); + + XML::addElement($doc, $entry, "published", DateTimeFormat::utc($item["created"]."+00:00", DateTimeFormat::ATOM)); + XML::addElement($doc, $entry, "updated", DateTimeFormat::utc($item["edited"]."+00:00", DateTimeFormat::ATOM)); + } + + /** + * Adds the elements at the foot of an entry to the XML document + * + * @param DOMDocument $doc XML document + * @param object $entry The entry element where the elements are added + * @param array $item Data of the item that is to be posted + * @param array $owner Contact data of the poster + * @param bool $complete default true + * @return void + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + private static function entryFooter(DOMDocument $doc, $entry, array $item, array $owner) + { + $mentioned = []; + + if ($item['gravity'] != GRAVITY_PARENT) { + $parent = Item::selectFirst(['guid', 'author-link', 'owner-link'], ['id' => $item['parent']]); + $parent_item = (($item['thr-parent']) ? $item['thr-parent'] : $item['parent-uri']); + + $thrparent = Item::selectFirst(['guid', 'author-link', 'owner-link', 'plink'], ['uid' => $owner["uid"], 'uri' => $parent_item]); + + if (DBA::isResult($thrparent)) { + $mentioned[$thrparent["author-link"]] = $thrparent["author-link"]; + $mentioned[$thrparent["owner-link"]] = $thrparent["owner-link"]; + $parent_plink = $thrparent["plink"]; + } else { + $mentioned[$parent["author-link"]] = $parent["author-link"]; + $mentioned[$parent["owner-link"]] = $parent["owner-link"]; + $parent_plink = DI::baseUrl()."/display/".$parent["guid"]; + } + + $attributes = [ + "ref" => $parent_item, + "href" => $parent_plink]; + XML::addElement($doc, $entry, "thr:in-reply-to", "", $attributes); + + $attributes = [ + "rel" => "related", + "href" => $parent_plink]; + XML::addElement($doc, $entry, "link", "", $attributes); + } + + // uri-id isn't present for follow entry pseudo-items + $tags = Tag::getByURIId($item['uri-id'] ?? 0); + foreach ($tags as $tag) { + $mentioned[$tag['url']] = $tag['url']; + } + + foreach ($tags as $tag) { + if ($tag['type'] == Tag::HASHTAG) { + XML::addElement($doc, $entry, "category", "", ["term" => $tag['name']]); + } + } + + OStatus::getAttachment($doc, $entry, $item); + } } diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 0b95a3a19d..d496389c58 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -1208,7 +1208,7 @@ class OStatus * * @return string The guid if the post is a reshare */ - private static function getResharedGuid(array $item) + public static function getResharedGuid(array $item) { $reshared = Item::getShareArray($item); if (empty($reshared['guid']) || !empty($reshared['comment'])) { @@ -1226,7 +1226,7 @@ class OStatus * @return string The cleaned body * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - private static function formatPicturePost($body) + public static function formatPicturePost($body) { $siteinfo = BBCode::getAttachedData($body); @@ -1262,26 +1262,23 @@ class OStatus * @param DOMDocument $doc XML document * @param array $owner Contact data of the poster * @param string $filter The related feed filter (activity, posts or comments) - * @param bool $feed_mode Behave like a regular feed for users if true * * @return object header root element * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - private static function addHeader(DOMDocument $doc, array $owner, $filter, $feed_mode = false) + private static function addHeader(DOMDocument $doc, array $owner, $filter) { $root = $doc->createElementNS(ActivityNamespace::ATOM1, 'feed'); $doc->appendChild($root); - if (!$feed_mode) { - $root->setAttribute("xmlns:thr", ActivityNamespace::THREAD); - $root->setAttribute("xmlns:georss", ActivityNamespace::GEORSS); - $root->setAttribute("xmlns:activity", ActivityNamespace::ACTIVITY); - $root->setAttribute("xmlns:media", ActivityNamespace::MEDIA); - $root->setAttribute("xmlns:poco", ActivityNamespace::POCO); - $root->setAttribute("xmlns:ostatus", ActivityNamespace::OSTATUS); - $root->setAttribute("xmlns:statusnet", ActivityNamespace::STATUSNET); - $root->setAttribute("xmlns:mastodon", ActivityNamespace::MASTODON); - } + $root->setAttribute("xmlns:thr", ActivityNamespace::THREAD); + $root->setAttribute("xmlns:georss", ActivityNamespace::GEORSS); + $root->setAttribute("xmlns:activity", ActivityNamespace::ACTIVITY); + $root->setAttribute("xmlns:media", ActivityNamespace::MEDIA); + $root->setAttribute("xmlns:poco", ActivityNamespace::POCO); + $root->setAttribute("xmlns:ostatus", ActivityNamespace::OSTATUS); + $root->setAttribute("xmlns:statusnet", ActivityNamespace::STATUSNET); + $root->setAttribute("xmlns:mastodon", ActivityNamespace::MASTODON); $title = ''; $selfUri = '/feed/' . $owner["nick"] . '/'; @@ -1299,9 +1296,7 @@ class OStatus break; } - if (!$feed_mode) { - $selfUri = "/dfrn_poll/" . $owner["nick"]; - } + $selfUri = "/dfrn_poll/" . $owner["nick"]; $attributes = ["uri" => "https://friendi.ca", "version" => FRIENDICA_VERSION . "-" . DB_UPDATE_VERSION]; XML::addElement($doc, $root, "generator", FRIENDICA_PLATFORM, $attributes); @@ -1311,7 +1306,7 @@ class OStatus XML::addElement($doc, $root, "logo", $owner["photo"]); XML::addElement($doc, $root, "updated", DateTimeFormat::utcNow(DateTimeFormat::ATOM)); - $author = self::addAuthor($doc, $owner, true, $feed_mode); + $author = self::addAuthor($doc, $owner, true); $root->appendChild($author); $attributes = ["href" => $owner["url"], "rel" => "alternate", "type" => "text/html"]; @@ -1325,16 +1320,14 @@ class OStatus self::hublinks($doc, $root, $owner["nick"]); - if (!$feed_mode) { - $attributes = ["href" => DI::baseUrl() . "/salmon/" . $owner["nick"], "rel" => "salmon"]; - XML::addElement($doc, $root, "link", "", $attributes); + $attributes = ["href" => DI::baseUrl() . "/salmon/" . $owner["nick"], "rel" => "salmon"]; + XML::addElement($doc, $root, "link", "", $attributes); - $attributes = ["href" => DI::baseUrl() . "/salmon/" . $owner["nick"], "rel" => "http://salmon-protocol.org/ns/salmon-replies"]; - XML::addElement($doc, $root, "link", "", $attributes); + $attributes = ["href" => DI::baseUrl() . "/salmon/" . $owner["nick"], "rel" => "http://salmon-protocol.org/ns/salmon-replies"]; + XML::addElement($doc, $root, "link", "", $attributes); - $attributes = ["href" => DI::baseUrl() . "/salmon/" . $owner["nick"], "rel" => "http://salmon-protocol.org/ns/salmon-mention"]; - XML::addElement($doc, $root, "link", "", $attributes); - } + $attributes = ["href" => DI::baseUrl() . "/salmon/" . $owner["nick"], "rel" => "http://salmon-protocol.org/ns/salmon-mention"]; + XML::addElement($doc, $root, "link", "", $attributes); $attributes = ["href" => DI::baseUrl() . $selfUri, "rel" => "self", "type" => "application/atom+xml"]; XML::addElement($doc, $root, "link", "", $attributes); @@ -1373,7 +1366,7 @@ class OStatus * @return void * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - private static function getAttachment(DOMDocument $doc, $root, $item) + public static function getAttachment(DOMDocument $doc, $root, $item) { $siteinfo = BBCode::getAttachedData($item["body"]); @@ -1443,79 +1436,75 @@ class OStatus * @param DOMDocument $doc XML document * @param array $owner Contact data of the poster * @param bool $show_profile Whether to show profile - * @param bool $feed_mode Behave like a regular feed for users if true * * @return \DOMElement author element * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - private static function addAuthor(DOMDocument $doc, array $owner, $show_profile = true, $feed_mode = false) + private static function addAuthor(DOMDocument $doc, array $owner, $show_profile = true) { $profile = DBA::selectFirst('profile', ['homepage', 'publish'], ['uid' => $owner['uid']]); $author = $doc->createElement("author"); - if (!$feed_mode) { - XML::addElement($doc, $author, "id", $owner["url"]); - if ($owner['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) { - XML::addElement($doc, $author, "activity:object-type", Activity\ObjectType::GROUP); - } else { - XML::addElement($doc, $author, "activity:object-type", Activity\ObjectType::PERSON); - } + XML::addElement($doc, $author, "id", $owner["url"]); + if ($owner['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) { + XML::addElement($doc, $author, "activity:object-type", Activity\ObjectType::GROUP); + } else { + XML::addElement($doc, $author, "activity:object-type", Activity\ObjectType::PERSON); } + XML::addElement($doc, $author, "uri", $owner["url"]); XML::addElement($doc, $author, "name", $owner["nick"]); XML::addElement($doc, $author, "email", $owner["addr"]); - if ($show_profile && !$feed_mode) { + if ($show_profile) { XML::addElement($doc, $author, "summary", BBCode::convert($owner["about"], false, BBCode::OSTATUS)); } - if (!$feed_mode) { - $attributes = ["rel" => "alternate", "type" => "text/html", "href" => $owner["url"]]; - XML::addElement($doc, $author, "link", "", $attributes); + $attributes = ["rel" => "alternate", "type" => "text/html", "href" => $owner["url"]]; + XML::addElement($doc, $author, "link", "", $attributes); + $attributes = [ + "rel" => "avatar", + "type" => "image/jpeg", // To-Do? + "media:width" => 300, + "media:height" => 300, + "href" => $owner["photo"]]; + XML::addElement($doc, $author, "link", "", $attributes); + + if (isset($owner["thumb"])) { $attributes = [ "rel" => "avatar", "type" => "image/jpeg", // To-Do? - "media:width" => 300, - "media:height" => 300, - "href" => $owner["photo"]]; + "media:width" => 80, + "media:height" => 80, + "href" => $owner["thumb"]]; XML::addElement($doc, $author, "link", "", $attributes); + } - if (isset($owner["thumb"])) { - $attributes = [ - "rel" => "avatar", - "type" => "image/jpeg", // To-Do? - "media:width" => 80, - "media:height" => 80, - "href" => $owner["thumb"]]; - XML::addElement($doc, $author, "link", "", $attributes); + XML::addElement($doc, $author, "poco:preferredUsername", $owner["nick"]); + XML::addElement($doc, $author, "poco:displayName", $owner["name"]); + if ($show_profile) { + XML::addElement($doc, $author, "poco:note", BBCode::convert($owner["about"], false, BBCode::OSTATUS)); + + if (trim($owner["location"]) != "") { + $element = $doc->createElement("poco:address"); + XML::addElement($doc, $element, "poco:formatted", $owner["location"]); + $author->appendChild($element); + } + } + + if (DBA::isResult($profile) && !$show_profile) { + if (trim($profile["homepage"]) != "") { + $urls = $doc->createElement("poco:urls"); + XML::addElement($doc, $urls, "poco:type", "homepage"); + XML::addElement($doc, $urls, "poco:value", $profile["homepage"]); + XML::addElement($doc, $urls, "poco:primary", "true"); + $author->appendChild($urls); } - XML::addElement($doc, $author, "poco:preferredUsername", $owner["nick"]); - XML::addElement($doc, $author, "poco:displayName", $owner["name"]); - if ($show_profile) { - XML::addElement($doc, $author, "poco:note", BBCode::convert($owner["about"], false, BBCode::OSTATUS)); + XML::addElement($doc, $author, "followers", "", ["url" => DI::baseUrl() . "/profile/" . $owner["nick"] . "/contacts/followers"]); + XML::addElement($doc, $author, "statusnet:profile_info", "", ["local_id" => $owner["uid"]]); - if (trim($owner["location"]) != "") { - $element = $doc->createElement("poco:address"); - XML::addElement($doc, $element, "poco:formatted", $owner["location"]); - $author->appendChild($element); - } - } - - if (DBA::isResult($profile) && !$show_profile) { - if (trim($profile["homepage"]) != "") { - $urls = $doc->createElement("poco:urls"); - XML::addElement($doc, $urls, "poco:type", "homepage"); - XML::addElement($doc, $urls, "poco:value", $profile["homepage"]); - XML::addElement($doc, $urls, "poco:primary", "true"); - $author->appendChild($urls); - } - - XML::addElement($doc, $author, "followers", "", ["url" => DI::baseUrl() . "/profile/" . $owner["nick"] . "/contacts/followers"]); - XML::addElement($doc, $author, "statusnet:profile_info", "", ["local_id" => $owner["uid"]]); - - if ($profile["publish"]) { - XML::addElement($doc, $author, "mastodon:scope", "public"); - } + if ($profile["publish"]) { + XML::addElement($doc, $author, "mastodon:scope", "public"); } } @@ -1535,7 +1524,7 @@ class OStatus * * @return string activity */ - private static function constructVerb(array $item) + public static function constructVerb(array $item) { if (!empty($item['verb'])) { return $item['verb']; @@ -1567,19 +1556,18 @@ class OStatus * @param array $item Data of the item that is to be posted * @param array $owner Contact data of the poster * @param bool $toplevel optional default false - * @param bool $feed_mode Behave like a regular feed for users if true * * @return \DOMElement Entry element * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - private static function entry(DOMDocument $doc, array $item, array $owner, $toplevel = false, $feed_mode = false) + private static function entry(DOMDocument $doc, array $item, array $owner, $toplevel = false) { $xml = null; $repeated_guid = self::getResharedGuid($item); if ($repeated_guid != "") { - $xml = self::reshareEntry($doc, $item, $owner, $repeated_guid, $toplevel, $feed_mode); + $xml = self::reshareEntry($doc, $item, $owner, $repeated_guid, $toplevel); } if ($xml) { @@ -1591,7 +1579,7 @@ class OStatus } elseif (in_array($item["verb"], [Activity::FOLLOW, Activity::O_UNFOLLOW])) { return self::followEntry($doc, $item, $owner, $toplevel); } else { - return self::noteEntry($doc, $item, $owner, $toplevel, $feed_mode); + return self::noteEntry($doc, $item, $owner, $toplevel); } } @@ -1625,13 +1613,12 @@ class OStatus * @param array $owner Contact data of the poster * @param string $repeated_guid guid * @param bool $toplevel Is it for en entry element (false) or a feed entry (true)? - * @param bool $feed_mode Behave like a regular feed for users if true * * @return bool Entry element * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - private static function reshareEntry(DOMDocument $doc, array $item, array $owner, $repeated_guid, $toplevel, $feed_mode = false) + private static function reshareEntry(DOMDocument $doc, array $item, array $owner, $repeated_guid, $toplevel) { if (($item['gravity'] != GRAVITY_PARENT) && (Strings::normaliseLink($item["author-link"]) != Strings::normaliseLink($owner["url"]))) { Logger::log("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", Logger::DEBUG); @@ -1650,38 +1637,36 @@ class OStatus $title = $owner["nick"]." repeated a notice by ".$contact["nick"]; - self::entryContent($doc, $entry, $item, $owner, $title, Activity::SHARE, false, $feed_mode); + self::entryContent($doc, $entry, $item, $owner, $title, Activity::SHARE, false); - if (!$feed_mode) { - $as_object = $doc->createElement("activity:object"); + $as_object = $doc->createElement("activity:object"); - XML::addElement($doc, $as_object, "activity:object-type", ActivityNamespace::ACTIVITY_SCHEMA . "activity"); + XML::addElement($doc, $as_object, "activity:object-type", ActivityNamespace::ACTIVITY_SCHEMA . "activity"); - self::entryContent($doc, $as_object, $repeated_item, $owner, "", "", false); + self::entryContent($doc, $as_object, $repeated_item, $owner, "", "", false); - $author = self::addAuthor($doc, $contact, false); - $as_object->appendChild($author); + $author = self::addAuthor($doc, $contact, false); + $as_object->appendChild($author); - $as_object2 = $doc->createElement("activity:object"); + $as_object2 = $doc->createElement("activity:object"); - XML::addElement($doc, $as_object2, "activity:object-type", self::constructObjecttype($repeated_item)); + XML::addElement($doc, $as_object2, "activity:object-type", self::constructObjecttype($repeated_item)); - $title = sprintf("New comment by %s", $contact["nick"]); + $title = sprintf("New comment by %s", $contact["nick"]); - self::entryContent($doc, $as_object2, $repeated_item, $owner, $title); + self::entryContent($doc, $as_object2, $repeated_item, $owner, $title); - $as_object->appendChild($as_object2); + $as_object->appendChild($as_object2); - self::entryFooter($doc, $as_object, $item, $owner, false); + self::entryFooter($doc, $as_object, $item, $owner, false); - $source = self::sourceEntry($doc, $contact); + $source = self::sourceEntry($doc, $contact); - $as_object->appendChild($source); + $as_object->appendChild($source); - $entry->appendChild($as_object); - } + $entry->appendChild($as_object); - self::entryFooter($doc, $entry, $item, $owner, true, $feed_mode); + self::entryFooter($doc, $entry, $item, $owner, true); return $entry; } @@ -1842,13 +1827,12 @@ class OStatus * @param array $item Data of the item that is to be posted * @param array $owner Contact data of the poster * @param bool $toplevel Is it for en entry element (false) or a feed entry (true)? - * @param bool $feed_mode Behave like a regular feed for users if true * * @return \DOMElement Entry element * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - private static function noteEntry(DOMDocument $doc, array $item, array $owner, $toplevel, $feed_mode) + private static function noteEntry(DOMDocument $doc, array $item, array $owner, $toplevel) { if (($item['gravity'] != GRAVITY_PARENT) && (Strings::normaliseLink($item["author-link"]) != Strings::normaliseLink($owner["url"]))) { Logger::log("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", Logger::DEBUG); @@ -1866,13 +1850,11 @@ class OStatus $entry = self::entryHeader($doc, $owner, $item, $toplevel); - if (!$feed_mode) { - XML::addElement($doc, $entry, "activity:object-type", Activity\ObjectType::NOTE); - } + XML::addElement($doc, $entry, "activity:object-type", Activity\ObjectType::NOTE); - self::entryContent($doc, $entry, $item, $owner, $title, '', true, $feed_mode); + self::entryContent($doc, $entry, $item, $owner, $title, '', true); - self::entryFooter($doc, $entry, $item, $owner, !$feed_mode, $feed_mode); + self::entryFooter($doc, $entry, $item, $owner, true); return $entry; } @@ -1889,7 +1871,7 @@ class OStatus * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - private static function entryHeader(DOMDocument $doc, array $owner, array $item, $toplevel) + public static function entryHeader(DOMDocument $doc, array $owner, array $item, $toplevel) { if (!$toplevel) { $entry = $doc->createElement("entry"); @@ -1928,11 +1910,10 @@ class OStatus * @param string $title Title for the post * @param string $verb The activity verb * @param bool $complete Add the "status_net" element? - * @param bool $feed_mode Behave like a regular feed for users if true * @return void * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - private static function entryContent(DOMDocument $doc, \DOMElement $entry, array $item, array $owner, $title, $verb = "", $complete = true, $feed_mode = false) + private static function entryContent(DOMDocument $doc, \DOMElement $entry, array $item, array $owner, $title, $verb = "", $complete = true) { if ($verb == "") { $verb = self::constructVerb($item); @@ -1943,7 +1924,7 @@ class OStatus $body = self::formatPicturePost($item['body']); - if (!empty($item['title']) && !$feed_mode) { + if (!empty($item['title'])) { $body = "[b]".$item['title']."[/b]\n\n".$body; } @@ -1955,13 +1936,11 @@ class OStatus "href" => DI::baseUrl()."/display/".$item["guid"]] ); - if (!$feed_mode && $complete && ($item["id"] > 0)) { + if ($complete && ($item["id"] > 0)) { XML::addElement($doc, $entry, "status_net", "", ["notice_id" => $item["id"]]); } - if (!$feed_mode) { - XML::addElement($doc, $entry, "activity:verb", $verb); - } + XML::addElement($doc, $entry, "activity:verb", $verb); XML::addElement($doc, $entry, "published", DateTimeFormat::utc($item["created"]."+00:00", DateTimeFormat::ATOM)); XML::addElement($doc, $entry, "updated", DateTimeFormat::utc($item["edited"]."+00:00", DateTimeFormat::ATOM)); @@ -1975,11 +1954,10 @@ class OStatus * @param array $item Data of the item that is to be posted * @param array $owner Contact data of the poster * @param bool $complete default true - * @param bool $feed_mode Behave like a regular feed for users if true * @return void * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - private static function entryFooter(DOMDocument $doc, $entry, array $item, array $owner, $complete = true, $feed_mode = false) + private static function entryFooter(DOMDocument $doc, $entry, array $item, array $owner, $complete = true) { $mentioned = []; @@ -2010,7 +1988,7 @@ class OStatus XML::addElement($doc, $entry, "link", "", $attributes); } - if (!$feed_mode && (intval($item['parent']) > 0)) { + if (intval($item['parent']) > 0) { $conversation_href = $conversation_uri = str_replace('/objects/', '/context/', $item['parent-uri']); if (isset($parent_item)) { @@ -2025,16 +2003,14 @@ class OStatus } } - if (!$feed_mode) { - XML::addElement($doc, $entry, "link", "", ["rel" => "ostatus:conversation", "href" => $conversation_href]); + XML::addElement($doc, $entry, "link", "", ["rel" => "ostatus:conversation", "href" => $conversation_href]); - $attributes = [ - "href" => $conversation_href, - "local_id" => $item['parent'], - "ref" => $conversation_uri]; + $attributes = [ + "href" => $conversation_href, + "local_id" => $item['parent'], + "ref" => $conversation_uri]; - XML::addElement($doc, $entry, "ostatus:conversation", $conversation_uri, $attributes); - } + XML::addElement($doc, $entry, "ostatus:conversation", $conversation_uri, $attributes); } // uri-id isn't present for follow entry pseudo-items @@ -2043,50 +2019,48 @@ class OStatus $mentioned[$tag['url']] = $tag['url']; } - if (!$feed_mode) { - // Make sure that mentions are accepted (GNU Social has problems with mixing HTTP and HTTPS) - $newmentions = []; - foreach ($mentioned as $mention) { - $newmentions[str_replace("http://", "https://", $mention)] = str_replace("http://", "https://", $mention); - $newmentions[str_replace("https://", "http://", $mention)] = str_replace("https://", "http://", $mention); - } - $mentioned = $newmentions; + // Make sure that mentions are accepted (GNU Social has problems with mixing HTTP and HTTPS) + $newmentions = []; + foreach ($mentioned as $mention) { + $newmentions[str_replace("http://", "https://", $mention)] = str_replace("http://", "https://", $mention); + $newmentions[str_replace("https://", "http://", $mention)] = str_replace("https://", "http://", $mention); + } + $mentioned = $newmentions; - foreach ($mentioned as $mention) { - $contact = Contact::getByURL($mention, ['contact-type']); - if (!empty($contact) && ($contact['contact-type'] == Contact::TYPE_COMMUNITY)) { - XML::addElement($doc, $entry, "link", "", - [ - "rel" => "mentioned", - "ostatus:object-type" => Activity\ObjectType::GROUP, - "href" => $mention] - ); - } else { - XML::addElement($doc, $entry, "link", "", - [ - "rel" => "mentioned", - "ostatus:object-type" => Activity\ObjectType::PERSON, - "href" => $mention] - ); - } + foreach ($mentioned as $mention) { + $contact = Contact::getByURL($mention, ['contact-type']); + if (!empty($contact) && ($contact['contact-type'] == Contact::TYPE_COMMUNITY)) { + XML::addElement($doc, $entry, "link", "", + [ + "rel" => "mentioned", + "ostatus:object-type" => Activity\ObjectType::GROUP, + "href" => $mention] + ); + } else { + XML::addElement($doc, $entry, "link", "", + [ + "rel" => "mentioned", + "ostatus:object-type" => Activity\ObjectType::PERSON, + "href" => $mention] + ); } + } - if ($owner['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) { - XML::addElement($doc, $entry, "link", "", [ - "rel" => "mentioned", - "ostatus:object-type" => "http://activitystrea.ms/schema/1.0/group", - "href" => $owner['url'] - ]); - } + if ($owner['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) { + XML::addElement($doc, $entry, "link", "", [ + "rel" => "mentioned", + "ostatus:object-type" => "http://activitystrea.ms/schema/1.0/group", + "href" => $owner['url'] + ]); + } - if ($item['private'] != Item::PRIVATE) { - XML::addElement($doc, $entry, "link", "", ["rel" => "ostatus:attention", - "href" => "http://activityschema.org/collection/public"]); - XML::addElement($doc, $entry, "link", "", ["rel" => "mentioned", - "ostatus:object-type" => "http://activitystrea.ms/schema/1.0/collection", - "href" => "http://activityschema.org/collection/public"]); - XML::addElement($doc, $entry, "mastodon:scope", "public"); - } + if ($item['private'] != Item::PRIVATE) { + XML::addElement($doc, $entry, "link", "", ["rel" => "ostatus:attention", + "href" => "http://activityschema.org/collection/public"]); + XML::addElement($doc, $entry, "link", "", ["rel" => "mentioned", + "ostatus:object-type" => "http://activitystrea.ms/schema/1.0/collection", + "href" => "http://activityschema.org/collection/public"]); + XML::addElement($doc, $entry, "mastodon:scope", "public"); } foreach ($tags as $tag) { @@ -2097,7 +2071,7 @@ class OStatus self::getAttachment($doc, $entry, $item); - if (!$feed_mode && $complete && ($item["id"] > 0)) { + if ($complete && ($item["id"] > 0)) { $app = $item["app"]; if ($app == "") { $app = "web"; @@ -2133,13 +2107,12 @@ class OStatus * @param integer $max_items Number of maximum items to fetch * @param string $filter Feed items filter (activity, posts or comments) * @param boolean $nocache Wether to bypass caching - * @param boolean $feed_mode Behave like a regular feed for users if true * * @return string XML feed * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function feed($owner_nick, &$last_update, $max_items = 300, $filter = 'activity', $nocache = false, $feed_mode = false) + public static function feed($owner_nick, &$last_update, $max_items = 300, $filter = 'activity', $nocache = false) { $stamp = microtime(true); @@ -2166,7 +2139,7 @@ class OStatus $last_update = 'now -30 days'; } - $check_date = $feed_mode ? '' : DateTimeFormat::utc($last_update); + $check_date = DateTimeFormat::utc($last_update); $authorid = Contact::getIdForURL($owner["url"], 0, false); $condition = ["`uid` = ? AND `received` > ? AND NOT `deleted` @@ -2197,7 +2170,7 @@ class OStatus $doc = new DOMDocument('1.0', 'utf-8'); $doc->formatOutput = true; - $root = self::addHeader($doc, $owner, $filter, $feed_mode); + $root = self::addHeader($doc, $owner, $filter); foreach ($items as $item) { if (DI::config()->get('system', 'ostatus_debug')) { @@ -2208,7 +2181,7 @@ class OStatus continue; } - $entry = self::entry($doc, $item, $owner, false, $feed_mode); + $entry = self::entry($doc, $item, $owner, false); $root->appendChild($entry); if ($last_update < $item['created']) { From c47931172dace4f0e1e9e03b782d667ff62b2a03 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 17 Jul 2020 04:46:42 +0000 Subject: [PATCH 034/573] Indention fixed --- src/Protocol/Feed.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Protocol/Feed.php b/src/Protocol/Feed.php index 1899f93bee..d2ab2559d3 100644 --- a/src/Protocol/Feed.php +++ b/src/Protocol/Feed.php @@ -645,7 +645,7 @@ class Feed return ($title == $body); } - /** + /** * Creates the Atom feed for a given nickname * * Supported filters: From 0b93bcbbf9f7ff5678952f1f88c0673b2c677454 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 17 Jul 2020 05:27:45 +0000 Subject: [PATCH 035/573] Create a (meaningful) title --- src/Protocol/Feed.php | 47 ++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/src/Protocol/Feed.php b/src/Protocol/Feed.php index d2ab2559d3..a665b7c85a 100644 --- a/src/Protocol/Feed.php +++ b/src/Protocol/Feed.php @@ -870,11 +870,7 @@ class Feed return false; } - $contact = Contact::getByURL($repeated_item['author-link']) ?: $owner; - - $title = $owner["nick"]." repeated a notice by ".$contact["nick"]; - - self::entryContent($doc, $entry, $item, $owner, $title, Activity::SHARE, false); + self::entryContent($doc, $entry, $item, self::getTitle($repeated_item), Activity::SHARE, false); self::entryFooter($doc, $entry, $item, $owner); @@ -899,15 +895,9 @@ class Feed Logger::info('Feed entry author does not match feed owner', ['owner' => $owner["url"], 'author' => $item["author-link"]]); } - if (!empty($item['title'])) { - $title = BBCode::convert($item['title'], false, BBCode::OSTATUS); - } else { - $title = sprintf("New note by %s", $owner["nick"]); - } - $entry = OStatus::entryHeader($doc, $owner, $item, false); - self::entryContent($doc, $entry, $item, $title, '', true); + self::entryContent($doc, $entry, $item, self::getTitle($item), '', true); self::entryFooter($doc, $entry, $item, $owner); @@ -1007,4 +997,37 @@ class Feed OStatus::getAttachment($doc, $entry, $item); } + + /** + * Fetch or create title for feed entry + * + * @param array $item + * @return string title + */ + private static function getTitle(array $item) + { + if ($item['title'] != '') { + return BBCode::convert($item['title'], false, BBCode::OSTATUS); + } + + // Fetch information about the post + $siteinfo = BBCode::getAttachedData($item["body"]); + if (isset($siteinfo["title"])) { + return $siteinfo["title"]; + } + + // If no bookmark is found then take the first line + // Remove the share element before fetching the first line + $title = trim(preg_replace("/\[share.*?\](.*?)\[\/share\]/ism","\n$1\n",$item['body'])); + + $title = HTML::toPlaintext(BBCode::convert($title, false), 0, true)."\n"; + $pos = strpos($title, "\n"); + $trailer = ""; + if (($pos == 0) || ($pos > 100)) { + $pos = 100; + $trailer = "..."; + } + + return substr($title, 0, $pos) . $trailer; + } } From c987785146887240f14d97367689901630c2e91e Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 17 Jul 2020 06:58:39 +0000 Subject: [PATCH 036/573] Use "contact-type" instead of "account-type" to avoid notices --- src/Protocol/OStatus.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index d496389c58..fedf0f2533 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -1332,7 +1332,7 @@ class OStatus $attributes = ["href" => DI::baseUrl() . $selfUri, "rel" => "self", "type" => "application/atom+xml"]; XML::addElement($doc, $root, "link", "", $attributes); - if ($owner['account-type'] == Contact::TYPE_COMMUNITY) { + if ($owner['contact-type'] == Contact::TYPE_COMMUNITY) { $condition = ['uid' => $owner['uid'], 'self' => false, 'pending' => false, 'archive' => false, 'hidden' => false, 'blocked' => false]; $members = DBA::count('contact', $condition); @@ -1445,7 +1445,7 @@ class OStatus $profile = DBA::selectFirst('profile', ['homepage', 'publish'], ['uid' => $owner['uid']]); $author = $doc->createElement("author"); XML::addElement($doc, $author, "id", $owner["url"]); - if ($owner['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) { + if ($owner['contact-type'] == Contact::TYPE_COMMUNITY) { XML::addElement($doc, $author, "activity:object-type", Activity\ObjectType::GROUP); } else { XML::addElement($doc, $author, "activity:object-type", Activity\ObjectType::PERSON); @@ -1876,7 +1876,7 @@ class OStatus if (!$toplevel) { $entry = $doc->createElement("entry"); - if ($owner['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) { + if ($owner['contact-type'] == Contact::TYPE_COMMUNITY) { $contact = Contact::getByURL($item['author-link']) ?: $owner; $author = self::addAuthor($doc, $contact, false); $entry->appendChild($author); @@ -2046,7 +2046,7 @@ class OStatus } } - if ($owner['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) { + if ($owner['contact-type'] == Contact::TYPE_COMMUNITY) { XML::addElement($doc, $entry, "link", "", [ "rel" => "mentioned", "ostatus:object-type" => "http://activitystrea.ms/schema/1.0/group", @@ -2151,7 +2151,7 @@ class OStatus $condition[] = Activity\ObjectType::COMMENT; } - if ($owner['account-type'] != User::ACCOUNT_TYPE_COMMUNITY) { + if ($owner['contact-type'] != Contact::TYPE_COMMUNITY) { $condition[0] .= " AND `contact-id` = ? AND `author-id` = ?"; $condition[] = $owner["id"]; $condition[] = $authorid; From da5045667527a84d6a3fd6efaee2fb97b9a49778 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 17 Jul 2020 17:14:13 -0400 Subject: [PATCH 037/573] Add Twitter source debug to Debug\Babel --- src/Module/Debug/Babel.php | 59 ++++++++++++++++++++++++++++++++++++++ view/templates/babel.tpl | 3 ++ 2 files changed, 62 insertions(+) diff --git a/src/Module/Debug/Babel.php b/src/Module/Debug/Babel.php index 2954bc010c..ab68f2b403 100644 --- a/src/Module/Debug/Babel.php +++ b/src/Module/Debug/Babel.php @@ -24,9 +24,12 @@ namespace Friendica\Module\Debug; use Friendica\BaseModule; use Friendica\Content\PageInfo; use Friendica\Content\Text; +use Friendica\Core\Protocol; use Friendica\Core\Renderer; use Friendica\DI; +use Friendica\Model\Conversation; use Friendica\Model\Item; +use Friendica\Protocol\Activity; use Friendica\Model\Tag; use Friendica\Util\XML; @@ -215,6 +218,60 @@ class Babel extends BaseModule 'title' => DI::l10n()->t('HTML::toPlaintext (compact)'), 'content' => visible_whitespace($text), ]; + break; + case 'twitter': + $json = trim($_REQUEST['text']); + + $status = json_decode($json); + + $results[] = [ + 'title' => DI::l10n()->t('Decoded post'), + 'content' => visible_whitespace(var_export($status, true)), + ]; + + $postarray = []; + $postarray['object-type'] = Activity\ObjectType::NOTE; + + if (!empty($status->full_text)) { + $postarray['body'] = $status->full_text; + } else { + $postarray['body'] = $status->text; + } + + // When the post contains links then use the correct object type + if (count($status->entities->urls) > 0) { + $postarray['object-type'] = Activity\ObjectType::BOOKMARK; + } + + if (file_exists('addon/twitter/twitter.php')) { + require_once 'addon/twitter/twitter.php'; + + $picture = \twitter_media_entities($status, $postarray); + + $results[] = [ + 'title' => DI::l10n()->t('Post array before expand entities'), + 'content' => visible_whitespace(var_export($postarray, true)), + ]; + + $converted = \twitter_expand_entities($postarray['body'], $status, $picture); + + $results[] = [ + 'title' => DI::l10n()->t('Post converted'), + 'content' => visible_whitespace(var_export($converted, true)), + ]; + + $results[] = [ + 'title' => DI::l10n()->t('Converted body'), + 'content' => visible_whitespace($converted['body']), + ]; + } else { + $results[] = [ + 'title' => DI::l10n()->t('Error'), + 'content' => DI::l10n()->t('Twitter addon is absent from the addon/ folder.'), + ]; + } + + break; } } @@ -225,6 +282,8 @@ class Babel extends BaseModule '$type_diaspora' => ['type', DI::l10n()->t('Diaspora'), 'diaspora', '', (($_REQUEST['type'] ?? '') ?: 'bbcode') == 'diaspora'], '$type_markdown' => ['type', DI::l10n()->t('Markdown'), 'markdown', '', (($_REQUEST['type'] ?? '') ?: 'bbcode') == 'markdown'], '$type_html' => ['type', DI::l10n()->t('HTML'), 'html', '', (($_REQUEST['type'] ?? '') ?: 'bbcode') == 'html'], + '$flag_twitter' => file_exists('addon/twitter/twitter.php'), + '$type_twitter' => ['type', DI::l10n()->t('Twitter Source'), 'twitter', '', (($_REQUEST['type'] ?? '') ?: 'bbcode') == 'twitter'], '$results' => $results ]); diff --git a/view/templates/babel.tpl b/view/templates/babel.tpl index 4dc50083d7..57d17fea91 100644 --- a/view/templates/babel.tpl +++ b/view/templates/babel.tpl @@ -9,6 +9,9 @@ {{include file="field_radio.tpl" field=$type_diaspora}} {{include file="field_radio.tpl" field=$type_markdown}} {{include file="field_radio.tpl" field=$type_html}} + {{if $flag_twitter}} + {{include file="field_radio.tpl" field=$type_twitter}} + {{/if}}

From 8de66c02742ae3a953cdb0e1fcbd32726d9e081b Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 17 Jul 2020 17:16:22 -0400 Subject: [PATCH 038/573] Add shortened URL link label stripping to PageInfo::stripTrailingUrlFromBody - Add test cases for shortened URL link labels --- src/Content/PageInfo.php | 23 ++++++++++++++++++----- tests/src/Content/PageInfoTest.php | 15 +++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/Content/PageInfo.php b/src/Content/PageInfo.php index 642c579387..86663a833f 100644 --- a/src/Content/PageInfo.php +++ b/src/Content/PageInfo.php @@ -252,22 +252,35 @@ class PageInfo /** * Remove the provided URL from the body if it is at the end of it. - * Keep the link label if it isn't the full URL. + * Keep the link label if it isn't the full URL or a shortened version of it. * * @param string $body * @param string $url - * @return string|string[]|null + * @return string */ protected static function stripTrailingUrlFromBody(string $body, string $url) { $quotedUrl = preg_quote($url, '#'); - $body = preg_replace("#(?: + $body = preg_replace_callback("#(?: \[url]$quotedUrl\[/url]| \[url=$quotedUrl]$quotedUrl\[/url]| \[url=$quotedUrl]([^[]*?)\[/url]| $quotedUrl - )$#isx", '$1', $body); + )$#isx", function ($match) use ($url) { + // Stripping URLs with no label + if (!isset($match[1])) { + return ''; + } - return $body; + // Stripping link labels that include a shortened version of the URL + if (strpos($url, trim($match[1], '.…')) !== false) { + return ''; + } + + // Keep all other labels + return $match[1]; + }, $body); + + return rtrim($body); } } diff --git a/tests/src/Content/PageInfoTest.php b/tests/src/Content/PageInfoTest.php index 6f9641564b..3604348fff 100644 --- a/tests/src/Content/PageInfoTest.php +++ b/tests/src/Content/PageInfoTest.php @@ -108,6 +108,21 @@ class PageInfoTest extends MockedTest 'body' => '[url=https://example.com]link label[/url]', 'url' => 'https://example.com', ], + 'task-8797-shortened-link-label' => [ + 'expected' => 'content', + 'body' => 'content [url=https://example.com/page]example.com/[/url]', + 'url' => 'https://example.com/page', + ], + 'task-8797-shortened-link-label-ellipsis' => [ + 'expected' => 'content', + 'body' => 'content [url=https://example.com/page]example.com…[/url]', + 'url' => 'https://example.com/page', + ], + 'task-8797-shortened-link-label-dots' => [ + 'expected' => 'content', + 'body' => 'content [url=https://example.com/page]example.com...[/url]', + 'url' => 'https://example.com/page', + ], ]; } From 25b3fa83fc25912ea03638fe1ac1aaaaa28f58be Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 17 Jul 2020 19:15:43 -0400 Subject: [PATCH 039/573] Rename PageInfo::appendToBody to searchAndAppendToBody --- src/Content/PageInfo.php | 2 +- src/Module/Debug/Babel.php | 2 +- src/Protocol/Diaspora.php | 4 ++-- src/Protocol/OStatus.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Content/PageInfo.php b/src/Content/PageInfo.php index 642c579387..478e7a490b 100644 --- a/src/Content/PageInfo.php +++ b/src/Content/PageInfo.php @@ -40,7 +40,7 @@ class PageInfo * @return string * @throws HTTPException\InternalServerErrorException */ - public static function appendToBody(string $body, bool $searchNakedUrls = false, bool $no_photos = false) + public static function searchAndAppendToBody(string $body, bool $searchNakedUrls = false, bool $no_photos = false) { Logger::info('add_page_info_to_body: fetch page info for body', ['body' => $body]); diff --git a/src/Module/Debug/Babel.php b/src/Module/Debug/Babel.php index 2954bc010c..ee9dae3054 100644 --- a/src/Module/Debug/Babel.php +++ b/src/Module/Debug/Babel.php @@ -115,7 +115,7 @@ class Babel extends BaseModule 'content' => visible_whitespace(var_export($tags, true)), ]; - $body2 = PageInfo::appendToBody($bbcode, true); + $body2 = PageInfo::searchAndAppendToBody($bbcode, true); $results[] = [ 'title' => DI::l10n()->t('PageInfo::appendToBody'), 'content' => visible_whitespace($body2) diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index 0dfcf6f77e..bd99b361e3 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -2622,7 +2622,7 @@ class Diaspora $item["body"] = self::replacePeopleGuid($item["body"], $item["author-link"]); // Add OEmbed and other information to the body - $item["body"] = PageInfo::appendToBody($item["body"], false, true); + $item["body"] = PageInfo::searchAndAppendToBody($item["body"], false, true); return $item; } else { @@ -2986,7 +2986,7 @@ class Diaspora // Add OEmbed and other information to the body if (!self::isHubzilla($contact["url"])) { - $body = PageInfo::appendToBody($body, false, true); + $body = PageInfo::searchAndAppendToBody($body, false, true); } } diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index fedf0f2533..9a52476b56 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -698,7 +698,7 @@ class OStatus // Only add additional data when there is no picture in the post if (!strstr($item["body"], '[/img]')) { - $item["body"] = PageInfo::appendToBody($item["body"]); + $item["body"] = PageInfo::searchAndAppendToBody($item["body"]); } Tag::storeFromBody($item['uri-id'], $item['body']); From 886cf400369289d5ef91fabe6d67008209350dca Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 17 Jul 2020 19:18:27 -0400 Subject: [PATCH 040/573] Ensure ParseUrl::getSiteinfo always returns the url and type keys --- src/Util/ParseUrl.php | 51 ++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/src/Util/ParseUrl.php b/src/Util/ParseUrl.php index 62b5d007d8..b6d172a3a1 100644 --- a/src/Util/ParseUrl.php +++ b/src/Util/ParseUrl.php @@ -55,14 +55,13 @@ class ParseUrl * to avoid endless loops * * @return array which contains needed data for embedding - * string 'url' => The url of the parsed page - * string 'type' => Content type - * string 'title' => The title of the content - * string 'text' => The description for the content - * string 'image' => A preview image of the content (only available - * if $no_geuessing = false - * array'images' = Array of preview pictures - * string 'keywords' => The tags which belong to the content + * string 'url' => The url of the parsed page + * string 'type' => Content type + * string 'title' => (optional) The title of the content + * string 'text' => (optional) The description for the content + * string 'image' => (optional) A preview image of the content (only available if $no_geuessing = false) + * array 'images' => (optional) Array of preview pictures + * string 'keywords' => (optional) The tags which belong to the content * * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @see ParseUrl::getSiteinfo() for more information about scraping @@ -115,14 +114,13 @@ class ParseUrl * @param int $count Internal counter to avoid endless loops * * @return array which contains needed data for embedding - * string 'url' => The url of the parsed page - * string 'type' => Content type - * string 'title' => The title of the content - * string 'text' => The description for the content - * string 'image' => A preview image of the content (only available - * if $no_geuessing = false - * array'images' = Array of preview pictures - * string 'keywords' => The tags which belong to the content + * string 'url' => The url of the parsed page + * string 'type' => Content type + * string 'title' => (optional) The title of the content + * string 'text' => (optional) The description for the content + * string 'image' => (optional) A preview image of the content (only available if $no_guessing = false) + * array 'images' => (optional) Array of preview pictures + * string 'keywords' => (optional) The tags which belong to the content * * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @todo https://developers.google.com/+/plugins/snippet/ @@ -140,28 +138,27 @@ class ParseUrl */ public static function getSiteinfo($url, $no_guessing = false, $do_oembed = true, $count = 1) { - $siteinfo = []; - // Check if the URL does contain a scheme $scheme = parse_url($url, PHP_URL_SCHEME); if ($scheme == '') { - $url = 'http://' . trim($url, '/'); + $url = 'http://' . ltrim($url, '/'); } + $url = trim($url, "'\""); + + $url = Network::stripTrackingQueryParams($url); + + $siteinfo = [ + 'url' => $url, + 'type' => 'link', + ]; + if ($count > 10) { Logger::log('Endless loop detected for ' . $url, Logger::DEBUG); return $siteinfo; } - $url = trim($url, "'"); - $url = trim($url, '"'); - - $url = Network::stripTrackingQueryParams($url); - - $siteinfo['url'] = $url; - $siteinfo['type'] = 'link'; - $curlResult = Network::curl($url); if (!$curlResult->isSuccess()) { return $siteinfo; From 972b65ba33f65be3fee4b6201304702c51b22ed3 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 17 Jul 2020 19:38:28 -0400 Subject: [PATCH 041/573] Add intermediate method PageInfo::appendDataToBody - It handles the already existing attachment in the body case --- src/Content/PageInfo.php | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/Content/PageInfo.php b/src/Content/PageInfo.php index 478e7a490b..212082f9f3 100644 --- a/src/Content/PageInfo.php +++ b/src/Content/PageInfo.php @@ -49,14 +49,34 @@ class PageInfo return $body; } - $footer = self::getFooterFromUrl($url, $no_photos); - if (!$footer) { + $data = self::queryUrl($url); + if (!$data) { return $body; } - $body = self::stripTrailingUrlFromBody($body, $url); + return self::appendDataToBody($body, $data, $no_photos); + } - $body .= "\n" . $footer; + /** + * @param string $body + * @param array $data + * @param bool $no_photos + * @return string + * @throws HTTPException\InternalServerErrorException + */ + public static function appendDataToBody(string $body, array $data, bool $no_photos = false) + { + // Only one [attachment] tag per body is allowed + $existingAttachmentPos = strpos($body, '[attachment'); + if ($existingAttachmentPos !== false) { + $linkTitle = $data['title'] ?: $data['url']; + // Additional link attachments are prepended before the existing [attachment] tag + $body = substr_replace($body, "\n[bookmark=" . $data['url'] . ']' . $linkTitle . "[/bookmark]\n", $existingAttachmentPos, 0); + } else { + $footer = PageInfo::getFooterFromData($data, $no_photos); + $body = self::stripTrailingUrlFromBody($body, $data['url']); + $body .= "\n" . $footer; + } return $body; } From 911a23f18b7861127504b91eecd3974f5e36b976 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 17 Jul 2020 19:39:12 -0400 Subject: [PATCH 042/573] Use PageInfo::appendDataToBody in ActivityPub\Processor::constructAttachList --- src/Protocol/ActivityPub/Processor.php | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 745a56c2a5..e4cef17045 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -21,6 +21,7 @@ namespace Friendica\Protocol\ActivityPub; +use Friendica\Content\PageInfo; use Friendica\Content\Text\BBCode; use Friendica\Content\Text\HTML; use Friendica\Core\Logger; @@ -96,18 +97,16 @@ class Processor foreach ($activity['attachments'] as $attach) { switch ($attach['type']) { case 'link': - // Only one [attachment] tag is allowed - $existingAttachmentPos = strpos($item['body'], '[attachment'); - if ($existingAttachmentPos !== false) { - $linkTitle = $attach['title'] ?: $attach['url']; - // Additional link attachments are prepended before the existing [attachment] tag - $item['body'] = substr_replace($item['body'], "\n[bookmark=" . $attach['url'] . ']' . $linkTitle . "[/bookmark]\n", $existingAttachmentPos, 0); - } else { - // Strip the link preview URL from the end of the body if any - $quotedUrl = preg_quote($attach['url'], '#'); - $item['body'] = preg_replace("#\s*(?:\[bookmark={$quotedUrl}].+?\[/bookmark]|\[url={$quotedUrl}].+?\[/url]|\[url]{$quotedUrl}\[/url]|{$quotedUrl})\s*$#", '', $item['body']); - $item['body'] .= "\n[attachment type='link' url='" . $attach['url'] . "' title='" . htmlspecialchars($attach['title'] ?? '', ENT_QUOTES) . "' image='" . ($attach['image'] ?? '') . "']" . ($attach['desc'] ?? '') . '[/attachment]'; - } + $data = [ + 'url' => $attach['url'], + 'type' => $attach['type'], + 'title' => $attach['title'] ?? '', + 'text' => $attach['desc'] ?? '', + 'image' => $attach['image'] ?? '', + 'images' => [], + 'keywords' => [], + ]; + $item['body'] = PageInfo::appendDataToBody($item['body'], $data); break; default: $filetype = strtolower(substr($attach['mediaType'], 0, strpos($attach['mediaType'], '/'))); From 9f1d1db1ee305f2b31e6b1572e0afbf2c76c52e8 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Sat, 18 Jul 2020 17:49:10 +0200 Subject: [PATCH 043/573] Database performance updates --- mod/ping.php | 11 ++++------- src/Database/Database.php | 4 ++-- src/Model/Item.php | 2 +- src/Worker/Cron.php | 3 +-- static/dbstructure.config.php | 4 +++- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/mod/ping.php b/mod/ping.php index d1983e8036..4b972369c7 100644 --- a/mod/ping.php +++ b/mod/ping.php @@ -136,13 +136,9 @@ function ping_init(App $a) $notifs = ping_get_notifications(local_user()); - $condition = ["`unseen` AND `uid` = ? AND `contact-id` != ? AND (`vid` != ? OR `vid` IS NULL)", - local_user(), local_user(), Verb::getID(Activity::FOLLOW)]; - $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar', - 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'wall', 'activity']; - $params = ['order' => ['received' => true]]; - $items = Item::selectForUser(local_user(), $fields, $condition, $params); - + $condition = ["`unseen` AND `uid` = ? AND NOT `origin` AND (`vid` != ? OR `vid` IS NULL)", + local_user(), Verb::getID(Activity::FOLLOW)]; + $items = Item::selectForUser(local_user(), ['wall'], $condition); if (DBA::isResult($items)) { $items_unseen = Item::inArray($items); $arr = ['items' => $items_unseen]; @@ -156,6 +152,7 @@ function ping_init(App $a) } } } + DBA::close($items); if ($network_count) { // Find out how unseen network posts are spread across groups diff --git a/src/Database/Database.php b/src/Database/Database.php index eaf4900509..5ef4815563 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -343,7 +343,7 @@ class Database $row['key'] . "\t" . $row['rows'] . "\t" . $row['Extra'] . "\t" . basename($backtrace[1]["file"]) . "\t" . $backtrace[1]["line"] . "\t" . $backtrace[2]["function"] . "\t" . - substr($query, 0, 2000) . "\n", FILE_APPEND); + substr($query, 0, 4000) . "\n", FILE_APPEND); } } } @@ -712,7 +712,7 @@ class Database @file_put_contents($this->configCache->get('system', 'db_log'), DateTimeFormat::utcNow() . "\t" . $duration . "\t" . basename($backtrace[1]["file"]) . "\t" . $backtrace[1]["line"] . "\t" . $backtrace[2]["function"] . "\t" . - substr($this->replaceParameters($sql, $args), 0, 2000) . "\n", FILE_APPEND); + substr($this->replaceParameters($sql, $args), 0, 4000) . "\n", FILE_APPEND); } } return $retval; diff --git a/src/Model/Item.php b/src/Model/Item.php index 332c734fa5..c75286b25c 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -2093,7 +2093,7 @@ class Item DBA::close($contacts); if (!empty($owner['alias'])) { - $condition = ['url' => $owner['alias'], 'rel' => [Contact::SHARING, Contact::FRIEND]]; + $condition = ['nurl' => Strings::normaliseLink($owner['alias']), 'rel' => [Contact::SHARING, Contact::FRIEND]]; $contacts = DBA::select('contact', ['uid'], $condition); while ($contact = DBA::fetch($contacts)) { if ($contact['uid'] == 0) { diff --git a/src/Worker/Cron.php b/src/Worker/Cron.php index a47193334c..6749b9648d 100644 --- a/src/Worker/Cron.php +++ b/src/Worker/Cron.php @@ -160,7 +160,6 @@ class Cron $condition = ["`network` IN (?, ?, ?, ?) AND `uid` = ? AND NOT `self` AND `last-update` < ?", Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS, 0, $last_updated]; - $total = DBA::count('contact', $condition); $oldest_date = ''; $oldest_id = ''; $contacts = DBA::select('contact', ['id', 'last-update'], $condition, ['limit' => 100, 'order' => ['last-update']]); @@ -172,7 +171,7 @@ class Cron Worker::add(PRIORITY_LOW, "UpdateContact", $contact['id'], 'force'); ++$count; } - Logger::info('Initiated update for public contacts', ['interval' => $count, 'total' => $total, 'id' => $oldest_id, 'oldest' => $oldest_date]); + Logger::info('Initiated update for public contacts', ['interval' => $count, 'id' => $oldest_id, 'oldest' => $oldest_date]); DBA::close($contacts); } diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index bab158d036..344a21fc5d 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', 1355); + define('DB_UPDATE_VERSION', 1356); } return [ @@ -198,6 +198,7 @@ return [ "attag_uid" => ["attag(32)", "uid"], "dfrn-id" => ["dfrn-id(64)"], "issued-id" => ["issued-id(64)"], + "network_uid_lastupdate" => ["network", "uid", "last-update"], "gsid" => ["gsid"] ] ], @@ -319,6 +320,7 @@ return [ "addr" => ["addr(32)"], "alias" => ["alias(190)"], "followers" => ["followers(190)"], + "baseurl" => ["baseurl(190)"], "gsid" => ["gsid"] ] ], From 2ad5bd9b9c0b07d0b1ef85d4d26d2411db01e308 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Sat, 18 Jul 2020 22:48:40 +0200 Subject: [PATCH 044/573] Add some more useful fields for ping hook --- mod/ping.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/ping.php b/mod/ping.php index 4b972369c7..848f2f0ecf 100644 --- a/mod/ping.php +++ b/mod/ping.php @@ -138,7 +138,7 @@ function ping_init(App $a) $condition = ["`unseen` AND `uid` = ? AND NOT `origin` AND (`vid` != ? OR `vid` IS NULL)", local_user(), Verb::getID(Activity::FOLLOW)]; - $items = Item::selectForUser(local_user(), ['wall'], $condition); + $items = Item::selectForUser(local_user(), ['wall', 'uid', 'uri-id'], $condition); if (DBA::isResult($items)) { $items_unseen = Item::inArray($items); $arr = ['items' => $items_unseen]; From 1532f0d52914cf7e16ed05b3d77d66c3a7771448 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 19 Jul 2020 01:15:57 +0000 Subject: [PATCH 045/573] New field "failed" for gserver, gcontact and contact --- src/Model/Contact.php | 14 ++++++++------ src/Model/GContact.php | 26 ++++++++++++++------------ src/Model/GServer.php | 8 +++++--- src/Worker/OnePoll.php | 28 +++++++++++++++++----------- static/dbstructure.config.php | 3 +++ 5 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 646da57caa..09d5277331 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -1029,7 +1029,7 @@ class Contact { // Always unarchive the relay contact entry if (!empty($contact['batch']) && !empty($contact['term-date']) && ($contact['term-date'] > DBA::NULL_DATETIME)) { - $fields = ['term-date' => DBA::NULL_DATETIME, 'archive' => false]; + $fields = ['failed' => false, 'term-date' => DBA::NULL_DATETIME, 'archive' => false]; $condition = ['uid' => 0, 'network' => Protocol::FEDERATED, 'batch' => $contact['batch'], 'contact-type' => self::TYPE_RELAY]; DBA::update('contact', $fields, $condition); } @@ -1053,7 +1053,7 @@ class Contact } // It's a miracle. Our dead contact has inexplicably come back to life. - $fields = ['term-date' => DBA::NULL_DATETIME, 'archive' => false]; + $fields = ['failed' => false, 'term-date' => DBA::NULL_DATETIME, 'archive' => false]; DBA::update('contact', $fields, ['id' => $contact['id']]); DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]); GContact::updateFromPublicContactURL($contact['url']); @@ -1495,7 +1495,8 @@ class Contact $updated = [ 'url' => $data['url'], 'nurl' => Strings::normaliseLink($data['url']), - 'updated' => DateTimeFormat::utcNow() + 'updated' => DateTimeFormat::utcNow(), + 'failed' => false ]; $fields = ['addr', 'alias', 'name', 'nick', 'keywords', 'location', 'about', 'baseurl', 'gsid']; @@ -1973,7 +1974,7 @@ class Contact // We check after the probing to be able to correct falsely detected contact types. if (($contact['contact-type'] == self::TYPE_RELAY) && (!Strings::compareLink($ret['url'], $contact['url']) || in_array($ret['network'], [Protocol::FEED, Protocol::PHANTOM]))) { - self::updateContact($id, $uid, $contact['url'], ['last-update' => $updated, 'success_update' => $updated]); + self::updateContact($id, $uid, $contact['url'], ['failed' => false, 'last-update' => $updated, 'success_update' => $updated]); Logger::info('Not updating relais', ['id' => $id, 'url' => $contact['url']]); return true; } @@ -1981,7 +1982,7 @@ class Contact // If Probe::uri fails the network code will be different ("feed" or "unkn") if (in_array($ret['network'], [Protocol::FEED, Protocol::PHANTOM]) && ($ret['network'] != $contact['network'])) { if ($force && ($uid == 0)) { - self::updateContact($id, $uid, $ret['url'], ['last-update' => $updated, 'failure_update' => $updated]); + self::updateContact($id, $uid, $ret['url'], ['failed' => true, 'last-update' => $updated, 'failure_update' => $updated]); } return false; } @@ -2025,7 +2026,7 @@ class Contact if (!$update) { if ($force) { - self::updateContact($id, $uid, $ret['url'], ['last-update' => $updated, 'success_update' => $updated]); + self::updateContact($id, $uid, $ret['url'], ['failed' => false, 'last-update' => $updated, 'success_update' => $updated]); } // Update the public contact @@ -2055,6 +2056,7 @@ class Contact if ($force && ($uid == 0)) { $ret['last-update'] = $updated; $ret['success_update'] = $updated; + $ret['failed'] = false; } unset($ret['photo']); diff --git a/src/Model/GContact.php b/src/Model/GContact.php index 5ef74dcf77..00867475a8 100644 --- a/src/Model/GContact.php +++ b/src/Model/GContact.php @@ -231,6 +231,7 @@ class GContact } $gcontact['server_url'] = $data['baseurl']; + $gcontact['failed'] = false; $gcontact = array_merge($gcontact, $data); } @@ -642,7 +643,7 @@ class GContact $fields = ['name' => $contact['name'], 'nick' => $contact['nick'] ?? '', 'addr' => $contact['addr'] ?? '', 'network' => $contact['network'], 'url' => $contact['url'], 'nurl' => Strings::normaliseLink($contact['url']), 'photo' => $contact['photo'], 'created' => DateTimeFormat::utcNow(), 'updated' => DateTimeFormat::utcNow(), 'location' => $contact['location'], - 'about' => $contact['about'], 'hide' => $contact['hide'], 'generation' => $contact['generation']]; + 'about' => $contact['about'], 'hide' => $contact['hide'], 'generation' => $contact['generation'], 'failed' => false]; DBA::insert('gcontact', $fields); @@ -681,7 +682,7 @@ class GContact } $public_contact = DBA::selectFirst('gcontact', [ - 'name', 'nick', 'photo', 'location', 'about', 'addr', 'generation', 'birthday', 'keywords', 'gsid', + 'name', 'nick', 'photo', 'location', 'about', 'addr', 'generation', 'birthday', 'keywords', 'gsid', 'failed', 'contact-type', 'hide', 'nsfw', 'network', 'alias', 'notify', 'server_url', 'connect', 'updated', 'url' ], ['id' => $gcontact_id]); @@ -787,7 +788,7 @@ class GContact 'location' => $contact['location'], 'about' => $contact['about'], 'generation' => $contact['generation'], 'updated' => $contact['updated'], 'server_url' => $contact['server_url'], 'connect' => $contact['connect'], - 'gsid' => $contact['gsid'] + 'failed' => $contact['failed'], 'gsid' => $contact['gsid'] ]; DBA::update('gcontact', $updated, $condition, $fields); @@ -851,13 +852,13 @@ class GContact $noscrape = json_decode($curlResult->getBody(), true); if (!empty($noscrape) && !empty($noscrape['updated'])) { $noscrape['updated'] = DateTimeFormat::utc($noscrape['updated'], DateTimeFormat::MYSQL); - $fields = ['last_contact' => DateTimeFormat::utcNow(), 'updated' => $noscrape['updated']]; + $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $noscrape['updated']]; DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); return true; } } elseif ($curlResult->isTimeout()) { // On a timeout return the existing value, but mark the contact as failure - $fields = ['last_failure' => DateTimeFormat::utcNow()]; + $fields = ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()]; DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); return true; } @@ -915,7 +916,7 @@ class GContact return; } - $fields = ['last_contact' => DateTimeFormat::utcNow(), 'updated' => $last_updated]; + $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $last_updated]; DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); } @@ -929,7 +930,7 @@ class GContact // Search for the newest entry in the feed $curlResult = Network::curl($data['poll']); if (!$curlResult->isSuccess()) { - $fields = ['last_failure' => DateTimeFormat::utcNow()]; + $fields = ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()]; DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); Logger::info("Profile wasn't reachable (no feed)", ['url' => $data['url']]); @@ -970,7 +971,7 @@ class GContact return; } - $fields = ['last_contact' => DateTimeFormat::utcNow(), 'updated' => $last_updated]; + $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $last_updated]; DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); } /** @@ -1012,7 +1013,7 @@ class GContact $fields = ['name', 'nick', 'url', 'nurl', 'location', 'about', 'keywords', 'bd', 'contact-type', 'network', 'addr', 'notify', 'alias', 'archive', 'term-date', 'created', 'updated', 'avatar', 'success_update', 'failure_update', 'forum', 'prv', - 'baseurl', 'gsid', 'sensitive', 'unsearchable']; + 'baseurl', 'gsid', 'sensitive', 'unsearchable', 'failed']; $contact = DBA::selectFirst('contact', $fields, array_merge($condition, ['uid' => 0, 'network' => Protocol::FEDERATED])); if (!DBA::isResult($contact)) { @@ -1022,7 +1023,7 @@ class GContact $fields = ['name', 'nick', 'url', 'nurl', 'location', 'about', 'keywords', 'generation', 'birthday', 'contact-type', 'network', 'addr', 'notify', 'alias', 'archived', 'archive_date', 'created', 'updated', 'photo', 'last_contact', 'last_failure', 'community', 'connect', - 'server_url', 'gsid', 'nsfw', 'hide', 'id']; + 'server_url', 'gsid', 'nsfw', 'hide', 'id', 'failed']; $old_gcontact = DBA::selectFirst('gcontact', $fields, ['nurl' => $contact['nurl']]); $do_insert = !DBA::isResult($old_gcontact); @@ -1034,7 +1035,7 @@ class GContact // These fields are identical in both contact and gcontact $fields = ['name', 'nick', 'url', 'nurl', 'location', 'about', 'keywords', 'gsid', - 'contact-type', 'network', 'addr', 'notify', 'alias', 'created', 'updated']; + 'contact-type', 'network', 'addr', 'notify', 'alias', 'created', 'updated', 'failed']; foreach ($fields as $field) { $gcontact[$field] = $contact[$field]; @@ -1100,13 +1101,14 @@ class GContact $data = Probe::uri($url, $force); if (in_array($data['network'], [Protocol::PHANTOM])) { - $fields = ['last_failure' => DateTimeFormat::utcNow()]; + $fields = ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()]; DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($url)]); Logger::info('Invalid network for contact', ['url' => $data['url'], 'callstack' => System::callstack()]); return false; } $data['server_url'] = $data['baseurl']; + $data['failed'] = false; self::update($data); diff --git a/src/Model/GServer.php b/src/Model/GServer.php index fdbc775098..23056906c2 100644 --- a/src/Model/GServer.php +++ b/src/Model/GServer.php @@ -235,14 +235,14 @@ class GServer private static function setFailure(string $url) { if (DBA::exists('gserver', ['nurl' => Strings::normaliseLink($url)])) { - DBA::update('gserver', ['last_failure' => DateTimeFormat::utcNow(), 'detection-method' => null], + DBA::update('gserver', ['failed' => true, 'last_failure' => DateTimeFormat::utcNow(), 'detection-method' => null], ['nurl' => Strings::normaliseLink($url)]); Logger::info('Set failed status for existing server', ['url' => $url]); return; } DBA::insert('gserver', ['url' => $url, 'nurl' => Strings::normaliseLink($url), 'network' => Protocol::PHANTOM, 'created' => DateTimeFormat::utcNow(), - 'last_failure' => DateTimeFormat::utcNow()]); + 'failed' => true, 'last_failure' => DateTimeFormat::utcNow()]); Logger::info('Set failed status for new server', ['url' => $url]); } @@ -303,7 +303,8 @@ class GServer // If the URL missmatches, then we mark the old entry as failure if ($url != $original_url) { - DBA::update('gserver', ['last_failure' => DateTimeFormat::utcNow()], ['nurl' => Strings::normaliseLink($original_url)]); + DBA::update('gserver', ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()], + ['nurl' => Strings::normaliseLink($original_url)]); } // When a nodeinfo is present, we don't need to dig further @@ -449,6 +450,7 @@ class GServer } $serverdata['last_contact'] = DateTimeFormat::utcNow(); + $serverdata['failed'] = false; $gserver = DBA::selectFirst('gserver', ['network'], ['nurl' => Strings::normaliseLink($url)]); if (!DBA::isResult($gserver)) { diff --git a/src/Worker/OnePoll.php b/src/Worker/OnePoll.php index a2659f4e76..1798612108 100644 --- a/src/Worker/OnePoll.php +++ b/src/Worker/OnePoll.php @@ -165,7 +165,7 @@ class OnePoll if (!strstr($xml, '<')) { Logger::log('post_handshake: response from ' . $url . ' did not contain XML.'); - $fields = ['last-update' => $updated, 'failure_update' => $updated]; + $fields = ['failed' => true, 'last-update' => $updated, 'failure_update' => $updated]; self::updateContact($contact, $fields); Contact::markForArchival($contact); return; @@ -213,10 +213,10 @@ class OnePoll } } - self::updateContact($contact, ['last-update' => $updated, 'success_update' => $updated]); + self::updateContact($contact, ['failed' => false, 'last-update' => $updated, 'success_update' => $updated]); Contact::unmarkForArchival($contact); } elseif (in_array($contact["network"], [Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS, Protocol::FEED])) { - self::updateContact($contact, ['last-update' => $updated, 'failure_update' => $updated]); + self::updateContact($contact, ['failed' => true, 'last-update' => $updated, 'failure_update' => $updated]); Contact::markForArchival($contact); } else { self::updateContact($contact, ['last-update' => $updated]); @@ -244,8 +244,14 @@ class OnePoll */ private static function updateContact(array $contact, array $fields) { + // Update the user's contact DBA::update('contact', $fields, ['id' => $contact['id']]); + + // Update the public contact DBA::update('contact', $fields, ['uid' => 0, 'nurl' => $contact['nurl']]); + + // Update the rest of the contacts that aren't polled + DBA::update('contact', $fields, ['rel' => Contact::FOLLOWER, 'nurl' => $contact['nurl']]); } /** @@ -289,7 +295,7 @@ class OnePoll if (!$curlResult->isSuccess() && ($curlResult->getErrorNumber() == CURLE_OPERATION_TIMEDOUT)) { // set the last-update so we don't keep polling - self::updateContact($contact, ['last-update' => $updated]); + self::updateContact($contact, ['failed' => true, 'last-update' => $updated]); Contact::markForArchival($contact); Logger::log('Contact archived'); return false; @@ -307,7 +313,7 @@ class OnePoll Logger::log("$url appears to be dead - marking for death "); // set the last-update so we don't keep polling - $fields = ['last-update' => $updated, 'failure_update' => $updated]; + $fields = ['failed' => true, 'last-update' => $updated, 'failure_update' => $updated]; self::updateContact($contact, $fields); Contact::markForArchival($contact); return false; @@ -316,7 +322,7 @@ class OnePoll if (!strstr($handshake_xml, '<')) { Logger::log('response from ' . $url . ' did not contain XML.'); - $fields = ['last-update' => $updated, 'failure_update' => $updated]; + $fields = ['failed' => true, 'last-update' => $updated, 'failure_update' => $updated]; self::updateContact($contact, $fields); Contact::markForArchival($contact); return false; @@ -327,7 +333,7 @@ class OnePoll if (!is_object($res)) { Logger::info('Unparseable response', ['url' => $url]); - $fields = ['last-update' => $updated, 'failure_update' => $updated]; + $fields = ['failed' => true, 'last-update' => $updated, 'failure_update' => $updated]; self::updateContact($contact, $fields); Contact::markForArchival($contact); return false; @@ -338,7 +344,7 @@ class OnePoll Logger::log("$url replied status 1 - marking for death "); // set the last-update so we don't keep polling - $fields = ['last-update' => $updated, 'failure_update' => $updated]; + $fields = ['failed' => true, 'last-update' => $updated, 'failure_update' => $updated]; self::updateContact($contact, $fields); Contact::markForArchival($contact); } elseif ($contact['term-date'] > DBA::NULL_DATETIME) { @@ -417,7 +423,7 @@ class OnePoll // Will only do this once per notify-enabled OStatus contact // or if relationship changes - $stat_writeable = ((($contact['notify']) && ($contact['rel'] == Contact::FOLLOWER || $contact['rel'] == Contact::FRIEND)) ? 1 : 0); + $stat_writeable = $contact['notify'] && ($contact['rel'] == Contact::FOLLOWER || $contact['rel'] == Contact::FRIEND); // Contacts from OStatus are always writable if ($protocol === Protocol::OSTATUS) { @@ -443,7 +449,7 @@ class OnePoll if ($curlResult->isTimeout()) { // set the last-update so we don't keep polling - self::updateContact($contact, ['last-update' => $updated]); + self::updateContact($contact, ['failed' => true, 'last-update' => $updated]); Contact::markForArchival($contact); Logger::log('Contact archived'); return false; @@ -467,7 +473,7 @@ class OnePoll $mail_disabled = ((function_exists('imap_open') && !DI::config()->get('system', 'imap_disabled')) ? 0 : 1); if ($mail_disabled) { // set the last-update so we don't keep polling - self::updateContact($contact, ['last-update' => $updated]); + self::updateContact($contact, ['failed' => true, 'last-update' => $updated]); Contact::markForArchival($contact); Logger::log('Contact archived'); return; diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index bab158d036..8908ba13aa 100755 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -82,6 +82,7 @@ return [ "last_poco_query" => ["type" => "datetime", "default" => DBA::NULL_DATETIME, "comment" => ""], "last_contact" => ["type" => "datetime", "default" => DBA::NULL_DATETIME, "comment" => ""], "last_failure" => ["type" => "datetime", "default" => DBA::NULL_DATETIME, "comment" => ""], + "failed" => ["type" => "boolean", "comment" => "Connection failed"], ], "indexes" => [ "PRIMARY" => ["id"], @@ -151,6 +152,7 @@ return [ "last-update" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Date of the last try to update the contact info"], "success_update" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Date of the last successful contact update"], "failure_update" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Date of the last failed update"], + "failed" => ["type" => "boolean", "comment" => "Connection failed"], "name-date" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""], "uri-date" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""], "avatar-date" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""], @@ -558,6 +560,7 @@ return [ "last_contact" => ["type" => "datetime", "default" => DBA::NULL_DATETIME, "comment" => ""], "last_failure" => ["type" => "datetime", "default" => DBA::NULL_DATETIME, "comment" => ""], "last_discovery" => ["type" => "datetime", "default" => DBA::NULL_DATETIME, "comment" => "Date of the last contact discovery"], + "failed" => ["type" => "boolean", "comment" => "Connection failed"], "archive_date" => ["type" => "datetime", "default" => DBA::NULL_DATETIME, "comment" => ""], "archived" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], "location" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], From d6a35c699558d8a592a23c2ba33bf48e5cad808e Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Sun, 19 Jul 2020 03:21:15 +0200 Subject: [PATCH 046/573] Support newline as block contact separator --- include/conversation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/conversation.php b/include/conversation.php index 8507c5ba97..8050296a56 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -316,7 +316,7 @@ function conv_get_blocklist() return []; } - $str_blocked = DI::pConfig()->get(local_user(), 'system', 'blocked'); + $str_blocked = str_replace(["\n", "\r"], ",", DI::pConfig()->get(local_user(), 'system', 'blocked')); if (empty($str_blocked)) { return []; } From 86bdb2d5b9298eb78316e85374ae8a00161b051e Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Sun, 19 Jul 2020 03:34:19 +0200 Subject: [PATCH 047/573] Add some logging to SpoolPost.php --- src/Worker/SpoolPost.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Worker/SpoolPost.php b/src/Worker/SpoolPost.php index 2026ff7f51..4dac93f67a 100644 --- a/src/Worker/SpoolPost.php +++ b/src/Worker/SpoolPost.php @@ -37,6 +37,7 @@ class SpoolPost { // It is not named like a spool file, so we don't care. if (substr($file, 0, 5) != "item-") { + Logger::notice('Spool file does does not start with "item-"', ['file' => $file]); continue; } @@ -44,11 +45,13 @@ class SpoolPost { // We don't care about directories either if (filetype($fullfile) != "file") { + Logger::notice('Spool file is no file', ['file' => $file]); continue; } // We can't read or write the file? So we don't care about it. if (!is_writable($fullfile) || !is_readable($fullfile)) { + Logger::notice('Spool file has insufficent permissions', ['file' => $file, 'writable' => is_writable($fullfile), 'readable' => is_readable($fullfile)]); continue; } @@ -56,17 +59,19 @@ class SpoolPost { // If it isn't an array then it is no spool file if (!is_array($arr)) { + Logger::notice('Spool file is no array', ['file' => $file]); continue; } // Skip if it doesn't seem to be an item array if (!isset($arr['uid']) && !isset($arr['uri']) && !isset($arr['network'])) { + Logger::notice('Spool file does not contain the needed fields', ['file' => $file]); continue; } $result = Item::insert($arr); - Logger::log("Spool file ".$file." stored: ".$result, Logger::DEBUG); + Logger::notice('Spool file is stored', ['file' => $file, 'result' => $result]); unlink($fullfile); } closedir($dh); From 35db33bcd2baf358c9fcfc3f8555d3daee2f4d15 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Sun, 19 Jul 2020 03:40:40 +0200 Subject: [PATCH 048/573] Ensure to only store valid item fields --- src/Model/Item.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/Model/Item.php b/src/Model/Item.php index c75286b25c..13a4a68380 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -31,6 +31,7 @@ use Friendica\Core\Session; use Friendica\Core\System; use Friendica\Core\Worker; use Friendica\Database\DBA; +use Friendica\Database\DBStructure; use Friendica\DI; use Friendica\Model\Post\Category; use Friendica\Protocol\Activity; @@ -118,8 +119,22 @@ class Item const PRIVATE = 1; const UNLISTED = 2; + const TABLES = ['item', 'user-item', 'item-content', 'post-delivery-data', 'diaspora-interaction']; + private static $legacy_mode = null; + private static function getItemFields() + { + $definition = DBStructure::definition('', false); + + $postfields = []; + foreach (self::TABLES as $table) { + $postfields[$table] = array_keys($definition[$table]['fields']); + } + + return $postfields; + } + public static function isLegacyMode() { if (is_null(self::$legacy_mode)) { @@ -1572,6 +1587,8 @@ class Item public static function insert($item, $notify = false, $dontcache = false) { + $structure = self::getItemFields(); + $orig_item = $item; $priority = PRIORITY_HIGH; @@ -1839,6 +1856,13 @@ class Item Tag::storeFromBody($item['uri-id'], $body); } + // Remove all fields that aren't part of the item table + foreach ($item as $field => $value) { + if (!in_array($field, $structure['item'])) { + unset($item[$field]); + } + } + $ret = DBA::insert('item', $item); // When the item was successfully stored we fetch the ID of the item. From c6c7c4e841ed2146f6baacc8adf802f730087450 Mon Sep 17 00:00:00 2001 From: Tobias Diekershoff Date: Sun, 19 Jul 2020 09:49:17 +0200 Subject: [PATCH 049/573] added CSV import/export of server blocklist --- src/Console/ServerBlock.php | 90 +++++++++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 13 deletions(-) diff --git a/src/Console/ServerBlock.php b/src/Console/ServerBlock.php index ada4f22132..7c17fdc575 100644 --- a/src/Console/ServerBlock.php +++ b/src/Console/ServerBlock.php @@ -51,19 +51,23 @@ Usage bin/console serverblock [-h|--help|-?] [-v] bin/console serverblock add [-h|--help|-?] [-v] bin/console serverblock remove [-h|--help|-?] [-v] + bin/console serverblock export + bin/console serverblock import Description With this tool, you can list the current blocked server domain patterns - or you can add / remove a blocked server domain pattern from the list. - - Patterns are case-insensitive shell wildcard comprising the following special characters: - - * : Any number of characters - - ? : Any single character - - [...] : char1 or char2 or... + or you can add / remove a blocked server domain pattern from the list. + Using the export and import options you can share your server blocklist + with other node admins by CSV files. + + Patterns are case-insensitive shell wildcard comprising the following special characters: + - * : Any number of characters + - ? : Any single character + - [...] : char1 or char2 or... Options - -h|--help|-? Show help information - -v Show more debug information. + -h|--help|-? Show help information + -v Show more debug information. HELP; return $help; } @@ -87,6 +91,10 @@ HELP; return $this->addBlockedServer($this->config); case 'remove': return $this->removeBlockedServer($this->config); + case 'export': + return $this->exportBlockedServers($this->config); + case 'import': + return $this->importBlockedServers($this->config); default: throw new CommandArgsException('Unknown command.'); break; @@ -94,10 +102,66 @@ HELP; } /** - * Prints the whole list of blocked domains including the reason + * Exports the list of blocked domains including the reason for the + * block to a CSV file. * * @param IConfig $config */ + private function exportBlockedServers(IConfig $config) + { + $filename = $this->getArgument(1); + $blocklist = $config->get('system', 'blocklist', []); + $fp = fopen($filename, 'w'); + foreach ($blocklist as $domain) { + fputcsv($fp, $domain); + } + } + /** + * Imports a list of domains and a reason for the block from a CSV + * file, e.g. created with the export function. + * + * @param IConfig $config + */ + private function importBlockedServers(IConfig $config) + { + $filename = $this->getArgument(1); + $currBlockList = $config->get('system', 'blocklist', []); + $newBlockList = []; + if (($fp = fopen($filename, 'r')) !== FALSE) { + while (($data = fgetcsv($fp, 1000, ',')) !== FALSE) { + $domain = $data[0]; + if (count($data) == 0) { + $reason = self::DEFAULT_REASON; + } else { + $reason = $data[1]; + } + $data = [ + 'domain' => $domain, + 'reason' => $reason + ]; + if (!in_array($data, $newBlockList)) + $newBlockList[] = $data; + } + foreach ($currBlockList as $blocked) { + if (!in_array($blocked, $newBlockList)) + $newBlockList[] = $blocked; + } + if ($config->set('system', 'blocklist', $newBlockList)) { + $this->out(sprintf("Entries from %s that were not blocked before are now blocked", $filename)); + return 0; + } else { + $this->out(sprintf("Couldn't save '%s' as blocked server", $domain)); + return 1; + } + + } + } + + /** + * Prints the whole list of blocked domains including the reason + * + /* @param IConfig $config + */ private function printBlockedServers(IConfig $config) { $table = new Console_Table(); @@ -127,9 +191,9 @@ HELP; $update = false; - $currBlocklist = $config->get('system', 'blocklist', []); + $currBlockList = $config->get('system', 'blocklist', []); $newBlockList = []; - foreach ($currBlocklist as $blocked) { + foreach ($currBlockList as $blocked) { if ($blocked['domain'] === $domain) { $update = true; $newBlockList[] = [ @@ -178,9 +242,9 @@ HELP; $found = false; - $currBlocklist = $config->get('system', 'blocklist', []); + $currBlockList = $config->get('system', 'blocklist', []); $newBlockList = []; - foreach ($currBlocklist as $blocked) { + foreach ($currBlockList as $blocked) { if ($blocked['domain'] === $domain) { $found = true; } else { From b191c8c11cf811db9f88a54d166b89336899320d Mon Sep 17 00:00:00 2001 From: Tobias Diekershoff Date: Sun, 19 Jul 2020 09:58:31 +0200 Subject: [PATCH 050/573] spaces instead of tabs here --- src/Console/ServerBlock.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/ServerBlock.php b/src/Console/ServerBlock.php index 7c17fdc575..934226867b 100644 --- a/src/Console/ServerBlock.php +++ b/src/Console/ServerBlock.php @@ -67,7 +67,7 @@ Description Options -h|--help|-? Show help information - -v Show more debug information. + -v Show more debug information. HELP; return $help; } From 59bebe7bcfdf82f18d545cb21d04cac8a6735399 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 19 Jul 2020 10:03:33 +0000 Subject: [PATCH 051/573] Post update added --- src/Model/Item.php | 8 +++---- static/dbstructure.config.php | 2 +- update.php | 40 +++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/Model/Item.php b/src/Model/Item.php index c75286b25c..537611dd1b 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -2391,7 +2391,7 @@ class Item } /// @todo On private posts we could obfuscate the date - $update = ($arr['private'] != self::PRIVATE); + $update = ($arr['private'] != self::PRIVATE) || in_array($arr['network'], Protocol::FEDERATED); // Is it a forum? Then we don't care about the rules from above if (!$update && in_array($arr["network"], [Protocol::ACTIVITYPUB, Protocol::DFRN]) && ($arr["parent-uri"] === $arr["uri"])) { @@ -2409,15 +2409,15 @@ class Item } else { $condition = ['id' => $arr['contact-id'], 'self' => false]; } - DBA::update('contact', ['success_update' => $arr['received'], 'last-item' => $arr['received']], $condition); + DBA::update('contact', ['failed' => false, 'success_update' => $arr['received'], 'last-item' => $arr['received']], $condition); } // Now do the same for the system wide contacts with uid=0 if ($arr['private'] != self::PRIVATE) { - DBA::update('contact', ['success_update' => $arr['received'], 'last-item' => $arr['received']], + DBA::update('contact', ['failed' => false, 'success_update' => $arr['received'], 'last-item' => $arr['received']], ['id' => $arr['owner-id']]); if ($arr['owner-id'] != $arr['author-id']) { - DBA::update('contact', ['success_update' => $arr['received'], 'last-item' => $arr['received']], + DBA::update('contact', ['failed' => false, 'success_update' => $arr['received'], 'last-item' => $arr['received']], ['id' => $arr['author-id']]); } } diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 7433531fe7..14a52aa9ad 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', 1356); + define('DB_UPDATE_VERSION', 1357); } return [ diff --git a/update.php b/update.php index 1a9a60ac65..a4d71bc4fd 100644 --- a/update.php +++ b/update.php @@ -535,3 +535,43 @@ function update_1354() } return Update::SUCCESS; } + +function update_1357() +{ + if (!DBA::e("UPDATE `contact` SET `failed` = true WHERE `success_update` < `failure_update` AND `failed` IS NULL")) { + return Update::FAILED; + } + + if (!DBA::e("UPDATE `contact` SET `failed` = false WHERE `success_update` > `failure_update` AND `failed` IS NULL")) { + return Update::FAILED; + } + + if (!DBA::e("UPDATE `contact` SET `failed` = false WHERE `updated` > `failure_update` AND `failed` IS NULL")) { + return Update::FAILED; + } + + if (!DBA::e("UPDATE `contact` SET `failed` = false WHERE `last-item` > `failure_update` AND `failed` IS NULL")) { + return Update::FAILED; + } + + if (!DBA::e("UPDATE `gserver` SET `failed` = true WHERE `last_contact` < `last_failure` AND `failed` IS NULL")) { + return Update::FAILED; + } + + if (!DBA::e("UPDATE `gserver` SET `failed` = false WHERE `last_contact` > `last_failure` AND `failed` IS NULL")) { + return Update::FAILED; + } + + if (!DBA::e("UPDATE `gcontact` SET `failed` = true WHERE `last_contact` < `last_failure` AND `failed` IS NULL")) { + return Update::FAILED; + } + + if (!DBA::e("UPDATE `gcontact` SET `failed` = false WHERE `last_contact` > `last_failure` AND `failed` IS NULL")) { + return Update::FAILED; + } + + if (!DBA::e("UPDATE `gcontact` SET `failed` = false WHERE `updated` > `last_failure` AND `failed` IS NULL")) { + return Update::FAILED; + } + return Update::SUCCESS; +} From ebcf75724467f6334048f785636a32be872156ec Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 19 Jul 2020 10:04:50 +0000 Subject: [PATCH 052/573] database.sql updates --- database.sql | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/database.sql b/database.sql index a988da7bfa..deaa39a5b0 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ --- Friendica 2020.06-dev (Red Hot Poker) --- DB_UPDATE_VERSION 1353 +-- Friendica 2020.09-dev (Red Hot Poker) +-- DB_UPDATE_VERSION 1357 -- ------------------------------------------ @@ -28,6 +28,7 @@ CREATE TABLE IF NOT EXISTS `gserver` ( `last_poco_query` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '', `last_contact` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '', `last_failure` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '', + `failed` boolean COMMENT 'Connection failed', PRIMARY KEY(`id`), UNIQUE INDEX `nurl` (`nurl`(190)) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Global servers'; @@ -95,6 +96,7 @@ CREATE TABLE IF NOT EXISTS `contact` ( `last-update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last try to update the contact info', `success_update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last successful contact update', `failure_update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last failed update', + `failed` boolean COMMENT 'Connection failed', `name-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `uri-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `avatar-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', @@ -137,8 +139,10 @@ CREATE TABLE IF NOT EXISTS `contact` ( INDEX `addr_uid` (`addr`(32),`uid`), INDEX `nurl_uid` (`nurl`(32),`uid`), INDEX `nick_uid` (`nick`(32),`uid`), + INDEX `attag_uid` (`attag`(32),`uid`), INDEX `dfrn-id` (`dfrn-id`(64)), INDEX `issued-id` (`issued-id`(64)), + INDEX `network_uid_lastupdate` (`network`,`uid`,`last-update`), INDEX `gsid` (`gsid`), FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='contact table'; @@ -253,6 +257,7 @@ CREATE TABLE IF NOT EXISTS `apcontact` ( INDEX `addr` (`addr`(32)), INDEX `alias` (`alias`(190)), INDEX `followers` (`followers`(190)), + INDEX `baseurl` (`baseurl`(190)), INDEX `gsid` (`gsid`), FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='ActivityPub compatible contacts - used in the ActivityPub implementation'; @@ -482,6 +487,7 @@ CREATE TABLE IF NOT EXISTS `gcontact` ( `last_contact` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '', `last_failure` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '', `last_discovery` datetime DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last contact discovery', + `failed` boolean COMMENT 'Connection failed', `archive_date` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '', `archived` boolean NOT NULL DEFAULT '0' COMMENT '', `location` varchar(255) NOT NULL DEFAULT '' COMMENT '', From 5bf813d0ec607ba2a276a77dccfcbe13b7664fd6 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 19 Jul 2020 11:42:23 +0000 Subject: [PATCH 053/573] Use "failed" field --- mod/poco.php | 4 ++-- src/Core/Search.php | 4 ++-- src/Model/GContact.php | 21 ++++++++++----------- src/Model/GServer.php | 2 +- src/Module/Admin/Federation.php | 4 ++-- src/Module/Api/Mastodon/Instance/Peers.php | 2 +- src/Object/Api/Mastodon/Stats.php | 2 +- src/Protocol/PortableContact.php | 2 +- src/Worker/SearchDirectory.php | 5 ++--- 9 files changed, 22 insertions(+), 24 deletions(-) diff --git a/mod/poco.php b/mod/poco.php index f1fdc55d75..abe10ee39c 100644 --- a/mod/poco.php +++ b/mod/poco.php @@ -110,7 +110,7 @@ function poco_init(App $a) { $totalResults = DBA::count('profile', ['net-publish' => true]); } else { $contacts = q("SELECT count(*) AS `total` FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0 - AND (`success_update` >= `failure_update` OR `last-item` >= `failure_update`) + AND NOT `failed` AND `network` IN ('%s', '%s', '%s', '%s') $sql_extra", intval($user['uid']), DBA::escape(Protocol::DFRN), @@ -148,7 +148,7 @@ function poco_init(App $a) { } else { Logger::log("Start query for user " . $user['nickname'], Logger::DEBUG); $contacts = q("SELECT * FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0 - AND (`success_update` >= `failure_update` OR `last-item` >= `failure_update`) + AND NOT `failed` AND `network` IN ('%s', '%s', '%s', '%s') $sql_extra LIMIT %d, %d", intval($user['uid']), DBA::escape(Protocol::DFRN), diff --git a/src/Core/Search.php b/src/Core/Search.php index ea979610e6..edc88ffd7d 100644 --- a/src/Core/Search.php +++ b/src/Core/Search.php @@ -180,7 +180,7 @@ class Search $count = DBA::count('gcontact', [ 'NOT `hide` AND `network` IN (?, ?, ?, ?) - AND ((`last_contact` >= `last_failure`) OR (`updated` >= `last_failure`)) + AND NOT `failed` AND (`url` LIKE ? OR `name` LIKE ? OR `location` LIKE ? OR `addr` LIKE ? OR `about` LIKE ? OR `keywords` LIKE ?) AND `community` = ?', @@ -199,7 +199,7 @@ class Search $data = DBA::select('gcontact', ['nurl', 'name', 'addr', 'url', 'photo', 'network', 'keywords'], [ 'NOT `hide` AND `network` IN (?, ?, ?, ?) - AND ((`last_contact` >= `last_failure`) OR (`updated` >= `last_failure`)) + AND NOT `failed` AND (`url` LIKE ? OR `name` LIKE ? OR `location` LIKE ? OR `addr` LIKE ? OR `about` LIKE ? OR `keywords` LIKE ?) AND `community` = ?', diff --git a/src/Model/GContact.php b/src/Model/GContact.php index 00867475a8..6a3c7da74c 100644 --- a/src/Model/GContact.php +++ b/src/Model/GContact.php @@ -95,7 +95,7 @@ class GContact $results = DBA::p("SELECT `nurl` FROM `gcontact` WHERE NOT `hide` AND `network` IN (?, ?, ?, ?) AND - ((`last_contact` >= `last_failure`) OR (`updated` >= `last_failure`)) AND + NOT `failed` AND (`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?) $extra_sql GROUP BY `nurl` ORDER BY `nurl` DESC LIMIT 1000", Protocol::DFRN, Protocol::ACTIVITYPUB, $ostatus, $diaspora, $search, $search, $search @@ -274,8 +274,7 @@ class GContact "SELECT count(*) as `total` FROM `glink` INNER JOIN `gcontact` on `glink`.`gcid` = `gcontact`.`id` WHERE `glink`.`cid` = %d AND `glink`.`uid` = %d AND - ((`gcontact`.`last_contact` >= `gcontact`.`last_failure`) OR - (`gcontact`.`updated` >= `gcontact`.`last_failure`)) + NOT `gcontact`.`failed` AND `gcontact`.`nurl` IN (select nurl from contact where uid = %d and self = 0 and blocked = 0 and hidden = 0 and id != %d) ", intval($cid), intval($uid), @@ -338,7 +337,7 @@ class GContact WHERE `glink`.`cid` = %d and `glink`.`uid` = %d AND `contact`.`uid` = %d AND `contact`.`self` = 0 AND `contact`.`blocked` = 0 AND `contact`.`hidden` = 0 AND `contact`.`id` != %d - AND ((`gcontact`.`last_contact` >= `gcontact`.`last_failure`) OR (`gcontact`.`updated` >= `gcontact`.`last_failure`)) + AND NOT `gcontact`.`failed` $sql_extra LIMIT %d, %d", intval($cid), intval($uid), @@ -397,7 +396,7 @@ class GContact "SELECT count(*) as `total` FROM `glink` INNER JOIN `gcontact` on `glink`.`gcid` = `gcontact`.`id` where `glink`.`cid` = %d and `glink`.`uid` = %d AND - ((`gcontact`.`last_contact` >= `gcontact`.`last_failure`) OR (`gcontact`.`updated` >= `gcontact`.`last_failure`))", + NOT `gcontact`.`failed`", intval($cid), intval($uid) ); @@ -425,7 +424,7 @@ class GContact INNER JOIN `gcontact` on `glink`.`gcid` = `gcontact`.`id` LEFT JOIN `contact` ON `contact`.`nurl` = `gcontact`.`nurl` AND `contact`.`uid` = %d WHERE `glink`.`cid` = %d AND `glink`.`uid` = %d AND - ((`gcontact`.`last_contact` >= `gcontact`.`last_failure`) OR (`gcontact`.`updated` >= `gcontact`.`last_failure`)) + NOT `gcontact`.`failed` ORDER BY `gcontact`.`name` ASC LIMIT %d, %d ", intval($uid), intval($cid), @@ -472,7 +471,7 @@ class GContact AND NOT `gcontact`.`name` IN (SELECT `name` FROM `contact` WHERE `uid` = %d) AND NOT `gcontact`.`id` IN (SELECT `gcid` FROM `gcign` WHERE `uid` = %d) AND `gcontact`.`updated` >= '%s' AND NOT `gcontact`.`hide` - AND `gcontact`.`last_contact` >= `gcontact`.`last_failure` + AND NOT `gcontact`.`failed` AND `gcontact`.`network` IN (%s) GROUP BY `glink`.`gcid` ORDER BY `gcontact`.`updated` DESC,`total` DESC LIMIT %d, %d", intval($uid), @@ -496,7 +495,7 @@ class GContact AND NOT `gcontact`.`name` IN (SELECT `name` FROM `contact` WHERE `uid` = %d) AND NOT `gcontact`.`id` IN (SELECT `gcid` FROM `gcign` WHERE `uid` = %d) AND `gcontact`.`updated` >= '%s' - AND `gcontact`.`last_contact` >= `gcontact`.`last_failure` + AND NOT `gcontact`.`failed` AND `gcontact`.`network` IN (%s) ORDER BY rand() LIMIT %d, %d", intval($uid), @@ -1270,7 +1269,7 @@ class GContact $r = DBA::select('gserver', ['nurl', 'url'], [ '`network` = ? - AND `last_contact` >= `last_failure` + AND NOT `failed` AND `last_poco_query` < ?', Protocol::OSTATUS, $last_update @@ -1421,8 +1420,8 @@ class GContact public static function getRandomUrl() { $r = DBA::selectFirst('gcontact', ['url'], [ - '`network` = ? - AND `last_contact` >= `last_failure` + '`network` = ? + AND NOT `failed` AND `updated` > ?', Protocol::DFRN, DateTimeFormat::utc('now - 1 month'), diff --git a/src/Model/GServer.php b/src/Model/GServer.php index 23056906c2..3d268c37b9 100644 --- a/src/Model/GServer.php +++ b/src/Model/GServer.php @@ -1587,7 +1587,7 @@ class GServer $gservers = DBA::p("SELECT `id`, `url`, `nurl`, `network`, `poco` FROM `gserver` - WHERE `last_contact` >= `last_failure` + WHERE NOT `failed` AND `poco` != '' AND `last_poco_query` < ? ORDER BY RAND()", $last_update diff --git a/src/Module/Admin/Federation.php b/src/Module/Admin/Federation.php index 928a286b14..fd60b07152 100644 --- a/src/Module/Admin/Federation.php +++ b/src/Module/Admin/Federation.php @@ -64,14 +64,14 @@ class Federation extends BaseAdmin $gservers = DBA::p("SELECT COUNT(*) AS `total`, SUM(`registered-users`) AS `users`, `platform`, ANY_VALUE(`network`) AS `network`, MAX(`version`) AS `version` - FROM `gserver` WHERE `last_contact` >= `last_failure` GROUP BY `platform`"); + FROM `gserver` WHERE NOT `failed` GROUP BY `platform`"); while ($gserver = DBA::fetch($gservers)) { $total += $gserver['total']; $users += $gserver['users']; $versionCounts = []; $versions = DBA::p("SELECT COUNT(*) AS `total`, `version` FROM `gserver` - WHERE `last_contact` >= `last_failure` AND `platform` = ? + WHERE NOT `failed` AND `platform` = ? GROUP BY `version` ORDER BY `version`", $gserver['platform']); while ($version = DBA::fetch($versions)) { $version['version'] = str_replace(["\n", "\r", "\t"], " ", $version['version']); diff --git a/src/Module/Api/Mastodon/Instance/Peers.php b/src/Module/Api/Mastodon/Instance/Peers.php index 82f08cbad0..537d25e28c 100644 --- a/src/Module/Api/Mastodon/Instance/Peers.php +++ b/src/Module/Api/Mastodon/Instance/Peers.php @@ -42,7 +42,7 @@ class Peers extends BaseApi $return = []; // We only select for Friendica and ActivityPub servers, since it is expected to only deliver AP compatible systems here. - $instances = DBA::select('gserver', ['url'], ["`network` in (?, ?) AND `last_contact` >= `last_failure`", Protocol::DFRN, Protocol::ACTIVITYPUB]); + $instances = DBA::select('gserver', ['url'], ["`network` in (?, ?) AND NOT `failed`", Protocol::DFRN, Protocol::ACTIVITYPUB]); while ($instance = DBA::fetch($instances)) { $urldata = parse_url($instance['url']); unset($urldata['scheme']); diff --git a/src/Object/Api/Mastodon/Stats.php b/src/Object/Api/Mastodon/Stats.php index 8677cf0425..6ead526729 100644 --- a/src/Object/Api/Mastodon/Stats.php +++ b/src/Object/Api/Mastodon/Stats.php @@ -51,7 +51,7 @@ class Stats extends BaseEntity if (!empty(DI::config()->get('system', 'nodeinfo'))) { $stats->user_count = intval(DI::config()->get('nodeinfo', 'total_users')); $stats->status_count = DI::config()->get('nodeinfo', 'local_posts') + DI::config()->get('nodeinfo', 'local_comments'); - $stats->domain_count = DBA::count('gserver', ["`network` in (?, ?) AND `last_contact` >= `last_failure`", Protocol::DFRN, Protocol::ACTIVITYPUB]); + $stats->domain_count = DBA::count('gserver', ["`network` in (?, ?) AND NOT `failed`", Protocol::DFRN, Protocol::ACTIVITYPUB]); } return $stats; } diff --git a/src/Protocol/PortableContact.php b/src/Protocol/PortableContact.php index f255347c12..d66676cac5 100644 --- a/src/Protocol/PortableContact.php +++ b/src/Protocol/PortableContact.php @@ -228,7 +228,7 @@ class PortableContact { $r = q( "SELECT `url`, `site_name` AS `displayName`, `network`, `platform`, `version` FROM `gserver` - WHERE `network` IN ('%s', '%s', '%s') AND `last_contact` > `last_failure` + WHERE `network` IN ('%s', '%s', '%s') AND NOT `failed` ORDER BY `last_contact` LIMIT 1000", DBA::escape(Protocol::DFRN), diff --git a/src/Worker/SearchDirectory.php b/src/Worker/SearchDirectory.php index 2ffe6120ec..afe54e5fb1 100644 --- a/src/Worker/SearchDirectory.php +++ b/src/Worker/SearchDirectory.php @@ -58,12 +58,11 @@ class SearchDirectory if (!empty($j->results)) { foreach ($j->results as $jj) { // Check if the contact already exists - $gcontact = DBA::selectFirst('gcontact', ['id', 'last_contact', 'last_failure', 'updated'], ['nurl' => Strings::normaliseLink($jj->url)]); + $gcontact = DBA::selectFirst('gcontact', ['failed'], ['nurl' => Strings::normaliseLink($jj->url)]); if (DBA::isResult($gcontact)) { Logger::info('Profile already exists', ['profile' => $jj->url, 'search' => $search]); - if (($gcontact['last_contact'] < $gcontact['last_failure']) && - ($gcontact['updated'] < $gcontact['last_failure'])) { + if ($gcontact['failed']) { continue; } From 1e6b5e8287e91b43251e6d92de677b9f47db8008 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 19 Jul 2020 11:55:42 +0000 Subject: [PATCH 054/573] Another placed replaced with "failed" --- src/Worker/OnePoll.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Worker/OnePoll.php b/src/Worker/OnePoll.php index 1798612108..fa8f748334 100644 --- a/src/Worker/OnePoll.php +++ b/src/Worker/OnePoll.php @@ -97,7 +97,7 @@ class OnePoll } // load current friends if possible. - if (!empty($contact['poco']) && ($contact['success_update'] > $contact['failure_update'])) { + if (!empty($contact['poco']) && !$contact['failed']) { if (!DBA::exists('glink', ["`cid` = ? AND updated > UTC_TIMESTAMP() - INTERVAL 1 DAY", $contact['id']])) { PortableContact::loadWorker($contact['id'], $importer_uid, 0, $contact['poco']); } From a1bbe36dd0f497970737b5f565e6e23f48976150 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 19 Jul 2020 16:45:21 +0000 Subject: [PATCH 055/573] Fix wrong variable --- src/Worker/Notifier.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Worker/Notifier.php b/src/Worker/Notifier.php index 99b97adc8d..01c8008071 100644 --- a/src/Worker/Notifier.php +++ b/src/Worker/Notifier.php @@ -534,7 +534,7 @@ class Notifier } if (self::skipActivityPubForDiaspora($contact, $target_item, $thr_parent)) { - Logger::info('Contact is from Diaspora, but the replied author is from ActivityPub, so skip delivery via Diaspora', ['id' => $target_id, 'url' => $rr['url']]); + Logger::info('Contact is from Diaspora, but the replied author is from ActivityPub, so skip delivery via Diaspora', ['id' => $target_id, 'url' => $contact['url']]); continue; } From 05bd0d0b671ad509465fa6cddabc3c2a07c796a7 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 20 Jul 2020 00:26:42 -0400 Subject: [PATCH 056/573] Add support for multiple Link as urls of Images in ActivityPub\Receiver - Address https://github.com/friendica/friendica/issues/8676#issuecomment-650554955 --- src/Protocol/ActivityPub/Receiver.php | 58 ++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/Protocol/ActivityPub/Receiver.php b/src/Protocol/ActivityPub/Receiver.php index 7a0a9c1f78..aa59b9eb9e 100644 --- a/src/Protocol/ActivityPub/Receiver.php +++ b/src/Protocol/ActivityPub/Receiver.php @@ -877,7 +877,7 @@ class Receiver * * @param array $attachments Attachments in JSON-LD format * - * @return array with attachmants in a simplified format + * @return array Attachments in a simplified format */ private static function processAttachments(array $attachments) { @@ -925,6 +925,62 @@ class Receiver 'url' => JsonLD::fetchElement($attachment, 'as:href', '@id') ]; break; + case 'as:Image': + $mediaType = JsonLD::fetchElement($attachment, 'as:mediaType', '@value'); + $imageFullUrl = JsonLD::fetchElement($attachment, 'as:url', '@id'); + $imagePreviewUrl = null; + // Multiple URLs? + if (!$imageFullUrl && ($urls = JsonLD::fetchElementArray($attachment, 'as:url'))) { + $imageVariants = []; + $previewVariants = []; + foreach ($urls as $url) { + // Scalar URL, no discrimination possible + if (is_string($url)) { + $imageFullUrl = $url; + continue; + } + + // Not sure what to do with a different Link media type than the base Image, we skip + if ($mediaType != JsonLD::fetchElement($url, 'as:mediaType', '@value')) { + continue; + } + + $href = JsonLD::fetchElement($url, 'as:href', '@id'); + + // Default URL choice if no discriminating width is provided + $imageFullUrl = $href ?? $imageFullUrl; + + $width = intval(JsonLD::fetchElement($url, 'as:width', '@value') ?? 1); + + if ($href && $width) { + $imageVariants[$width] = $href; + // 632 is the ideal width for full screen frio posts, we compute the absolute distance to it + $previewVariants[abs(632 - $width)] = $href; + } + } + + if ($imageVariants) { + // Taking the maximum size image + ksort($imageVariants); + $imageFullUrl = array_pop($imageVariants); + + // Taking the minimum number distance to the target distance + ksort($previewVariants); + $imagePreviewUrl = array_shift($previewVariants); + } + + unset($imageVariants); + unset($previewVariants); + } + + $attachlist[] = [ + 'type' => str_replace('as:', '', JsonLD::fetchElement($attachment, '@type')), + 'mediaType' => $mediaType, + 'name' => JsonLD::fetchElement($attachment, 'as:name', '@value'), + 'url' => $imageFullUrl, + 'image' => $imagePreviewUrl !== $imageFullUrl ? $imagePreviewUrl : null, + ]; + break; default: $attachlist[] = [ 'type' => str_replace('as:', '', JsonLD::fetchElement($attachment, '@type')), From 3894976a2d7dd42784488d3ff3ddb7ff53c25bc1 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 20 Jul 2020 00:27:36 -0400 Subject: [PATCH 057/573] Add support for image preview in attachments in ActivityPub\Processor - Address https://github.com/friendica/friendica/issues/8676#issuecomment-650554955 --- src/Protocol/ActivityPub/Processor.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index e4cef17045..177813e32d 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -115,10 +115,22 @@ class Processor continue 2; } + $item['body'] .= "\n"; + + // image is the preview/thumbnail URL + if (!empty($attach['image'])) { + $item['body'] .= '[url=' . $attach['url'] . ']'; + $attach['url'] = $attach['image']; + } + if (empty($attach['name'])) { - $item['body'] .= "\n[img]" . $attach['url'] . '[/img]'; + $item['body'] .= '[img]' . $attach['url'] . '[/img]'; } else { - $item['body'] .= "\n[img=" . $attach['url'] . ']' . $attach['name'] . '[/img]'; + $item['body'] .= '[img=' . $attach['url'] . ']' . $attach['name'] . '[/img]'; + } + + if (!empty($attach['image'])) { + $item['body'] .= '[/url]'; } } elseif ($filetype == 'audio') { if (!empty($activity['source']) && strpos($activity['source'], $attach['url'])) { From 259a62f9ddcc7f15ee258749bc236269c971e05a Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 20 Jul 2020 00:37:43 -0400 Subject: [PATCH 058/573] Separate concerns between postItem and createItem in ActivityPub\Processor - postItem now only posts items created with createItem without altering it - createItem now only creates an item array from an activity without posting it - postItem scope is now public --- src/Protocol/ActivityPub/Processor.php | 142 +++++++++++++------------ src/Protocol/ActivityPub/Receiver.php | 8 +- 2 files changed, 77 insertions(+), 73 deletions(-) diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index e4cef17045..0ff2a588ea 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -158,7 +158,8 @@ class Processor $item = Item::selectFirst(['uri', 'uri-id', 'thr-parent', 'gravity'], ['uri' => $activity['id']]); if (!DBA::isResult($item)) { Logger::warning('No existing item, item will be created', ['uri' => $activity['id']]); - self::createItem($activity); + $item = self::createItem($activity); + self::postItem($activity, $item); return; } @@ -177,6 +178,7 @@ class Processor * Prepares data for a message * * @param array $activity Activity array + * @return array Internal item * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ @@ -204,7 +206,71 @@ class Processor $item['diaspora_signed_text'] = $activity['diaspora:comment'] ?? ''; - self::postItem($activity, $item); + /// @todo What to do with $activity['context']? + if (empty($activity['directmessage']) && ($item['gravity'] != GRAVITY_PARENT) && !Item::exists(['uri' => $item['thr-parent']])) { + Logger::info('Parent not found, message will be discarded.', ['thr-parent' => $item['thr-parent']]); + return []; + } + + $item['network'] = Protocol::ACTIVITYPUB; + $item['author-link'] = $activity['author']; + $item['author-id'] = Contact::getIdForURL($activity['author'], 0, false); + $item['owner-link'] = $activity['actor']; + $item['owner-id'] = Contact::getIdForURL($activity['actor'], 0, false); + + if (in_array(0, $activity['receiver']) && !empty($activity['unlisted'])) { + $item['private'] = Item::UNLISTED; + } elseif (in_array(0, $activity['receiver'])) { + $item['private'] = Item::PUBLIC; + } else { + $item['private'] = Item::PRIVATE; + } + + if (!empty($activity['raw'])) { + $item['source'] = $activity['raw']; + $item['protocol'] = Conversation::PARCEL_ACTIVITYPUB; + $item['conversation-href'] = $activity['context'] ?? ''; + $item['conversation-uri'] = $activity['conversation'] ?? ''; + + if (isset($activity['push'])) { + $item['direction'] = $activity['push'] ? Conversation::PUSH : Conversation::PULL; + } + } + + $item['isForum'] = false; + + if (!empty($activity['thread-completion'])) { + // Store the original actor in the "causer" fields to enable the check for ignored or blocked contacts + $item['causer-link'] = $item['owner-link']; + $item['causer-id'] = $item['owner-id']; + + Logger::info('Ignoring actor because of thread completion.', ['actor' => $item['owner-link']]); + $item['owner-link'] = $item['author-link']; + $item['owner-id'] = $item['author-id']; + } else { + $actor = APContact::getByURL($item['owner-link'], false); + $item['isForum'] = ($actor['type'] == 'Group'); + } + + $item['uri'] = $activity['id']; + + $item['created'] = DateTimeFormat::utc($activity['published']); + $item['edited'] = DateTimeFormat::utc($activity['updated']); + $guid = $activity['sc:identifier'] ?: self::getGUIDByURL($item['uri']); + $item['guid'] = $activity['diaspora:guid'] ?: $guid; + + $item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]); + + $item = self::processContent($activity, $item); + if (empty($item)) { + return []; + } + + $item['plink'] = $activity['alternate-url'] ?? $item['uri']; + + $item = self::constructAttachList($activity, $item); + + return $item; } /** @@ -291,7 +357,7 @@ class Processor */ public static function createActivity($activity, $verb) { - $item = []; + $item = self::createItem($activity); $item['verb'] = $verb; $item['thr-parent'] = $activity['object_id']; $item['gravity'] = GRAVITY_ACTIVITY; @@ -434,72 +500,8 @@ class Processor * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - private static function postItem($activity, $item) + public static function postItem(array $activity, array $item) { - /// @todo What to do with $activity['context']? - if (empty($activity['directmessage']) && ($item['gravity'] != GRAVITY_PARENT) && !Item::exists(['uri' => $item['thr-parent']])) { - Logger::info('Parent not found, message will be discarded.', ['thr-parent' => $item['thr-parent']]); - return; - } - - $item['network'] = Protocol::ACTIVITYPUB; - $item['author-link'] = $activity['author']; - $item['author-id'] = Contact::getIdForURL($activity['author'], 0, false); - $item['owner-link'] = $activity['actor']; - $item['owner-id'] = Contact::getIdForURL($activity['actor'], 0, false); - - if (in_array(0, $activity['receiver']) && !empty($activity['unlisted'])) { - $item['private'] = Item::UNLISTED; - } elseif (in_array(0, $activity['receiver'])) { - $item['private'] = Item::PUBLIC; - } else { - $item['private'] = Item::PRIVATE; - } - - if (!empty($activity['raw'])) { - $item['source'] = $activity['raw']; - $item['protocol'] = Conversation::PARCEL_ACTIVITYPUB; - $item['conversation-href'] = $activity['context'] ?? ''; - $item['conversation-uri'] = $activity['conversation'] ?? ''; - - if (isset($activity['push'])) { - $item['direction'] = $activity['push'] ? Conversation::PUSH : Conversation::PULL; - } - } - - $isForum = false; - - if (!empty($activity['thread-completion'])) { - // Store the original actor in the "causer" fields to enable the check for ignored or blocked contacts - $item['causer-link'] = $item['owner-link']; - $item['causer-id'] = $item['owner-id']; - - Logger::info('Ignoring actor because of thread completion.', ['actor' => $item['owner-link']]); - $item['owner-link'] = $item['author-link']; - $item['owner-id'] = $item['author-id']; - } else { - $actor = APContact::getByURL($item['owner-link'], false); - $isForum = ($actor['type'] == 'Group'); - } - - $item['uri'] = $activity['id']; - - $item['created'] = DateTimeFormat::utc($activity['published']); - $item['edited'] = DateTimeFormat::utc($activity['updated']); - $guid = $activity['sc:identifier'] ?: self::getGUIDByURL($item['uri']); - $item['guid'] = $activity['diaspora:guid'] ?: $guid; - - $item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]); - - $item = self::processContent($activity, $item); - if (empty($item)) { - return; - } - - $item['plink'] = $activity['alternate-url'] ?? $item['uri']; - - $item = self::constructAttachList($activity, $item); - $stored = false; foreach ($activity['receiver'] as $receiver) { @@ -509,7 +511,7 @@ class Processor $item['uid'] = $receiver; - if ($isForum) { + if ($item['isForum'] ?? false) { $item['contact-id'] = Contact::getIdForURL($activity['actor'], $receiver, false); } else { $item['contact-id'] = Contact::getIdForURL($activity['author'], $receiver, false); @@ -527,7 +529,7 @@ class Processor if (DI::pConfig()->get($receiver, 'system', 'accept_only_sharer', false) && ($receiver != 0) && ($item['gravity'] == GRAVITY_PARENT)) { $skip = !Contact::isSharingByURL($activity['author'], $receiver); - if ($skip && (($activity['type'] == 'as:Announce') || $isForum)) { + if ($skip && (($activity['type'] == 'as:Announce') || ($item['isForum'] ?? false))) { $skip = !Contact::isSharingByURL($activity['actor'], $receiver); } diff --git a/src/Protocol/ActivityPub/Receiver.php b/src/Protocol/ActivityPub/Receiver.php index 7a0a9c1f78..5accfec044 100644 --- a/src/Protocol/ActivityPub/Receiver.php +++ b/src/Protocol/ActivityPub/Receiver.php @@ -227,6 +227,7 @@ class Receiver if ($type == 'as:Announce') { $trust_source = false; } + $object_data = self::fetchObject($object_id, $activity['as:object'], $trust_source, $uid); if (empty($object_data)) { Logger::log("Object data couldn't be processed", Logger::DEBUG); @@ -337,7 +338,6 @@ class Receiver if (!JsonLD::fetchElement($activity, 'as:actor', '@id')) { Logger::log('Empty actor', Logger::DEBUG); return; - } // Don't trust the source if "actor" differs from "attributedTo". The content could be forged. @@ -374,7 +374,8 @@ class Receiver switch ($type) { case 'as:Create': if (in_array($object_data['object_type'], self::CONTENT_TYPES)) { - ActivityPub\Processor::createItem($object_data); + $item = ActivityPub\Processor::createItem($object_data); + ActivityPub\Processor::postItem($object_data, $item); } break; @@ -391,7 +392,8 @@ class Receiver // If this isn't set, then a single reshare appears on top. This is used for groups. $object_data['thread-completion'] = ($profile['type'] != 'Group'); - ActivityPub\Processor::createItem($object_data); + $item = ActivityPub\Processor::createItem($object_data); + ActivityPub\Processor::postItem($object_data, $item); // Add the bottom reshare information only for persons if ($profile['type'] != 'Group') { From c26cc5b75e2dda23a137f979a48441d916a7ff32 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 20 Jul 2020 00:38:45 -0400 Subject: [PATCH 059/573] Expand scope of prepareObjectData in ActivityPub\Receiver --- src/Protocol/ActivityPub/Receiver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Protocol/ActivityPub/Receiver.php b/src/Protocol/ActivityPub/Receiver.php index 5accfec044..51157c259a 100644 --- a/src/Protocol/ActivityPub/Receiver.php +++ b/src/Protocol/ActivityPub/Receiver.php @@ -184,7 +184,7 @@ class Receiver * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - private static function prepareObjectData($activity, $uid, $push, &$trust_source) + public static function prepareObjectData($activity, $uid, $push, &$trust_source) { $actor = JsonLD::fetchElement($activity, 'as:actor', '@id'); if (empty($actor)) { From 0a71495fa47cf966ea132342509abf16a33c3759 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 20 Jul 2020 00:39:17 -0400 Subject: [PATCH 060/573] Add new admin debug module for ActivityPub --- src/Module/BaseAdmin.php | 1 + src/Module/Debug/ActivityPubConversion.php | 144 ++++++++++++++++++ static/routes.config.php | 1 + .../templates/debug/activitypubconversion.tpl | 24 +++ 4 files changed, 170 insertions(+) create mode 100644 src/Module/Debug/ActivityPubConversion.php create mode 100644 view/templates/debug/activitypubconversion.tpl diff --git a/src/Module/BaseAdmin.php b/src/Module/BaseAdmin.php index a7b38a5033..01215dc8e8 100644 --- a/src/Module/BaseAdmin.php +++ b/src/Module/BaseAdmin.php @@ -121,6 +121,7 @@ abstract class BaseAdmin extends BaseModule 'webfinger' => ['webfinger' , DI::l10n()->t('check webfinger') , 'webfinger'], 'itemsource' => ['admin/item/source' , DI::l10n()->t('Item Source') , 'itemsource'], 'babel' => ['babel' , DI::l10n()->t('Babel') , 'babel'], + 'debug/ap' => ['debug/ap' , DI::l10n()->t('ActivityPub Conversion') , 'debug/ap'], ]], ]; diff --git a/src/Module/Debug/ActivityPubConversion.php b/src/Module/Debug/ActivityPubConversion.php new file mode 100644 index 0000000000..87a531d5b4 --- /dev/null +++ b/src/Module/Debug/ActivityPubConversion.php @@ -0,0 +1,144 @@ +. + * + */ + +namespace Friendica\Module\Debug; + +use Friendica\BaseModule; +use Friendica\Content\Text; +use Friendica\Core\Logger; +use Friendica\Core\Renderer; +use Friendica\DI; +use Friendica\Model\Item; +use Friendica\Model\Tag; +use Friendica\Protocol\ActivityPub; +use Friendica\Util\JsonLD; +use Friendica\Util\XML; + +class ActivityPubConversion extends BaseModule +{ + public static function content(array $parameters = []) + { + function visible_whitespace($s) + { + return '
' . htmlspecialchars($s) . '
'; + } + + $results = []; + if (!empty($_REQUEST['source'])) { + try { + $source = json_decode($_REQUEST['source'], true); + $trust_source = true; + $uid = local_user(); + $push = false; + + if (!$source) { + throw new \Exception('Failed to decode source JSON'); + } + + $formatted = json_encode($source, JSON_PRETTY_PRINT); + $results[] = [ + 'title' => DI::l10n()->t('Formatted'), + 'content' => visible_whitespace(trim(var_export($formatted, true), "'")), + ]; + $results[] = [ + 'title' => DI::l10n()->t('Source'), + 'content' => visible_whitespace(var_export($source, true)) + ]; + $activity = JsonLD::compact($source); + if (!$activity) { + throw new \Exception('Failed to compact JSON'); + } + $results[] = [ + 'title' => DI::l10n()->t('Activity'), + 'content' => visible_whitespace(var_export($activity, true)) + ]; + + $type = JsonLD::fetchElement($activity, '@type'); + + if (!$type) { + throw new \Exception('Empty type'); + } + + if (!JsonLD::fetchElement($activity, 'as:object', '@id')) { + throw new \Exception('Empty object'); + } + + if (!JsonLD::fetchElement($activity, 'as:actor', '@id')) { + throw new \Exception('Empty actor'); + } + + // Don't trust the source if "actor" differs from "attributedTo". The content could be forged. + if ($trust_source && ($type == 'as:Create') && is_array($activity['as:object'])) { + $actor = JsonLD::fetchElement($activity, 'as:actor', '@id'); + $attributed_to = JsonLD::fetchElement($activity['as:object'], 'as:attributedTo', '@id'); + $trust_source = ($actor == $attributed_to); + if (!$trust_source) { + throw new \Exception('Not trusting actor: ' . $actor . '. It differs from attributedTo: ' . $attributed_to); + } + } + + // $trust_source is called by reference and is set to true if the content was retrieved successfully + $object_data = ActivityPub\Receiver::prepareObjectData($activity, $uid, $push, $trust_source); + if (empty($object_data)) { + throw new \Exception('No object data found'); + } + + if (!$trust_source) { + throw new \Exception('No trust for activity type "' . $type . '", so we quit now.'); + } + + if (!empty($body) && empty($object_data['raw'])) { + $object_data['raw'] = $body; + } + + // Internal flag for thread completion. See Processor.php + if (!empty($activity['thread-completion'])) { + $object_data['thread-completion'] = $activity['thread-completion']; + } + + $results[] = [ + 'title' => DI::l10n()->t('Object data'), + 'content' => visible_whitespace(var_export($object_data, true)) + ]; + + $item = ActivityPub\Processor::createItem($object_data); + + $results[] = [ + 'title' => DI::l10n()->t('Result Item'), + 'content' => visible_whitespace(var_export($item, true)) + ]; + } catch (\Throwable $e) { + $results[] = [ + 'title' => DI::l10n()->t('Error'), + 'content' => $e->getMessage(), + ]; + } + } + + $tpl = Renderer::getMarkupTemplate('debug/activitypubconversion.tpl'); + $o = Renderer::replaceMacros($tpl, [ + '$source' => ['source', DI::l10n()->t('Source activity'), $_REQUEST['source'] ?? '', ''], + '$results' => $results + ]); + + return $o; + } +} diff --git a/static/routes.config.php b/static/routes.config.php index ac78826939..8c3fba99b7 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -107,6 +107,7 @@ return [ '/apps' => [Module\Apps::class, [R::GET]], '/attach/{item:\d+}' => [Module\Attach::class, [R::GET]], '/babel' => [Module\Debug\Babel::class, [R::GET, R::POST]], + '/debug/ap' => [Module\Debug\ActivityPubConversion::class, [R::GET, R::POST]], '/bookmarklet' => [Module\Bookmarklet::class, [R::GET]], '/community[/{content}[/{accounttype}]]' => [Module\Conversation\Community::class, [R::GET]], diff --git a/view/templates/debug/activitypubconversion.tpl b/view/templates/debug/activitypubconversion.tpl new file mode 100644 index 0000000000..dfc6d73677 --- /dev/null +++ b/view/templates/debug/activitypubconversion.tpl @@ -0,0 +1,24 @@ +

ActivityPub Conversion

+
+
+
+ {{include file="field_textarea.tpl" field=$source}} +
+

+
+
+ +{{if $results}} +
+ {{foreach $results as $result}} +
+
+

{{$result.title}}

+
+
+ {{$result.content nofilter}} +
+
+ {{/foreach}} +
+{{/if}} \ No newline at end of file From 39ffb3e74573acefd70222d59070c20345c8ea1c Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 20 Jul 2020 08:02:34 +0000 Subject: [PATCH 061/573] Remove debug output --- src/Model/GServer.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Model/GServer.php b/src/Model/GServer.php index 3d268c37b9..8cad1aad08 100644 --- a/src/Model/GServer.php +++ b/src/Model/GServer.php @@ -1637,7 +1637,6 @@ class GServer if (!empty($data['data']['nodes'])) { foreach ($data['data']['nodes'] as $server) { // Using "only_nodeinfo" since servers that are listed on that page should always have it. - echo $server['host']."\n"; Worker::add(PRIORITY_LOW, 'UpdateGServer', 'https://' . $server['host'], true); } } From ee02be3d48525f87fc3cf6ded9e0e1c693f08213 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Tue, 21 Jul 2020 02:27:05 -0400 Subject: [PATCH 062/573] Improve exception documentation in Model\User --- src/Model/User.php | 51 ++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/Model/User.php b/src/Model/User.php index b4ada344e1..3dbf88f288 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -21,7 +21,9 @@ namespace Friendica\Model; +use DivineOmega\DOFileCachePSR6\CacheItemPool; use DivineOmega\PasswordExposed; +use ErrorException; use Exception; use Friendica\Content\Pager; use Friendica\Core\Hook; @@ -33,7 +35,7 @@ use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\TwoFactor\AppSpecificPassword; -use Friendica\Network\HTTPException\InternalServerErrorException; +use Friendica\Network\HTTPException; use Friendica\Object\Image; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; @@ -41,6 +43,7 @@ use Friendica\Util\Images; use Friendica\Util\Network; use Friendica\Util\Strings; use Friendica\Worker\Delivery; +use ImagickException; use LightOpenID; /** @@ -262,11 +265,11 @@ class User /** * Returns the default group for a given user and network * - * @param int $uid User id + * @param int $uid User id * @param string $network network name * * @return int group id - * @throws InternalServerErrorException + * @throws Exception */ public static function getDefaultGroup($uid, $network = '') { @@ -422,6 +425,7 @@ class User * Generates a human-readable random password * * @return string + * @throws Exception */ public static function generateNewPassword() { @@ -437,7 +441,7 @@ class User */ public static function isPasswordExposed($password) { - $cache = new \DivineOmega\DOFileCachePSR6\CacheItemPool(); + $cache = new CacheItemPool(); $cache->changeConfig([ 'cacheDirectory' => get_temppath() . '/password-exposed-cache/', ]); @@ -446,7 +450,7 @@ class User $passwordExposedChecker = new PasswordExposed\PasswordExposedChecker(null, $cache); return $passwordExposedChecker->passwordExposed($password) === PasswordExposed\PasswordStatus::EXPOSED; - } catch (\Exception $e) { + } catch (Exception $e) { Logger::error('Password Exposed Exception: ' . $e->getMessage(), [ 'code' => $e->getCode(), 'file' => $e->getFile(), @@ -543,7 +547,6 @@ class User * * @param string $nickname The nickname that should be checked * @return boolean True is the nickname is blocked on the node - * @throws InternalServerErrorException */ public static function isNicknameBlocked($nickname) { @@ -579,9 +582,9 @@ class User * * @param array $data * @return array - * @throws \ErrorException - * @throws InternalServerErrorException - * @throws \ImagickException + * @throws ErrorException + * @throws HTTPException\InternalServerErrorException + * @throws ImagickException * @throws Exception */ public static function create(array $data) @@ -707,7 +710,7 @@ class User $nickname = $data['nickname'] = strtolower($nickname); - if (!preg_match('/^[a-z0-9][a-z0-9\_]*$/', $nickname)) { + if (!preg_match('/^[a-z0-9][a-z0-9_]*$/', $nickname)) { throw new Exception(DI::l10n()->t('Your nickname can only contain a-z, 0-9 and _.')); } @@ -896,7 +899,7 @@ class User * * @return bool True, if the allow was successful * - * @throws InternalServerErrorException + * @throws HTTPException\InternalServerErrorException * @throws Exception */ public static function allow(string $hash) @@ -970,16 +973,16 @@ class User * @param string $lang The user's language (default is english) * * @return bool True, if the user was created successfully - * @throws InternalServerErrorException - * @throws \ErrorException - * @throws \ImagickException + * @throws HTTPException\InternalServerErrorException + * @throws ErrorException + * @throws ImagickException */ public static function createMinimal(string $name, string $email, string $nick, string $lang = L10n::DEFAULT) { if (empty($name) || empty($email) || empty($nick)) { - throw new InternalServerErrorException('Invalid arguments.'); + throw new HTTPException\InternalServerErrorException('Invalid arguments.'); } $result = self::create([ @@ -1042,7 +1045,7 @@ class User * @param string $siteurl * @param string $password Plaintext password * @return NULL|boolean from notification() and email() inherited - * @throws InternalServerErrorException + * @throws HTTPException\InternalServerErrorException */ public static function sendRegisterPendingEmail($user, $sitename, $siteurl, $password) { @@ -1078,16 +1081,16 @@ class User * * It's here as a function because the mail is sent from different parts * - * @param \Friendica\Core\L10n $l10n The used language - * @param array $user User record array - * @param string $sitename - * @param string $siteurl - * @param string $password Plaintext password + * @param L10n $l10n The used language + * @param array $user User record array + * @param string $sitename + * @param string $siteurl + * @param string $password Plaintext password * * @return NULL|boolean from notification() and email() inherited - * @throws InternalServerErrorException + * @throws HTTPException\InternalServerErrorException */ - public static function sendRegisterOpenEmail(\Friendica\Core\L10n $l10n, $user, $sitename, $siteurl, $password) + public static function sendRegisterOpenEmail(L10n $l10n, $user, $sitename, $siteurl, $password) { $preamble = Strings::deindent($l10n->t( ' @@ -1144,7 +1147,7 @@ class User /** * @param int $uid user to remove * @return bool - * @throws InternalServerErrorException + * @throws HTTPException\InternalServerErrorException */ public static function remove(int $uid) { From 8565617ea120b7ff3d8783e90070e286e8b119c0 Mon Sep 17 00:00:00 2001 From: Philipp Holzer Date: Sun, 28 Jul 2019 00:14:39 +0200 Subject: [PATCH 063/573] Refactor ExAuth for DICE --- bin/auth_ejabberd.php | 3 +- src/Model/User.php | 9 ++-- src/Util/ExAuth.php | 104 +++++++++++++++++++++++++++--------------- 3 files changed, 75 insertions(+), 41 deletions(-) diff --git a/bin/auth_ejabberd.php b/bin/auth_ejabberd.php index f00615f02b..fa71faf263 100755 --- a/bin/auth_ejabberd.php +++ b/bin/auth_ejabberd.php @@ -80,6 +80,7 @@ $dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['auth_ejabb $appMode = $dice->create(Mode::class); if ($appMode->isNormal()) { - $oAuth = new ExAuth(); + /** @var ExAuth $oAuth */ + $oAuth = $dice->create(ExAuth::class); $oAuth->readStdin(); } diff --git a/src/Model/User.php b/src/Model/User.php index 3dbf88f288..c12ac31cdc 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -321,7 +321,8 @@ class User * @param string $password * @param bool $third_party * @return int User Id if authentication is successful - * @throws Exception + * @throws HTTPException\ForbiddenException + * @throws HTTPException\NotFoundException */ public static function getIdFromPasswordAuthentication($user_info, $password, $third_party = false) { @@ -356,7 +357,7 @@ class User return $user['uid']; } - throw new Exception(DI::l10n()->t('Login failed')); + throw new HTTPException\ForbiddenException(DI::l10n()->t('Login failed')); } /** @@ -370,7 +371,7 @@ class User * * @param mixed $user_info * @return array - * @throws Exception + * @throws HTTPException\NotFoundException */ private static function getAuthenticationInfo($user_info) { @@ -414,7 +415,7 @@ class User } if (!DBA::isResult($user)) { - throw new Exception(DI::l10n()->t('User not found')); + throw new HTTPException\NotFoundException(DI::l10n()->t('User not found')); } } diff --git a/src/Util/ExAuth.php b/src/Util/ExAuth.php index de13ee82f5..9a531b5f75 100644 --- a/src/Util/ExAuth.php +++ b/src/Util/ExAuth.php @@ -34,9 +34,13 @@ namespace Friendica\Util; -use Friendica\Database\DBA; -use Friendica\DI; +use Exception; +use Friendica\App; +use Friendica\Core\Config\IConfig; +use Friendica\Core\PConfig\IPConfig; +use Friendica\Database\Database; use Friendica\Model\User; +use Friendica\Network\HTTPException; class ExAuth { @@ -44,12 +48,43 @@ class ExAuth private $host; /** - * Create the class - * + * @var App\Mode */ - public function __construct() + private $appMode; + /** + * @var IConfig + */ + private $config; + /** + * @var IPConfig + */ + private $pConfig; + /** + * @var Database + */ + private $dba; + /** + * @var App\BaseURL + */ + private $baseURL; + + /** + * @param App\Mode $appMode + * @param IConfig $config + * @param IPConfig $pConfig + * @param Database $dba + * @param App\BaseURL $baseURL + * @throws Exception + */ + public function __construct(App\Mode $appMode, IConfig $config, IPConfig $pConfig, Database $dba, App\BaseURL $baseURL) { - $this->bDebug = (int) DI::config()->get('jabber', 'debug'); + $this->appMode = $appMode; + $this->config = $config; + $this->pConfig = $pConfig; + $this->dba = $dba; + $this->baseURL = $baseURL; + + $this->bDebug = (int)$config->get('jabber', 'debug'); openlog('auth_ejabberd', LOG_PID, LOG_USER); @@ -60,14 +95,18 @@ class ExAuth * Standard input reading function, executes the auth with the provided * parameters * - * @return null - * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws HTTPException\InternalServerErrorException */ public function readStdin() { + if (!$this->appMode->isNormal()) { + $this->writeLog(LOG_ERR, 'The node isn\'t ready.'); + return; + } + while (!feof(STDIN)) { // Quit if the database connection went down - if (!DBA::connected()) { + if (!$this->dba->isConnected()) { $this->writeLog(LOG_ERR, 'the database connection went down'); return; } @@ -123,7 +162,7 @@ class ExAuth * Check if the given username exists * * @param array $aCommand The command array - * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws HTTPException\InternalServerErrorException */ private function isUser(array $aCommand) { @@ -142,9 +181,9 @@ class ExAuth $sUser = str_replace(['%20', '(a)'], [' ', '@'], $aCommand[1]); // Does the hostname match? So we try directly - if (DI::baseUrl()->getHostname() == $aCommand[2]) { + if ($this->baseURL->getHostname() == $aCommand[2]) { $this->writeLog(LOG_INFO, 'internal user check for ' . $sUser . '@' . $aCommand[2]); - $found = DBA::exists('user', ['nickname' => $sUser]); + $found = $this->dba->exists('user', ['nickname' => $sUser]); } else { $found = false; } @@ -173,7 +212,7 @@ class ExAuth * @param boolean $ssl Should the check be done via SSL? * * @return boolean Was the user found? - * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws HTTPException\InternalServerErrorException */ private function checkUser($host, $user, $ssl) { @@ -203,7 +242,7 @@ class ExAuth * Authenticate the given user and password * * @param array $aCommand The command array - * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws Exception */ private function auth(array $aCommand) { @@ -221,35 +260,29 @@ class ExAuth // We now check if the password match $sUser = str_replace(['%20', '(a)'], [' ', '@'], $aCommand[1]); + $Error = false; // Does the hostname match? So we try directly - if (DI::baseUrl()->getHostname() == $aCommand[2]) { - $this->writeLog(LOG_INFO, 'internal auth for ' . $sUser . '@' . $aCommand[2]); - - $aUser = DBA::selectFirst('user', ['uid', 'password', 'legacy_password'], ['nickname' => $sUser]); - if (DBA::isResult($aUser)) { - $uid = $aUser['uid']; - $success = User::authenticate($aUser, $aCommand[3], true); - $Error = $success === false; - } else { - $this->writeLog(LOG_WARNING, 'user not found: ' . $sUser); - $Error = true; - $uid = -1; - } - if ($Error) { + if ($this->baseURL->getHostname() == $aCommand[2]) { + try { + $this->writeLog(LOG_INFO, 'internal auth for ' . $sUser . '@' . $aCommand[2]); + User::getIdFromPasswordAuthentication($sUser, $aCommand[3], true); + } catch (HTTPException\ForbiddenException $ex) { + // User exists, authentication failed $this->writeLog(LOG_INFO, 'check against alternate password for ' . $sUser . '@' . $aCommand[2]); - $sPassword = DI::pConfig()->get($uid, 'xmpp', 'password', null, true); + $aUser = User::getByNickname($sUser, ['uid']); + $sPassword = $this->pConfig->get($aUser['uid'], 'xmpp', 'password', null, true); $Error = ($aCommand[3] != $sPassword); + } catch (\Throwable $ex) { + // User doesn't exist and any other failure case + $this->writeLog(LOG_WARNING, $ex->getMessage() . ': ' . $sUser); + $Error = true; } } else { $Error = true; } // If the hostnames doesn't match or there is some failure, we try to check remotely - if ($Error) { - $Error = !$this->checkCredentials($aCommand[2], $aCommand[1], $aCommand[3], true); - } - - if ($Error) { + if ($Error && !$this->checkCredentials($aCommand[2], $aCommand[1], $aCommand[3], true)) { $this->writeLog(LOG_WARNING, 'authentification failed for user ' . $sUser . '@' . $aCommand[2]); fwrite(STDOUT, pack('nn', 2, 0)); } else { @@ -297,7 +330,6 @@ class ExAuth * Set the hostname for this process * * @param string $host The hostname - * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ private function setHost($host) { @@ -309,7 +341,7 @@ class ExAuth $this->host = $host; - $lockpath = DI::config()->get('jabber', 'lockpath'); + $lockpath = $this->config->get('jabber', 'lockpath'); if (is_null($lockpath)) { $this->writeLog(LOG_INFO, 'No lockpath defined.'); return; From 5344efef71f3a2978d50f1cf24771df28afc5aaa Mon Sep 17 00:00:00 2001 From: nupplaPhil Date: Wed, 4 Mar 2020 22:07:05 +0100 Subject: [PATCH 064/573] 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 8b87bae5d3..f78e8d55aa 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 8d50761db1..faa55a1082 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 f8e4c90236..bdc407b0b1 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 747e0b2f03..4ec47c4cc6 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 97367c3ea5..5238893320 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 751afcc731..6b6c94987d 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 67610140b6..49e41246c9 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 4d33503796..9403d3eb7f 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 d928e66df0..deb97ca1c6 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 db467a2630..8cfb8ce0a1 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 ce34d58ac5..f03ea6104b 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 37b51d2ed9..7b6291ff39 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 e510f1868c..84b589bf29 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 edc88ffd7d..26af05e746 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 fe3d17ad7f..a5c4226c47 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 5966b8c25e..9fc72ac5a4 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 6a3c7da74c..a7cf837bdd 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 8cad1aad08..80ef201a8a 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 9d8b5611f7..125718bf50 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 2fcbde0779..c8fd9d029c 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 b4ada344e1..fda105687d 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 c19b7f7f87..6dcef2ea43 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 e969de9cc3..6214b49dd5 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 f27ffeac58..b65159585b 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 0000000000..6986a93599 --- /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 920dac47e6..dadd794fe6 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 2f8c2f419e..24c6250a41 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 b7c3204b79..21adc58cc0 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 bd99b361e3..46d2bc1d4a 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 9a52476b56..779c99358e 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 d66676cac5..f2ad51070d 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 7708459102..d01ea2ce12 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 de13ee82f5..710c009796 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 d151516bea..5d57c2281f 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 35f0cfc042..9e3be4f4f9 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 ddec359907..888dc20a69 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 b6d172a3a1..577ffd4c11 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 9572342c3e..f0369daab3 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 319a369d1f..23434beb14 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 6c6d26f26c..0dea9841f0 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 fa8f748334..13d1b8ee7a 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 2eb94eeb72..4fa3b3e894 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 afe54e5fb1..1dcf0c8db6 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 065/573] 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 9ed0c5b24d..5986ca9615 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 6986a93599..7d7d59a6de 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 066/573] 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 6b6c94987d..5a3a625cec 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 49e41246c9..0e80d971a6 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 deb97ca1c6..4069518cdd 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 26af05e746..aafa4024a8 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 a7cf837bdd..669bc60c5d 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 80ef201a8a..c868e19d40 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 125718bf50..a10711db2a 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 c8fd9d029c..d32940ae67 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 6dcef2ea43..ad84bb6ab6 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 b65159585b..a025617d8d 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 7d7d59a6de..73e5cd8e0b 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 dadd794fe6..01dc284081 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 24c6250a41..f417080114 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 21adc58cc0..ef7eb0f481 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 779c99358e..a9ee2277e3 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 f2ad51070d..0284c9b8aa 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 710c009796..5933860827 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 5d57c2281f..8c1d4f986e 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 577ffd4c11..cf38ffd7be 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 13d1b8ee7a..0dc67d80f5 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 067/573] 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 f78e8d55aa..f7460b79f3 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 faa55a1082..00732982ed 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 4ec47c4cc6..064107764a 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 73e5cd8e0b..131fac8a78 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 ef7eb0f481..7c87ee0b25 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 46d2bc1d4a..ed369a3047 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 d01ea2ce12..35707b6356 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 8c1d4f986e..84a11b8ec0 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 0dc67d80f5..ed6bfacb35 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 4fa3b3e894..eab68b4304 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 068/573] 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 9403d3eb7f..96f26838ed 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 7b6291ff39..af7c7aa496 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 131fac8a78..14395c37f3 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 0284c9b8aa..f118ffa949 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 069/573] 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 00732982ed..7f7fbe498f 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 bdc407b0b1..8c8557650e 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 5238893320..4746651c16 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 8cfb8ce0a1..592d25c10d 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 f03ea6104b..9d9679c494 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 84b589bf29..e6133240c4 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 aafa4024a8..768a0bf275 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 a5c4226c47..83a24c38fe 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 669bc60c5d..dabc907f89 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 c868e19d40..76082fe1ff 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 fda105687d..9980c48d5a 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 6214b49dd5..deeb8d7ec3 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 14395c37f3..eaef6966d7 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 ed369a3047..5e1f096773 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 35707b6356..921c060f88 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 9e3be4f4f9..2d161a5c43 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 f0369daab3..260d6b16f2 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 23434beb14..1e45058569 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 0dea9841f0..2cab09f339 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 1dcf0c8db6..c3c344d93c 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 070/573] 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 eaef6966d7..ac2d70cad2 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 071/573] 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 9b6f6a5a29..65ae3fe2f4 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 9d9679c494..036bc4a270 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 ac2d70cad2..1a0048b2a1 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 888dc20a69..a8b216b34b 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 072/573] 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 1a0048b2a1..08ac203f5a 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 a665b7c85a..a609ae2963 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 a8b216b34b..7795b0cd29 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 073/573] 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 08ac203f5a..3e5091e076 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 a609ae2963..9aab4f52c6 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 074/573] 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 5a3a625cec..459e9e2c92 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 0e80d971a6..a1faab6efb 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 4069518cdd..b2f76738be 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 036bc4a270..bf208a1036 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 768a0bf275..c5c6ca08c2 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 dabc907f89..109f5d54b3 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 76082fe1ff..ae4332511d 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 a10711db2a..7d984a8ce6 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 d32940ae67..0cff8ba287 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 9980c48d5a..78ae958041 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 ad84bb6ab6..a130c48393 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 a025617d8d..95b742bb30 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 3e5091e076..f9279fa602 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 01dc284081..c41006b128 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 f417080114..6b29eabce5 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 7c87ee0b25..8190806a04 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 a9ee2277e3..9c87f367ad 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 f118ffa949..5216e39370 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 5933860827..25eb3cc62e 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 84a11b8ec0..89da59ba26 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 cf38ffd7be..01ad79d4f1 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 ed6bfacb35..fbd1ab4e59 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 075/573] 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 7f7fbe498f..183f6022e3 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 8c8557650e..cb21a211f9 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 4746651c16..b8da9df7ef 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 96f26838ed..3445436182 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 592d25c10d..30a113f461 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 bf208a1036..1181c8f47b 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 af7c7aa496..28db93d292 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 c5c6ca08c2..577b11266e 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 83a24c38fe..937dd0a565 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 109f5d54b3..ab0a4fdd84 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 ae4332511d..7643c9590e 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 deeb8d7ec3..1da0457c45 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 f9279fa602..c751406c1b 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 5e1f096773..98a315ce21 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 5216e39370..cfc140d66d 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 2d161a5c43..ef171873f2 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 260d6b16f2..be325461b0 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 1e45058569..4f988b6e14 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 2cab09f339..d71e593dc5 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 c3c344d93c..546c369b2c 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 076/573] 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 5986ca9615..39e892adcb 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 c751406c1b..839586880c 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 0000000000..3ebcc5dc1b --- /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 84344a60e2..fe8a8caee0 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 077/573] 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 e6133240c4..7b9789752e 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 078/573] 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 f512bf17b9..6ec2f1bc7c 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 079/573] 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 e31097f53c..46f2fb09a7 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 6b29eabce5..3c4f4f2e67 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 e4cef17045..04d8a7467f 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 ab8eef24c52273de0a43714198ba13fdbf17ed8f Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 21 Jul 2020 08:35:57 +0000 Subject: [PATCH 080/573] Posts with subscribed tags will now be stored for the user --- src/Model/Item.php | 64 ++++++++++++++++++++++++++++++++++------------ src/Model/Tag.php | 38 ++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 17 deletions(-) diff --git a/src/Model/Item.php b/src/Model/Item.php index e31097f53c..39fb3cb54c 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -1964,6 +1964,9 @@ class Item check_user_notification($current_post); + // Distribute items to users who subscribed to their tags + self::distributeByTags($item, $orig_item); + $transmit = $notify || ($item['visible'] && ($parent_origin || $item['origin'])); if ($transmit) { @@ -1983,6 +1986,26 @@ class Item return $current_post; } + /** + * Distribute the given item to users who subscribed to their tags + * + * @param array $item Processed item + * @param array $original Original item + */ + private static function distributeByTags(array $item, array $original) + { + if (($item['uid'] != 0) || ($item['gravity'] != GRAVITY_PARENT) || !in_array($item['network'], Protocol::FEDERATED)) { + return; + } + + $uids = Tag::getUIDListByURIId($item['uri-id']); + foreach ($uids as $uid) { + $original['uri-id'] = $item['uri-id']; + $stored = self::storeForUser($original, $uid); + Logger::info('Stored item for users', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'stored' => $stored]); + } + } + /** * Insert a new item content entry * @@ -2079,13 +2102,6 @@ class Item $origin = $item['origin']; - unset($item['id']); - unset($item['parent']); - unset($item['mention']); - unset($item['wall']); - unset($item['origin']); - unset($item['starred']); - $users = []; /// @todo add a field "pcid" in the contact table that referrs to the public contact id. @@ -2145,33 +2161,48 @@ class Item if ($origin_uid == $uid) { $item['diaspora_signed_text'] = $signed_text; } - self::storeForUser($itemid, $item, $uid); + self::storeForUser($item, $uid); } } /** * Store public items for the receivers * - * @param integer $itemid Item ID that should be added * @param array $item The item entry that will be stored * @param integer $uid The user that will receive the item entry + * @return integer stored item id * @throws \Exception */ - private static function storeForUser($itemid, $item, $uid) + private static function storeForUser(array $item, int $uid) { + if (self::exists(['uri-id' => $item['uri-id'], 'uid' => $uid])) { + Logger::info('Item already exists', ['uri-id' => $item['uri-id'], 'uid' => $uid]); + return 0; + } + + unset($item['id']); + unset($item['parent']); + unset($item['mention']); + unset($item['starred']); + $item['uid'] = $uid; $item['origin'] = 0; $item['wall'] = 0; + if ($item['uri'] == $item['parent-uri']) { - $item['contact-id'] = Contact::getIdForURL($item['owner-link'], $uid); + $contact = Contact::getByURLForUser($item['owner-link'], $uid, false, ['id']); } else { - $item['contact-id'] = Contact::getIdForURL($item['author-link'], $uid); + $contact = Contact::getByURLForUser($item['author-link'], $uid, false, ['id']); } - if (empty($item['contact-id'])) { + if (!empty($item['contact-id'])) { + $item['contact-id'] = $contact['id']; + } else { + // Shouldn't happen at all $self = DBA::selectFirst('contact', ['id'], ['self' => true, 'uid' => $uid]); if (!DBA::isResult($self)) { - return; + // Shouldn't happen even less + return 0; } $item['contact-id'] = $self['id']; } @@ -2189,10 +2220,11 @@ class Item $distributed = self::insert($item, $notify, true); if (!$distributed) { - Logger::info("Distributed public item wasn't stored", ['id' => $itemid, 'user' => $uid]); + Logger::info("Distributed public item wasn't stored", ['uri-id' => $item['uri-id'], 'user' => $uid]); } else { - Logger::info('Distributed public item was stored', ['id' => $itemid, 'user' => $uid, 'stored' => $distributed]); + Logger::info('Distributed public item was stored', ['uri-id' => $item['uri-id'], 'user' => $uid, 'stored' => $distributed]); } + return $distributed; } /** diff --git a/src/Model/Tag.php b/src/Model/Tag.php index 3424a23771..a48f2cb92b 100644 --- a/src/Model/Tag.php +++ b/src/Model/Tag.php @@ -536,5 +536,41 @@ class Tag } return Strings::startsWithChars($tag, $tag_chars); - } + } + + /** + * Fetch user who subscribed to the given tag + * + * @param string $tag + * @return array User list + */ + private static function getUIDListByTag(string $tag) + { + $uids = []; + $searches = DBA::select('search', ['uid'], ['term' => $tag]); + while ($search = DBA::fetch($searches)) { + $uids[] = $search['uid']; + } + DBA::close($searches); + + return $uids; + } + + /** + * Fetch user who subscribed to the tags of the given item + * + * @param integer $uri_id + * @return array User list + */ + public static function getUIDListByURIId(int $uri_id) + { + $uids = []; + $tags = self::getByURIId($uri_id, [self::HASHTAG]); + + foreach ($tags as $tag) { + $uids = array_merge($uids, self::getUIDListByTag(self::TAG_CHARACTER[self::HASHTAG] . $tag['name'])); + } + + return array_unique($uids); + } } From 186bc467218a2dd985281ab4b28f3a16fd82e275 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 21 Jul 2020 14:13:19 +0000 Subject: [PATCH 081/573] Don't load tag postings on the network page anymore --- mod/network.php | 75 +------------------------------------------------ 1 file changed, 1 insertion(+), 74 deletions(-) diff --git a/mod/network.php b/mod/network.php index 3311a796a8..f847e6757e 100644 --- a/mod/network.php +++ b/mod/network.php @@ -271,7 +271,7 @@ function networkConversation(App $a, $items, Pager $pager, $mode, $update, $orde $a->page_contact = $a->contact; if (!is_array($items)) { - Logger::log("Expecting items to be an array. Got " . print_r($items, true)); + Logger::info('Expecting items to be an array.', ['items' => $items]); $items = []; } @@ -541,7 +541,6 @@ function networkThreadedView(App $a, $update, $parent) } $sql_nets = (($nets) ? sprintf(" AND $sql_table.`network` = '%s' ", DBA::escape($nets)) : ''); - $sql_tag_nets = (($nets) ? sprintf(" AND `item`.`network` = '%s' ", DBA::escape($nets)) : ''); if ($gid) { $group = DBA::selectFirst('group', ['name'], ['id' => $gid, 'uid' => local_user()]); @@ -739,78 +738,6 @@ function networkThreadedView(App $a, $update, $parent) ); } - // Only show it when unfiltered (no groups, no networks, ...) - if (in_array($nets, ['', Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS]) && (strlen($sql_extra . $sql_extra2 . $sql_extra3) == 0)) { - if (DBA::isResult($r)) { - $top_limit = current($r)['order_date']; - $bottom_limit = end($r)['order_date']; - if (empty($_SESSION['network_last_top_limit']) || ($_SESSION['network_last_top_limit'] < $top_limit)) { - $_SESSION['network_last_top_limit'] = $top_limit; - } - } else { - $top_limit = $bottom_limit = DateTimeFormat::utcNow(); - } - - // When checking for updates we need to fetch from the newest date to the newest date before - // Only do this, when the last stored date isn't too long ago (10 times the update interval) - $browser_update = DI::pConfig()->get(local_user(), 'system', 'update_interval', 40000) / 1000; - - if (($browser_update > 0) && $update && !empty($_SESSION['network_last_date']) && - (($bottom_limit < $_SESSION['network_last_date']) || ($top_limit == $bottom_limit)) && - ((time() - $_SESSION['network_last_date_timestamp']) < ($browser_update * 10))) { - $bottom_limit = $_SESSION['network_last_date']; - } - $_SESSION['network_last_date'] = Session::get('network_last_top_limit', $top_limit); - $_SESSION['network_last_date_timestamp'] = time(); - - if ($last_date > $top_limit) { - $top_limit = $last_date; - } elseif ($pager->getPage() == 1) { - // Highest possible top limit when we are on the first page - $top_limit = DateTimeFormat::utcNow(); - } - - // Handle bad performance situations when the distance between top and bottom is too high - // See issue https://github.com/friendica/friendica/issues/8619 - if (strtotime($top_limit) - strtotime($bottom_limit) > 86400) { - // Set the bottom limit to one day in the past at maximum - $bottom_limit = DateTimeFormat::utc(date('c', strtotime($top_limit) - 86400)); - } - - $items = DBA::p("SELECT `item`.`parent-uri` AS `uri`, 0 AS `item_id`, `item`.$ordering AS `order_date`, `author`.`url` AS `author-link` FROM `item` - STRAIGHT_JOIN (SELECT `uri-id` FROM `tag-search-view` WHERE `name` IN - (SELECT SUBSTR(`term`, 2) FROM `search` WHERE `uid` = ? AND `term` LIKE '#%') AND `uid` = 0) AS `tag-search` - ON `item`.`uri-id` = `tag-search`.`uri-id` - STRAIGHT_JOIN `contact` AS `author` ON `author`.`id` = `item`.`author-id` - WHERE `item`.`uid` = 0 AND `item`.$ordering < ? AND `item`.$ordering > ? AND `item`.`gravity` = ? - AND NOT `author`.`hidden` AND NOT `author`.`blocked`" . $sql_tag_nets, - local_user(), $top_limit, $bottom_limit, GRAVITY_PARENT); - - $data = DBA::toArray($items); - - if (count($data) > 0) { - $tag_top_limit = current($data)['order_date']; - if ($_SESSION['network_last_date'] < $tag_top_limit) { - $_SESSION['network_last_date'] = $tag_top_limit; - } - - Logger::log('Tagged items: ' . count($data) . ' - ' . $bottom_limit . ' - ' . $top_limit . ' - ' . local_user().' - '.(int)$update); - $s = []; - foreach ($r as $item) { - $s[$item['uri']] = $item; - } - foreach ($data as $item) { - // Don't show hash tag posts from blocked or ignored contacts - $condition = ["`nurl` = ? AND `uid` = ? AND (`blocked` OR `readonly`)", - Strings::normaliseLink($item['author-link']), local_user()]; - if (!DBA::exists('contact', $condition)) { - $s[$item['uri']] = $item; - } - } - $r = $s; - } - } - $parents_str = ''; $date_offset = ''; From e2826a98d355e030bab1695996bf8cacfa0c2442 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 21 Jul 2020 18:30:45 +0000 Subject: [PATCH 082/573] Added logging --- src/Model/Item.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Model/Item.php b/src/Model/Item.php index 39fb3cb54c..96aeb63816 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -2199,9 +2199,11 @@ class Item $item['contact-id'] = $contact['id']; } else { // Shouldn't happen at all + Logger::warning('contact-id could not be fetched', ['uid' => $uid, 'item' => $item]); $self = DBA::selectFirst('contact', ['id'], ['self' => true, 'uid' => $uid]); if (!DBA::isResult($self)) { // Shouldn't happen even less + Logger::warning('self contact could not be fetched', ['uid' => $uid, 'item' => $item]); return 0; } $item['contact-id'] = $self['id']; From 1d9ef1a3d84158c73cd8903fb38a9637ca153f5e Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 21 Jul 2020 18:53:01 +0000 Subject: [PATCH 083/573] Corrected variable in condition --- src/Model/Item.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/Item.php b/src/Model/Item.php index 96aeb63816..f7e7ae8737 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -2195,7 +2195,7 @@ class Item $contact = Contact::getByURLForUser($item['author-link'], $uid, false, ['id']); } - if (!empty($item['contact-id'])) { + if (!empty($contact['id'])) { $item['contact-id'] = $contact['id']; } else { // Shouldn't happen at all From 990c70807dad6407d94acba098f9b574b5738a33 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 21 Jul 2020 19:43:07 +0000 Subject: [PATCH 084/573] Fix notice "Undefined index: parent-uri" --- src/Model/Item.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Model/Item.php b/src/Model/Item.php index f7e7ae8737..860d9d73c9 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -2001,6 +2001,7 @@ class Item $uids = Tag::getUIDListByURIId($item['uri-id']); foreach ($uids as $uid) { $original['uri-id'] = $item['uri-id']; + $original['gravity'] = $item['gravity']; $stored = self::storeForUser($original, $uid); Logger::info('Stored item for users', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'stored' => $stored]); } @@ -2189,7 +2190,7 @@ class Item $item['origin'] = 0; $item['wall'] = 0; - if ($item['uri'] == $item['parent-uri']) { + if ($item['gravity'] == GRAVITY_PARENT) { $contact = Contact::getByURLForUser($item['owner-link'], $uid, false, ['id']); } else { $contact = Contact::getByURLForUser($item['author-link'], $uid, false, ['id']); @@ -2212,7 +2213,7 @@ class Item /// @todo Handling of "event-id" $notify = false; - if ($item['uri'] == $item['parent-uri']) { + if ($item['gravity'] == GRAVITY_PARENT) { $contact = DBA::selectFirst('contact', [], ['id' => $item['contact-id'], 'self' => false]); if (DBA::isResult($contact)) { $notify = self::isRemoteSelf($contact, $item); From 5545bafdee61232e9afa0f7e257e8f7a61627bb8 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 21 Jul 2020 19:55:24 +0000 Subject: [PATCH 085/573] Fix notice "Undefined index: gravity in /src/Protocol/ActivityPub/Processor.php on line 556" --- src/Protocol/ActivityPub/Processor.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 241907f623..db1492aa1d 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -514,6 +514,10 @@ class Processor */ public static function postItem(array $activity, array $item) { + if (empty($item)) { + return; + } + $stored = false; foreach ($activity['receiver'] as $receiver) { From d0ef6f2b08b73dd53bb1f6e5c408860f57921b32 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 21 Jul 2020 23:26:01 +0000 Subject: [PATCH 086/573] Store personal copy of public item upon commenting --- mod/item.php | 10 ++++++++++ src/Model/Item.php | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/mod/item.php b/mod/item.php index e2d47ae2fa..c4d7231c22 100644 --- a/mod/item.php +++ b/mod/item.php @@ -136,6 +136,16 @@ function item_post(App $a) { throw new HTTPException\NotFoundException(DI::l10n()->t('Unable to locate original post.')); } + // When commenting on a public post then store the post for the current user + // This enables interaction like starring and saving into folders + if ($toplevel_item['uid'] == 0) { + $stored = Item::storeForUser($toplevel_item, local_user()); + Logger::info('Public item stored for user', ['uri-id' => $toplevel_item['uri-id'], 'uid' => $uid, 'stored' => $stored]); + if ($stored) { + $toplevel_item = Item::selectFirst([], ['id' => $stored]); + } + } + $toplevel_item_id = $toplevel_item['id']; $parent_user = $toplevel_item['uid']; diff --git a/src/Model/Item.php b/src/Model/Item.php index 860d9d73c9..afecc41162 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -2174,7 +2174,7 @@ class Item * @return integer stored item id * @throws \Exception */ - private static function storeForUser(array $item, int $uid) + public static function storeForUser(array $item, int $uid) { if (self::exists(['uri-id' => $item['uri-id'], 'uid' => $uid])) { Logger::info('Item already exists', ['uri-id' => $item['uri-id'], 'uid' => $uid]); From ec3ec3b78a2a431b82ff16136dddd5e6900c3e1e Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Tue, 21 Jul 2020 21:39:59 -0400 Subject: [PATCH 087/573] 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 ab0a4fdd84..41ca763fc6 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 7643c9590e..0f47146eb7 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 da349a1814d3a474ca7a1c9cf3edf1740adf6e4f Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 22 Jul 2020 05:16:57 +0000 Subject: [PATCH 088/573] Store copy on activities --- src/Model/Item.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Model/Item.php b/src/Model/Item.php index afecc41162..70e6d7cea8 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -3015,6 +3015,14 @@ class Item return false; } + if (!Item::exists(['uri-id' => $item['parent-uri-id'], 'uid' => $uid])) { + $parent_item = self::selectFirst(self::ITEM_FIELDLIST, ['uri-id' => $item['parent-uri-id'], 'uid' => 0]); + if (!empty($parent_item) && ($parent_item['private'] =! self::PRIVATE)) { + $stored = self::storeForUser($parent_item, $uid); + Logger::info('Public item stored for user', ['uri-id' => $parent_item['uri-id'], 'uid' => $uid, 'stored' => $stored]); + } + } + // Retrieves the local post owner $owner_self_contact = DBA::selectFirst('contact', [], ['uid' => $uid, 'self' => true]); if (!DBA::isResult($owner_self_contact)) { From abdcf7ca88e6c1875e1a5c20894550ad0dd7631a Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 22 Jul 2020 05:34:56 +0000 Subject: [PATCH 089/573] Fix "!=" --- src/Model/Item.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/Item.php b/src/Model/Item.php index 70e6d7cea8..5cce6096fc 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -3017,7 +3017,7 @@ class Item if (!Item::exists(['uri-id' => $item['parent-uri-id'], 'uid' => $uid])) { $parent_item = self::selectFirst(self::ITEM_FIELDLIST, ['uri-id' => $item['parent-uri-id'], 'uid' => 0]); - if (!empty($parent_item) && ($parent_item['private'] =! self::PRIVATE)) { + if (!empty($parent_item) && ($parent_item['private'] != self::PRIVATE)) { $stored = self::storeForUser($parent_item, $uid); Logger::info('Public item stored for user', ['uri-id' => $parent_item['uri-id'], 'uid' => $uid, 'stored' => $stored]); } From 8572cec0cb40aced0a7a25a43087b0767a966565 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 22 Jul 2020 10:42:53 -0400 Subject: [PATCH 090/573] [Composer] Add new dependency npm-asset/textcomplete --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index b3dd0ec903..2d6b46574d 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 091/573] [Composer] Update Composer lock file --- composer.lock | 270 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 210 insertions(+), 60 deletions(-) diff --git a/composer.lock b/composer.lock index 45c6137a4f..9f6f78d000 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 092/573] 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 7f5f36cfd7..c3993603b4 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 f1ffcf69a5..ed87017dc4 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 9ad0c8a7e9..4015a325a5 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 093/573] 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 e115bf9af0..0000000000 --- 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 4848bd6377..0000000000 --- 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 d74dfbd902..0000000000 --- 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 37a761b7e4..0000000000 --- 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 69ae1394ad..0000000000 --- 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 4cdb660295..0000000000 --- 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 e27ef4d40d..0000000000 --- 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 ca9f8e7420d925fb316f8841f338a4583d94fe51 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 23 Jul 2020 03:26:54 +0000 Subject: [PATCH 094/573] New function "Item::storeForUserByUriId" --- mod/item.php | 2 +- src/Model/Item.php | 40 +++++++++++++++++++++++++++------------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/mod/item.php b/mod/item.php index c4d7231c22..f6157f3dbc 100644 --- a/mod/item.php +++ b/mod/item.php @@ -139,7 +139,7 @@ function item_post(App $a) { // When commenting on a public post then store the post for the current user // This enables interaction like starring and saving into folders if ($toplevel_item['uid'] == 0) { - $stored = Item::storeForUser($toplevel_item, local_user()); + $stored = Item::storeForUserByUriId($toplevel_item['uri-id'], local_user()); Logger::info('Public item stored for user', ['uri-id' => $toplevel_item['uri-id'], 'uid' => $uid, 'stored' => $stored]); if ($stored) { $toplevel_item = Item::selectFirst([], ['id' => $stored]); diff --git a/src/Model/Item.php b/src/Model/Item.php index 5cce6096fc..98e4cdf307 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -1965,7 +1965,7 @@ class Item check_user_notification($current_post); // Distribute items to users who subscribed to their tags - self::distributeByTags($item, $orig_item); + self::distributeByTags($item); $transmit = $notify || ($item['visible'] && ($parent_origin || $item['origin'])); @@ -1990,9 +1990,8 @@ class Item * Distribute the given item to users who subscribed to their tags * * @param array $item Processed item - * @param array $original Original item */ - private static function distributeByTags(array $item, array $original) + private static function distributeByTags(array $item) { if (($item['uid'] != 0) || ($item['gravity'] != GRAVITY_PARENT) || !in_array($item['network'], Protocol::FEDERATED)) { return; @@ -2000,9 +1999,7 @@ class Item $uids = Tag::getUIDListByURIId($item['uri-id']); foreach ($uids as $uid) { - $original['uri-id'] = $item['uri-id']; - $original['gravity'] = $item['gravity']; - $stored = self::storeForUser($original, $uid); + $stored = self::storeForUserByUriId($item['uri-id'], $uid); Logger::info('Stored item for users', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'stored' => $stored]); } } @@ -2167,14 +2164,33 @@ class Item } /** - * Store public items for the receivers + * Store a public item defined by their URI-ID for the given users + * + * @param integer $uri_id URI-ID of the given item + * @param integer $uid The user that will receive the item entry + * @return integer stored item id + */ + public static function storeForUserByUriId(int $uri_id, int $uid) + { + $item = self::selectFirst(self::ITEM_FIELDLIST, ['uri-id' => $uri_id, 'uid' => 0]); + if (empty($item) || ($item['private'] != self::PRIVATE)) { + return 0; + } + + $stored = self::storeForUser($item, $uid); + Logger::info('Public item stored for user', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'stored' => $stored]); + return $stored; + } + + /** + * Store a public item array for the given users * * @param array $item The item entry that will be stored * @param integer $uid The user that will receive the item entry * @return integer stored item id * @throws \Exception */ - public static function storeForUser(array $item, int $uid) + private static function storeForUser(array $item, int $uid) { if (self::exists(['uri-id' => $item['uri-id'], 'uid' => $uid])) { Logger::info('Item already exists', ['uri-id' => $item['uri-id'], 'uid' => $uid]); @@ -2185,6 +2201,8 @@ class Item unset($item['parent']); unset($item['mention']); unset($item['starred']); + unset($item['unseen']); + unset($item['psid']); $item['uid'] = $uid; $item['origin'] = 0; @@ -3016,11 +3034,7 @@ class Item } if (!Item::exists(['uri-id' => $item['parent-uri-id'], 'uid' => $uid])) { - $parent_item = self::selectFirst(self::ITEM_FIELDLIST, ['uri-id' => $item['parent-uri-id'], 'uid' => 0]); - if (!empty($parent_item) && ($parent_item['private'] != self::PRIVATE)) { - $stored = self::storeForUser($parent_item, $uid); - Logger::info('Public item stored for user', ['uri-id' => $parent_item['uri-id'], 'uid' => $uid, 'stored' => $stored]); - } + $stored = self::storeForUserByUriId($item['parent-uri-id'], $uid); } // Retrieves the local post owner From 3fc3ded750afe4b90c95f3036bded015ed5c9572 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 23 Jul 2020 06:11:21 +0000 Subject: [PATCH 095/573] 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 437cc160b4..0c16044b48 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 141fa9fdba..ac07d040cc 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 c4d7231c22..57fb64e3da 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 438f96030b..4404e6aff8 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 f847e6757e..2afc90dbf1 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 97367c3ea5..6a05d49944 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 f33a9241ef..4118b80695 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 9b2f4f650e..14db27e6f8 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 4022f999db..179276663b 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 09466ba802..5ccc9c859a 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 e5b482a65e..82d87ca290 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 678bb0058c..a0ce5df650 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 4b9eb68bdd..58c595cb7b 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 06ba6398a8..ed131910c7 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 0b728e33d7..a2c8bb4397 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 3049cdc6a7..d52085389d 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 4f19ca361d..eccb65598f 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 5158108e46..b60936c785 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 2e16cc657e..c4b320e72d 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 c8d0578382..405e289022 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 675e33c846..37de7c2384 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 955ddadc70..78d27dfa0b 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 811a0eb25c..a3bc94a1fe 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 e67d3c3c93..57b5976ef0 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 f63d42c0ea..ee8ad3663d 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 d29d0609a9..8640808439 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 b4adff46d3..9f2ae7bde6 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 5637c6f419..c86bf9176c 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 e969de9cc3..f0f86f607f 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 3d03f10711..491023145c 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 7866656e33..a8a8a896b3 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 12226107ba..4b2fdb09e8 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 11e7f1a760..d5f1fc8ef8 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 0b1cb9e6a3..4b05b8c2cd 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 3d55c57f48..dbf0cf8d84 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 23f12d2638..bf3ae2585b 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 73372b03a0..0f45b50f5b 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 1335a8211e..a7e02f4299 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 3e4f9b8a4e..df9622f2e9 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 096/573] 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 47a809497e..474d57af4a 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 8db2237844..0e8e8a2af3 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 9231d090b8..0ff523b3f7 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 8b87bae5d3..f8d2be44d6 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 8d50761db1..d19ae287c0 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 f8e4c90236..eb323c2482 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 8bde032933..cfca7695e0 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 0c16044b48..69f6b6f326 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 57fb64e3da..7faec2e4e6 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 211477b0db..036a308df5 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 747e0b2f03..b7a9d3a567 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 4404e6aff8..204b136f9b 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 2afc90dbf1..1aa8046db1 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 67f8fcab29..f1cf6cfde5 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 751afcc731..a1b378001f 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 4118b80695..e5ab6b8dce 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 33e97499e5..0d30fc298d 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 14db27e6f8..d02375ed7c 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 9cd2fb1cd9..66d22b001e 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 eb99a366f2..8abff0cd99 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 a3344a8b43..3dd17179ae 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 c02a06c375..8cb19ab1a0 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 093d5db773..c3ba323043 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 82d87ca290..cbf53b45d6 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 a0ce5df650..e3d2737470 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 b4dbb87d82..5376b817fc 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 eccb65598f..b145d6d019 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 dd7febcc59..950e258f8e 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 0ad20f97c9..b907d39bda 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 dca8c9c2e6..41a691b0b7 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 04c7d7b6ac..29a735121d 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 ee8ad3663d..096f693302 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 8640808439..5fa86ab378 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 491023145c..507da6b942 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 28c849a861..f4e4c5ebf9 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 98668bf71d..cb944a3fd3 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 4b05b8c2cd..cd59626e68 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 dbf0cf8d84..4cd97b4097 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 9ab15a4e36..4b36cdd4de 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 097/573] 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 036a308df5..01e0006e95 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 1aa8046db1..a3eb169839 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 d52085389d..1636d6cc69 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 c4b320e72d..290d92d139 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 78d27dfa0b..9993f1f117 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 098/573] 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 c41006b128..3c43fb28ea 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 099/573] 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 668c6d2e27..bd52a67cff 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 e93df399f2..1e485e3040 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 09d5277331..708c375b07 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 b5a9540991..1a561c7f13 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 5fa86ab378..be1e874a57 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 8190806a04..f213af8db3 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 98a315ce21..f3b95db68f 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 9c87f367ad..1cf2894eb5 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 100/573] 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 deaa39a5b0..ea89847c19 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 708c375b07..1cf8c74237 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 0000000000..19baaef795 --- /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 41ca763fc6..ec53133c94 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 1a561c7f13..d8fbac8307 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 290d92d139..c1d4479d9a 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 94e4d07d7b..3f71241fa5 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 14a52aa9ad..56412b20bd 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 dc76db31c5..49bae09ef7 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 ddd6606a7a..5f5df4aac3 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 101/573] 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 19baaef795..dba1d53c89 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 102/573] 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 dba1d53c89..06c25059fa 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 103/573] 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 54363cb1d7..c1861c7913 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 745010ff4e..2ff7495497 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 0000000000..59478b326c --- /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 8c3fba99b7..ddfabd7780 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 60337918b4..36c9cbb88d 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 7170ceb333..0d7ccb20fa 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 38aa947498..1a756db8a3 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 0d8c896e16..cec886253f 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 91e9dafe44..de45eecff0 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 a5b6d52d6d..2cd231f8fb 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 c15b110ec9..5e7f49bf48 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 1ce336b0a6..11947643c6 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 0e4aaaf8f7..cb400ac4f5 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 e6d8b97547..b6907219ff 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 23af1b794b..b3ae01eb4e 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 85d480c31f..4ff34aed11 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 f0b7a60890..94e9232af5 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 f70ec5b561..87501c031a 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 1813dd49f6..1da18b0867 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 04ea7b424d..430a6943e3 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 104/573] 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 e48debfc6b..0000000000 --- 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 105/573] 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 67ee3a8038..cb91661024 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 106/573] 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 1181c8f47b..cae3e941a7 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 1b3f8ec710..45f38e4c5b 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 dd229be287..ee70a66b6e 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 1f77db67ac..7c4802077a 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 bf4cd39078..24f0034173 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 03f1dfd9cd..3548544e95 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 5ef4815563..0b38c24ba3 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 f513eef35f..116afe18a7 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 839586880c..87177b1a40 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 8787db0528..b69682ca61 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 ef171873f2..f39b0db00d 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 2f19409528..e0f18b2851 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 240273bde3..db3e1bb978 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 107/573] 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 ee70a66b6e..0462504e70 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 8fdadd666a..851dd60ed9 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 108/573] 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 8094e3b46d..dfe890fb96 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 851dd60ed9..d7b1f737a0 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 fe8a8caee0..3df54b79e6 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 109/573] 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 a7c439d1fd..03bb14b605 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 064e37a12a..df1ea5e9ad 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 110/573] 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 68db1448a1..0023dff548 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 111/573] 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 1cf8c74237..4f8cb84db7 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 096f693302..e9f00a1b65 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 112/573] 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 4f8cb84db7..9e89fc30a5 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 e9f00a1b65..5a4d0b1e86 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 113/573] 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 67ee3a8038..8b2c2d5005 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 921c060f88..88c342a87c 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 114/573] 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 3548544e95..334f31a6e5 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 fd69278354..8b49d9c302 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 115/573] 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 06c25059fa..bc4a38b744 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 116/573] 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 bc4a38b744..dfbea17cb4 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 117/573] 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 59478b326c..3e760ef1e0 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']; From d72d59bf9e59bf97e5cf77947c37900e7b56c3e6 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 28 Jul 2020 06:42:12 +0000 Subject: [PATCH 118/573] Logging and check for network added --- src/Model/Item.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Model/Item.php b/src/Model/Item.php index a4ba41a695..6567e5e260 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -2175,7 +2175,12 @@ class Item public static function storeForUserByUriId(int $uri_id, int $uid) { $item = self::selectFirst(self::ITEM_FIELDLIST, ['uri-id' => $uri_id, 'uid' => 0]); - if (empty($item) || ($item['private'] != self::PRIVATE)) { + if (!DBA::isResult($item)) { + return 0; + } + + if (($item['private'] == self::PRIVATE) || !in_array($item['network'], Protocol::FEDERATED)) { + Logger::notice('Item is private or not from a federated network. It will not be stored for the user.', ['uri-id' => $uri_id, 'uid' => $uid, 'private' => $item['private'], 'network' => $item['network']]); return 0; } From b79bb0d2cb3e6da43d7e6296f5afa29987be88e4 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 28 Jul 2020 12:58:19 +0000 Subject: [PATCH 119/573] Fetch photo fields, ensuring that they are filled --- mod/common.php | 4 +- mod/match.php | 3 +- mod/network.php | 6 +-- mod/ping.php | 7 +--- src/Model/Contact.php | 69 +++++++++++++++++++++++++++++++- src/Module/AllFriends.php | 3 +- src/Module/Contact.php | 6 +-- src/Module/Contact/Hovercard.php | 4 +- src/Module/Directory.php | 3 +- src/Module/Profile/Contacts.php | 3 +- src/Module/Search/Acl.php | 4 +- src/Util/Proxy.php | 2 +- 12 files changed, 82 insertions(+), 32 deletions(-) diff --git a/mod/common.php b/mod/common.php index 0ff523b3f7..b59b36ee7d 100644 --- a/mod/common.php +++ b/mod/common.php @@ -26,8 +26,8 @@ use Friendica\Core\Renderer; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model; +use Friendica\Model\Contact; use Friendica\Module; -use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Strings; function common_content(App $a) @@ -136,7 +136,7 @@ function common_content(App $a) 'url' => Model\Contact::magicLink($common_friend['url']), 'itemurl' => ($contact_details['addr'] ?? '') ?: $common_friend['url'], 'name' => $contact_details['name'], - 'thumb' => ProxyUtils::proxifyUrl($contact_details['thumb'], false, ProxyUtils::SIZE_THUMB), + 'thumb' => Contact::getThumb($contact_details), 'img_hover' => $contact_details['name'], 'details' => $contact_details['location'], 'tags' => $contact_details['keywords'], diff --git a/mod/match.php b/mod/match.php index 3b24c4097a..f50a454ba9 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\Util\Proxy as ProxyUtils; /** * Controller for /match. @@ -111,7 +110,7 @@ function match_content(App $a) 'tags' => $contact_details['keywords'] ?? '', 'about' => $contact_details['about'] ?? '', 'account_type' => Contact::getAccountType($contact_details), - 'thumb' => ProxyUtils::proxifyUrl($profile->photo, false, ProxyUtils::SIZE_THUMB), + 'thumb' => Contact::getThumb($contact_details, $profile->photo), 'conntxt' => DI::l10n()->t('Connect'), 'connlnk' => $connlnk, 'img_hover' => $profile->tags, diff --git a/mod/network.php b/mod/network.php index a3eb169839..d82072754c 100644 --- a/mod/network.php +++ b/mod/network.php @@ -20,7 +20,6 @@ */ use Friendica\App; -use Friendica\Content\Feature; use Friendica\Content\ForumManager; use Friendica\Content\Nav; use Friendica\Content\Pager; @@ -29,9 +28,7 @@ use Friendica\Content\Text\HTML; use Friendica\Core\ACL; use Friendica\Core\Hook; use Friendica\Core\Logger; -use Friendica\Core\Protocol; use Friendica\Core\Renderer; -use Friendica\Core\Session; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; @@ -41,7 +38,6 @@ use Friendica\Model\Post\Category; use Friendica\Model\Profile; use Friendica\Module\Security\Login; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Strings; function network_init(App $a) @@ -587,7 +583,7 @@ function networkThreadedView(App $a, $update, $parent) 'id' => 'network', 'name' => $contact['name'], 'itemurl' => ($contact['addr'] ?? '') ?: $contact['nurl'], - 'thumb' => ProxyUtils::proxifyUrl($contact['thumb'], false, ProxyUtils::SIZE_THUMB), + 'thumb' => Contact::getThumb($contact), 'details' => $contact['location'], ]; diff --git a/mod/ping.php b/mod/ping.php index 848f2f0ecf..c6e32a9314 100644 --- a/mod/ping.php +++ b/mod/ping.php @@ -34,7 +34,6 @@ use Friendica\Model\Verb; use Friendica\Protocol\Activity; use Friendica\Util\DateTimeFormat; use Friendica\Util\Temporal; -use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\XML; /** @@ -329,11 +328,7 @@ function ping_init(App $a) if (DBA::isResult($notifs)) { foreach ($notifs as $notif) { $contact = Contact::getByURL($notif['url'], false, ['micro']); - if (isset($contact['micro'])) { - $notif['photo'] = ProxyUtils::proxifyUrl($contact['micro'], false, ProxyUtils::SIZE_MICRO); - } else { - $notif['photo'] = ProxyUtils::proxifyUrl($notif['photo'], false, ProxyUtils::SIZE_MICRO); - } + $notif['photo'] = Contact::getMicro($contact, $notif['photo']); $local_time = DateTimeFormat::local($notif['date']); diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 9e89fc30a5..bb87640542 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -43,6 +43,7 @@ use Friendica\Protocol\Salmon; use Friendica\Util\DateTimeFormat; use Friendica\Util\Images; use Friendica\Util\Network; +use Friendica\Util\Proxy; use Friendica\Util\Strings; /** @@ -1787,13 +1788,79 @@ class Contact self::updateAvatar($cid, $contact['avatar'], true); } + /** + * Return the photo path for a given contact array in the given size + * + * @param array $contact contact array + * @param string $field Fieldname of the photo in the contact array + * @param string $default Default path when no picture had been found + * @param string $size Size of the avatar picture + * @param string $avatar Avatar path that is displayed when no photo had been found + * @return string photo path + */ + private static function getAvatarPath(array $contact, string $field, string $default, string $size, string $avatar) + { + if (!empty($contact)) { + $contact = self::checkAvatarCacheByArray($contact); + if (!empty($contact[$field])) { + $avatar = $contact[$field]; + } + } + + if (empty($avatar)) { + return $default; + } + + if (Proxy::isLocalImage($avatar)) { + return $avatar; + } else { + return Proxy::proxifyUrl($avatar, false, $size); + } + } + + /** + * Return the photo path for a given contact array + * + * @param array $contact Contact array + * @param string $avatar Avatar path that is displayed when no photo had been found + * @return string photo path + */ + public static function getPhoto(array $contact, string $avatar = '') + { + return self::getAvatarPath($contact, 'photo', DI::baseUrl() . '/images/person-300.jpg', Proxy::SIZE_SMALL, $avatar); + } + + /** + * Return the photo path (thumb size) for a given contact array + * + * @param array $contact Contact array + * @param string $avatar Avatar path that is displayed when no photo had been found + * @return string photo path + */ + public static function getThumb(array $contact, string $avatar = '') + { + return self::getAvatarPath($contact, 'thumb', DI::baseUrl() . '/images/person-80.jpg', Proxy::SIZE_THUMB, $avatar); + } + + /** + * Return the photo path (micro size) for a given contact array + * + * @param array $contact Contact array + * @param string $avatar Avatar path that is displayed when no photo had been found + * @return string photo path + */ + public static function getMicro(array $contact, string $avatar = '') + { + return self::getAvatarPath($contact, 'micro', DI::baseUrl() . '/images/person-48.jpg', Proxy::SIZE_MICRO, $avatar); + } + /** * Check the given contact array for avatar cache fields * * @param array $contact * @return array contact array with avatar cache fields */ - public static function checkAvatarCacheByArray(array $contact) + private static function checkAvatarCacheByArray(array $contact) { $update = false; $contact_fields = []; diff --git a/src/Module/AllFriends.php b/src/Module/AllFriends.php index 0a95256172..1c254b2094 100644 --- a/src/Module/AllFriends.php +++ b/src/Module/AllFriends.php @@ -28,7 +28,6 @@ use Friendica\Core\Renderer; use Friendica\DI; use Friendica\Model; use Friendica\Network\HTTPException; -use Friendica\Util\Proxy as ProxyUtils; /** * This module shows all public friends of the selected contact @@ -99,7 +98,7 @@ class AllFriends extends BaseModule 'url' => Model\Contact::magicLinkbyId($friend['id'], $friend['url']), 'itemurl' => ($contactDetails['addr'] ?? '') ?: $friend['url'], 'name' => $contactDetails['name'], - 'thumb' => ProxyUtils::proxifyUrl($contactDetails['thumb'], false, ProxyUtils::SIZE_THUMB), + 'thumb' => Model\Contact::getThumb($contactDetails), 'img_hover' => $contactDetails['name'], 'details' => $contactDetails['location'], 'tags' => $contactDetails['keywords'], diff --git a/src/Module/Contact.php b/src/Module/Contact.php index 5a4d0b1e86..ed6fe728a1 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -279,8 +279,6 @@ class Contact extends BaseModule if ($contact['network'] == Protocol::PHANTOM) { $contact = false; } - - $contact = ModelContact::checkAvatarCacheByArray($contact); } if (DBA::isResult($contact)) { @@ -318,7 +316,7 @@ class Contact extends BaseModule $vcard_widget = Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/vcard.tpl'), [ '$name' => $contact['name'], - '$photo' => $contact['photo'], + '$photo' => Model\Contact::getPhoto($contact), '$url' => Model\Contact::magicLinkByContact($contact, $contact['url']), '$addr' => $contact['addr'] ?? '', '$network_link' => $network_link, @@ -614,7 +612,7 @@ class Contact extends BaseModule '$notify' => ['notify', DI::l10n()->t('Notification for new posts'), ($contact['notify_new_posts'] == 1), DI::l10n()->t('Send a notification of every new post of this contact')], '$fetch_further_information' => $fetch_further_information, '$ffi_keyword_denylist' => ['ffi_keyword_denylist', DI::l10n()->t('Keyword Deny List'), $contact['ffi_keyword_denylist'], DI::l10n()->t('Comma separated list of keywords that should not be converted to hashtags, when "Fetch information and keywords" is selected')], - '$photo' => $contact['photo'], + '$photo' => Model\Contact::getPhoto($contact), '$name' => $contact['name'], '$dir_icon' => $dir_icon, '$sparkle' => $sparkle, diff --git a/src/Module/Contact/Hovercard.php b/src/Module/Contact/Hovercard.php index 750b856bc3..1d2fe85676 100644 --- a/src/Module/Contact/Hovercard.php +++ b/src/Module/Contact/Hovercard.php @@ -27,10 +27,8 @@ use Friendica\Core\Session; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Model\GContact; use Friendica\Network\HTTPException; use Friendica\Util\Strings; -use Friendica\Util\Proxy; /** * Asynchronous HTML fragment provider for frio contact hovercards @@ -88,7 +86,7 @@ class Hovercard extends BaseModule 'name' => $contact['name'], 'nick' => $contact['nick'], 'addr' => $contact['addr'] ?: $contact['url'], - 'thumb' => Proxy::proxifyUrl($contact['thumb'], false, Proxy::SIZE_THUMB), + 'thumb' => Contact::getThumb($contact), 'url' => Contact::magicLink($contact['url']), 'nurl' => $contact['nurl'], 'location' => $contact['location'], diff --git a/src/Module/Directory.php b/src/Module/Directory.php index 507da6b942..38be89b93c 100644 --- a/src/Module/Directory.php +++ b/src/Module/Directory.php @@ -32,7 +32,6 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Profile; use Friendica\Network\HTTPException; -use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Strings; /** @@ -168,7 +167,7 @@ class Directory extends BaseModule 'id' => $contact['id'], 'url' => Contact::magicLink($profile_link), 'itemurl' => $itemurl, - 'thumb' => ProxyUtils::proxifyUrl($contact[$photo_size], false, ProxyUtils::SIZE_THUMB), + 'thumb' => Contact::getThumb($contact), 'img_hover' => $contact['name'], 'name' => $contact['name'], 'details' => $details, diff --git a/src/Module/Profile/Contacts.php b/src/Module/Profile/Contacts.php index 4cd97b4097..e7931bdb02 100644 --- a/src/Module/Profile/Contacts.php +++ b/src/Module/Profile/Contacts.php @@ -32,7 +32,6 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Profile; use Friendica\Module\BaseProfile; -use Friendica\Util\Proxy as ProxyUtils; class Contacts extends BaseProfile { @@ -109,7 +108,7 @@ class Contacts extends BaseProfile 'id' => $contact['id'], 'img_hover' => DI::l10n()->t('Visit %s\'s profile [%s]', $contact_details['name'], $contact['url']), 'photo_menu' => Contact::photoMenu($contact), - 'thumb' => ProxyUtils::proxifyUrl($contact_details['thumb'], false, ProxyUtils::SIZE_THUMB), + 'thumb' => Contact::getThumb($contact_details), 'name' => substr($contact_details['name'], 0, 20), 'username' => $contact_details['name'], 'details' => $contact_details['location'], diff --git a/src/Module/Search/Acl.php b/src/Module/Search/Acl.php index 8a5c9faf58..2c0cc967c0 100644 --- a/src/Module/Search/Acl.php +++ b/src/Module/Search/Acl.php @@ -294,7 +294,7 @@ class Acl extends BaseModule foreach ($r as $g) { $entry = [ 'type' => 'c', - 'photo' => ProxyUtils::proxifyUrl($g['micro'], false, ProxyUtils::SIZE_MICRO), + 'photo' => Contact::getMicro($g), 'name' => htmlspecialchars($g['name']), 'id' => intval($g['id']), 'network' => $g['network'], @@ -355,7 +355,7 @@ class Acl extends BaseModule if (count($contact) > 0) { $unknown_contacts[] = [ 'type' => 'c', - 'photo' => ProxyUtils::proxifyUrl($contact['micro'], false, ProxyUtils::SIZE_MICRO), + 'photo' => Contact::getMicro($contact), 'name' => htmlspecialchars($contact['name']), 'id' => intval($contact['cid']), 'network' => $contact['network'], diff --git a/src/Util/Proxy.php b/src/Util/Proxy.php index e104073f03..87f7c983e1 100644 --- a/src/Util/Proxy.php +++ b/src/Util/Proxy.php @@ -170,7 +170,7 @@ class Proxy * @return boolean * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - private static function isLocalImage($url) + public static function isLocalImage($url) { if (substr($url, 0, 1) == '/') { return true; From d5a3ef6c2a2426a9504bd6d681716912f3465ebb Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 28 Jul 2020 15:04:39 +0000 Subject: [PATCH 120/573] Removed unused "use" --- src/Module/Contact.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Module/Contact.php b/src/Module/Contact.php index ed6fe728a1..d21524a793 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -36,7 +36,6 @@ 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; From ab3106a129ce085643389b1a5ac0c2a725f84276 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Tue, 28 Jul 2020 11:40:14 -0400 Subject: [PATCH 121/573] Move "View As" form at the bottom of the profile page --- src/Module/Profile/Profile.php | 20 ++++++++++++++++++- view/templates/profile/index.tpl | 34 +++++++++++++++++++++----------- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/Module/Profile/Profile.php b/src/Module/Profile/Profile.php index c187281d33..b1e0673b24 100644 --- a/src/Module/Profile/Profile.php +++ b/src/Module/Profile/Profile.php @@ -112,6 +112,7 @@ class Profile extends BaseProfile $view_as_contacts = []; $view_as_contact_id = 0; + $view_as_contact_alert = ''; if ($is_owner) { $view_as_contact_id = intval($_GET['viewas'] ?? 0); @@ -122,10 +123,20 @@ class Profile extends BaseProfile 'blocked' => false, ]); + $view_as_contact_ids = array_column($view_as_contacts, 'id'); + // User manually provided a contact ID they aren't privy to, silently defaulting to their own view - if (!in_array($view_as_contact_id, array_column($view_as_contacts, 'id'))) { + if (!in_array($view_as_contact_id, $view_as_contact_ids)) { $view_as_contact_id = 0; } + + if (($key = array_search($view_as_contact_id, $view_as_contact_ids)) !== false) { + $view_as_contact_alert = DI::l10n()->t( + 'You\'re currently viewing your profile as %s Cancel', + htmlentities($view_as_contacts[$key]['name'], ENT_COMPAT, 'UTF-8'), + 'profile/' . $parameters['nickname'] . '/profile' + ); + } } $basic_fields = []; @@ -225,7 +236,9 @@ class Profile extends BaseProfile '$title' => DI::l10n()->t('Profile'), '$view_as_contacts' => $view_as_contacts, '$view_as_contact_id' => $view_as_contact_id, + '$view_as_contact_alert' => $view_as_contact_alert, '$view_as' => DI::l10n()->t('View profile as:'), + '$submit' => DI::l10n()->t('Submit'), '$basic' => DI::l10n()->t('Basic'), '$advanced' => DI::l10n()->t('Advanced'), '$is_owner' => $a->profile_uid == local_user(), @@ -238,6 +251,11 @@ class Profile extends BaseProfile 'title' => '', 'label' => DI::l10n()->t('Edit profile') ], + '$viewas_link' => [ + 'url' => DI::args()->getQueryString() . '#viewas', + 'title' => '', + 'label' => DI::l10n()->t('View as') + ], ]); Hook::callAll('profile_advanced', $o); diff --git a/view/templates/profile/index.tpl b/view/templates/profile/index.tpl index 60bf46b992..36b64ffbb8 100644 --- a/view/templates/profile/index.tpl +++ b/view/templates/profile/index.tpl @@ -1,3 +1,8 @@ +{{if $view_as_contact_alert}} + +{{/if}}
    {{include file="section_title.tpl"}} @@ -10,20 +15,11 @@  {{$edit_link.label}} - {{if count($view_as_contacts)}}
  • -
    - - - -
    + +  {{$viewas_link.label}} +
  • - {{/if}}
    @@ -101,3 +97,17 @@ {{/foreach}}
    +{{if $is_owner}} +
    +
    + + + +
    +
    +{{/if}} From 71b6226909aaef47ae4cfa7ba3d880cb0a73e2ef Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 28 Jul 2020 19:30:55 +0000 Subject: [PATCH 122/573] Some more avatar function replacements --- include/conversation.php | 5 ++--- mod/message.php | 21 ++++++------------- mod/network.php | 2 +- mod/ping.php | 2 +- mod/suggest.php | 3 +-- src/Content/ForumManager.php | 5 ++--- src/Content/Text/HTML.php | 3 +-- src/Content/Widget.php | 6 +++--- src/Content/Widget/ContactBlock.php | 2 +- src/Factory/Notification/Notification.php | 14 ++++++------- src/Module/BaseSearch.php | 6 +++--- src/Module/Contact.php | 3 +-- src/Module/Search/Acl.php | 25 +++++++++++------------ src/Object/Post.php | 5 ++--- view/theme/vier/theme.php | 8 ++++---- 15 files changed, 47 insertions(+), 63 deletions(-) diff --git a/include/conversation.php b/include/conversation.php index 8050296a56..86af3b69d4 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -40,7 +40,6 @@ use Friendica\Object\Thread; use Friendica\Protocol\Activity; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Strings; use Friendica\Util\Temporal; use Friendica\Util\XML; @@ -593,7 +592,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o 'name' => $profile_name, 'sparkle' => $sparkle, 'lock' => $lock, - 'thumb' => DI::baseUrl()->remove(ProxyUtils::proxifyUrl($item['author-avatar'], false, ProxyUtils::SIZE_THUMB)), + 'thumb' => DI::baseUrl()->remove($item['author-avatar']), 'title' => $title, 'body' => $body, 'tags' => $tags['tags'], @@ -613,7 +612,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o 'indent' => '', 'owner_name' => $owner_name, 'owner_url' => $owner_url, - 'owner_photo' => DI::baseUrl()->remove(ProxyUtils::proxifyUrl($item['owner-avatar'], false, ProxyUtils::SIZE_THUMB)), + 'owner_photo' => DI::baseUrl()->remove($item['owner-avatar']), 'plink' => Item::getPlink($item), 'edpost' => false, 'isstarred' => $isstarred, diff --git a/mod/message.php b/mod/message.php index 204b136f9b..f04bdaecd7 100644 --- a/mod/message.php +++ b/mod/message.php @@ -32,7 +32,6 @@ use Friendica\Model\Mail; use Friendica\Model\Notify\Type; use Friendica\Module\Security\Login; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Strings; use Friendica\Util\Temporal; @@ -393,12 +392,8 @@ function message_content(App $a) $body_e = BBCode::convert($message['body']); $to_name_e = $message['name']; - $contact = Contact::getByURL($message['from-url'], false, ['thumb', 'addr']); - if (isset($contact["thumb"])) { - $from_photo = $contact["thumb"]; - } else { - $from_photo = $message['from-photo']; - } + $contact = Contact::getByURL($message['from-url'], false, ['thumb', 'addr', 'id', 'avatar']); + $from_photo = Contact::getThumb($contact, $message['from-photo']); $mails[] = [ 'id' => $message['id'], @@ -406,7 +401,7 @@ function message_content(App $a) 'from_url' => $from_url, 'from_addr' => $contact['addr'], 'sparkle' => $sparkle, - 'from_photo' => ProxyUtils::proxifyUrl($from_photo, false, ProxyUtils::SIZE_THUMB), + 'from_photo' => $from_photo, 'subject' => $subject_e, 'body' => $body_e, 'delete' => DI::l10n()->t('Delete message'), @@ -525,12 +520,8 @@ function render_messages(array $msg, $t) $body_e = $rr['body']; $to_name_e = $rr['name']; - $contact = Contact::getByURL($rr['url'], false, ['thumb', 'addr']); - if (isset($contact["thumb"])) { - $from_photo = $contact["thumb"]; - } else { - $from_photo = (($rr['thumb']) ? $rr['thumb'] : $rr['from-photo']); - } + $contact = Contact::getByURL($rr['url'], false, ['thumb', 'addr', 'id', 'avatar']); + $from_photo = Contact::getThumb($contact, $rr['thumb'] ?: $rr['from-photo']); $rslt .= Renderer::replaceMacros($tpl, [ '$id' => $rr['id'], @@ -538,7 +529,7 @@ function render_messages(array $msg, $t) '$from_url' => Contact::magicLink($rr['url']), '$from_addr' => $contact['addr'] ?? '', '$sparkle' => ' sparkle', - '$from_photo' => ProxyUtils::proxifyUrl($from_photo, false, ProxyUtils::SIZE_THUMB), + '$from_photo' => $from_photo, '$subject' => $rr['title'], '$delete' => DI::l10n()->t('Delete conversation'), '$body' => $body_e, diff --git a/mod/network.php b/mod/network.php index d82072754c..eb1dbdb043 100644 --- a/mod/network.php +++ b/mod/network.php @@ -572,7 +572,7 @@ function networkThreadedView(App $a, $update, $parent) '$title' => DI::l10n()->t('Group: %s', $group['name']) ]) . $o; } elseif ($cid) { - $fields = ['id', 'name', 'network', 'writable', 'nurl', + $fields = ['id', 'name', 'network', 'writable', 'nurl', 'avatar', 'forum', 'prv', 'contact-type', 'addr', 'thumb', 'location']; $condition = ["`id` = ? AND (NOT `blocked` OR `pending`)", $cid]; $contact = DBA::selectFirst('contact', $fields, $condition); diff --git a/mod/ping.php b/mod/ping.php index c6e32a9314..7c8d6c846d 100644 --- a/mod/ping.php +++ b/mod/ping.php @@ -327,7 +327,7 @@ function ping_init(App $a) if (DBA::isResult($notifs)) { foreach ($notifs as $notif) { - $contact = Contact::getByURL($notif['url'], false, ['micro']); + $contact = Contact::getByURL($notif['url'], false, ['micro', 'id', 'avatar']); $notif['photo'] = Contact::getMicro($contact, $notif['photo']); $local_time = DateTimeFormat::local($notif['date']); diff --git a/mod/suggest.php b/mod/suggest.php index 66d22b001e..d592537f48 100644 --- a/mod/suggest.php +++ b/mod/suggest.php @@ -27,7 +27,6 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\GContact; -use Friendica\Util\Proxy as ProxyUtils; function suggest_init(App $a) { @@ -111,7 +110,7 @@ function suggest_content(App $a) 'itemurl' => (($contact_details['addr'] != "") ? $contact_details['addr'] : $rr['url']), 'img_hover' => $rr['url'], 'name' => $contact_details['name'], - 'thumb' => ProxyUtils::proxifyUrl($contact_details['thumb'], false, ProxyUtils::SIZE_THUMB), + 'thumb' => Contact::getThumb($contact_details), 'details' => $contact_details['location'], 'tags' => $contact_details['keywords'], 'about' => $contact_details['about'], diff --git a/src/Content/ForumManager.php b/src/Content/ForumManager.php index 9441e9dbb0..980e82522c 100644 --- a/src/Content/ForumManager.php +++ b/src/Content/ForumManager.php @@ -27,7 +27,6 @@ use Friendica\Core\Renderer; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Util\Proxy as ProxyUtils; /** * This class handles methods related to the forum functionality @@ -72,7 +71,7 @@ class ForumManager $forumlist = []; - $fields = ['id', 'url', 'name', 'micro', 'thumb']; + $fields = ['id', 'url', 'name', 'micro', 'thumb', 'avatar']; $condition = [$condition_str, Protocol::DFRN, Protocol::ACTIVITYPUB, $uid]; $contacts = DBA::select('contact', $fields, $condition, $params); if (!$contacts) { @@ -131,7 +130,7 @@ class ForumManager 'name' => $contact['name'], 'cid' => $contact['id'], 'selected' => $selected, - 'micro' => DI::baseUrl()->remove(ProxyUtils::proxifyUrl($contact['micro'], false, ProxyUtils::SIZE_MICRO)), + 'micro' => DI::baseUrl()->remove(Contact::getMicro($contact)), 'id' => ++$id, ]; $entries[] = $entry; diff --git a/src/Content/Text/HTML.php b/src/Content/Text/HTML.php index b69f5abc23..7b8153b8a8 100644 --- a/src/Content/Text/HTML.php +++ b/src/Content/Text/HTML.php @@ -30,7 +30,6 @@ use Friendica\Core\Search; use Friendica\DI; use Friendica\Model\Contact; use Friendica\Util\Network; -use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Strings; use Friendica\Util\XML; use League\HTMLToMarkdown\HtmlConverter; @@ -868,7 +867,7 @@ class HTML '$click' => $contact['click'] ?? '', '$class' => $class, '$url' => $url, - '$photo' => ProxyUtils::proxifyUrl($contact['thumb'], false, ProxyUtils::SIZE_THUMB), + '$photo' => Contact::getThumb($contact), '$name' => $contact['name'], 'title' => $contact['name'] . ' [' . $contact['addr'] . ']', '$parkle' => $sparkle, diff --git a/src/Content/Widget.php b/src/Content/Widget.php index fad863fbeb..e8e70c0e11 100644 --- a/src/Content/Widget.php +++ b/src/Content/Widget.php @@ -34,7 +34,6 @@ use Friendica\Model\Group; use Friendica\Model\Item; use Friendica\Model\Profile; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Strings; use Friendica\Util\Temporal; @@ -432,10 +431,11 @@ class Widget $entries = []; foreach ($r as $rr) { + $contact = Contact::getByURL($rr['url']); $entry = [ 'url' => Contact::magicLink($rr['url']), - 'name' => $rr['name'], - 'photo' => ProxyUtils::proxifyUrl($rr['photo'], false, ProxyUtils::SIZE_THUMB), + 'name' => $contact['name'] ?? $rr['name'], + 'photo' => Contact::getThumb($contact, $rr['photo']), ]; $entries[] = $entry; } diff --git a/src/Content/Widget/ContactBlock.php b/src/Content/Widget/ContactBlock.php index 47fac09a83..85c722c8a0 100644 --- a/src/Content/Widget/ContactBlock.php +++ b/src/Content/Widget/ContactBlock.php @@ -98,7 +98,7 @@ class ContactBlock $contact_ids[] = $contact["id"]; } - $contacts_stmt = DBA::select('contact', ['id', 'uid', 'addr', 'url', 'name', 'thumb', 'network'], ['id' => $contact_ids]); + $contacts_stmt = DBA::select('contact', ['id', 'uid', 'addr', 'url', 'name', 'thumb', 'avatar', 'network'], ['id' => $contact_ids]); if (DBA::isResult($contacts_stmt)) { $contacts_title = DI::l10n()->tt('%d Contact', '%d Contacts', $total); diff --git a/src/Factory/Notification/Notification.php b/src/Factory/Notification/Notification.php index 982c2a7e0d..ba36b0cef4 100644 --- a/src/Factory/Notification/Notification.php +++ b/src/Factory/Notification/Notification.php @@ -97,7 +97,7 @@ class Notification extends BaseFactory $item['label'] = (($item['gravity'] == GRAVITY_PARENT) ? 'post' : 'comment'); $item['link'] = $this->baseUrl->get(true) . '/display/' . $item['parent-guid']; - $item['image'] = Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO); + $item['image'] = $item['author-avatar']; $item['url'] = $item['author-link']; $item['text'] = (($item['gravity'] == GRAVITY_PARENT) ? $this->l10n->t("%s created a new post", $item['author-name']) @@ -125,7 +125,7 @@ class Notification extends BaseFactory return new \Friendica\Object\Notification\Notification([ 'label' => 'like', 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'], - 'image' => Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO), + 'image' => $item['author-avatar'], 'url' => $item['author-link'], 'text' => $this->l10n->t("%s liked %s's post", $item['author-name'], $item['parent-author-name']), 'when' => $item['when'], @@ -136,7 +136,7 @@ class Notification extends BaseFactory return new \Friendica\Object\Notification\Notification([ 'label' => 'dislike', 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'], - 'image' => Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO), + 'image' => $item['author-avatar'], 'url' => $item['author-link'], 'text' => $this->l10n->t("%s disliked %s's post", $item['author-name'], $item['parent-author-name']), 'when' => $item['when'], @@ -147,7 +147,7 @@ class Notification extends BaseFactory return new \Friendica\Object\Notification\Notification([ 'label' => 'attend', 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'], - 'image' => Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO), + 'image' => $item['author-avatar'], 'url' => $item['author-link'], 'text' => $this->l10n->t("%s is attending %s's event", $item['author-name'], $item['parent-author-name']), 'when' => $item['when'], @@ -158,7 +158,7 @@ class Notification extends BaseFactory return new \Friendica\Object\Notification\Notification([ 'label' => 'attendno', 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'], - 'image' => Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO), + 'image' => $item['author-avatar'], 'url' => $item['author-link'], 'text' => $this->l10n->t("%s is not attending %s's event", $item['author-name'], $item['parent-author-name']), 'when' => $item['when'], @@ -169,7 +169,7 @@ class Notification extends BaseFactory return new \Friendica\Object\Notification\Notification([ 'label' => 'attendmaybe', 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'], - 'image' => Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO), + 'image' => $item['author-avatar'], 'url' => $item['author-link'], 'text' => $this->l10n->t("%s may attending %s's event", $item['author-name'], $item['parent-author-name']), 'when' => $item['when'], @@ -196,7 +196,7 @@ class Notification extends BaseFactory return new \Friendica\Object\Notification\Notification([ 'label' => 'friend', 'link' => $this->baseUrl->get(true) . '/display/' . $item['parent-guid'], - 'image' => Proxy::proxifyUrl($item['author-avatar'], false, Proxy::SIZE_MICRO), + 'image' => $item['author-avatar'], 'url' => $item['author-link'], 'text' => $this->l10n->t("%s is now friends with %s", $item['author-name'], $item['fname']), 'when' => $item['when'], diff --git a/src/Module/BaseSearch.php b/src/Module/BaseSearch.php index 57b5976ef0..08970f67d7 100644 --- a/src/Module/BaseSearch.php +++ b/src/Module/BaseSearch.php @@ -31,7 +31,6 @@ use Friendica\Model; use Friendica\Network\HTTPException; use Friendica\Object\Search\ContactResult; use Friendica\Object\Search\ResultList; -use Friendica\Util\Proxy as ProxyUtils; /** * Base class for search modules @@ -160,13 +159,14 @@ class BaseSearch extends BaseModule } $photo = str_replace("http:///photo/", Search::getGlobalDirectory() . "/photo/", $result->getPhoto()); + $contact = Model\Contact::getByURL($result->getUrl()); $entry = [ 'alt_text' => $alt_text, 'url' => Model\Contact::magicLink($result->getUrl()), 'itemurl' => $result->getItem(), - 'name' => $result->getName(), - 'thumb' => ProxyUtils::proxifyUrl($photo, false, ProxyUtils::SIZE_THUMB), + 'name' => $contact['name'] ?? $result->getName(), + 'thumb' => Model\Contact::getThumb($contact, $photo), 'img_hover' => $result->getTags(), 'conntxt' => $connTxt, 'connlnk' => $connLink, diff --git a/src/Module/Contact.php b/src/Module/Contact.php index d21524a793..3fa89cde91 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -40,7 +40,6 @@ use Friendica\Module\Security\Login; use Friendica\Network\HTTPException\BadRequestException; use Friendica\Network\HTTPException\NotFoundException; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Strings; /** @@ -1067,7 +1066,7 @@ class Contact extends BaseModule 'id' => $rr['id'], 'alt_text' => $alt_text, 'dir_icon' => $dir_icon, - 'thumb' => ProxyUtils::proxifyUrl($rr['thumb'], false, ProxyUtils::SIZE_THUMB), + 'thumb' => Model\Contact::getThumb($rr), 'name' => $rr['name'], 'username' => $rr['name'], 'account_type' => Model\Contact::getAccountType($rr), diff --git a/src/Module/Search/Acl.php b/src/Module/Search/Acl.php index 2c0cc967c0..1e08adbe66 100644 --- a/src/Module/Search/Acl.php +++ b/src/Module/Search/Acl.php @@ -32,7 +32,6 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Item; use Friendica\Network\HTTPException; -use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Strings; /** @@ -83,14 +82,14 @@ class Acl extends BaseModule DI::logger()->warning('Wrong result item from Search::searchGlobalContact', ['$g' => $g, '$search' => $search, '$mode' => $mode, '$page' => $page]); continue; } - + $contact = Contact::getByURL($g['url']); $contacts[] = [ - 'photo' => ProxyUtils::proxifyUrl($g['photo'], false, ProxyUtils::SIZE_MICRO), - 'name' => htmlspecialchars($g['name']), - 'nick' => $g['addr'] ?: $g['url'], - 'network' => $g['network'], + 'photo' => Contact::getMicro($contact, $g['photo']), + 'name' => htmlspecialchars($contact['name'] ?? $g['name']), + 'nick' => $contact['nick'] ?? ($g['addr'] ?: $g['url']), + 'network' => $contact['network'] ?? $g['network'], 'link' => $g['url'], - 'forum' => !empty($g['community']) ? 1 : 0, + 'forum' => !empty($g['community']), ]; } @@ -231,7 +230,7 @@ class Acl extends BaseModule $r = []; switch ($type) { case self::TYPE_MENTION_CONTACT_GROUP: - $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv`, (`prv` OR `forum`) AS `frm` FROM `contact` + $r = q("SELECT `id`, `name`, `nick`, `avatar`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv`, (`prv` OR `forum`) AS `frm` FROM `contact` WHERE `uid` = %d AND NOT `self` AND NOT `deleted` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND `notify` != '' AND NOT (`network` IN ('%s', '%s')) $sql_extra2 @@ -243,7 +242,7 @@ class Acl extends BaseModule break; case self::TYPE_MENTION_CONTACT: - $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv` FROM `contact` + $r = q("SELECT `id`, `name`, `nick`, `avatar`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv` FROM `contact` WHERE `uid` = %d AND NOT `self` AND NOT `deleted` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND `notify` != '' AND NOT (`network` IN ('%s')) $sql_extra2 @@ -254,7 +253,7 @@ class Acl extends BaseModule break; case self::TYPE_MENTION_FORUM: - $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv` FROM `contact` + $r = q("SELECT `id`, `name`, `nick`, `avatar`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv` FROM `contact` WHERE `uid` = %d AND NOT `self` AND NOT `deleted` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND `notify` != '' AND NOT (`network` IN ('%s')) AND (`forum` OR `prv`) @@ -266,7 +265,7 @@ class Acl extends BaseModule break; case self::TYPE_PRIVATE_MESSAGE: - $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr` FROM `contact` + $r = q("SELECT `id`, `name`, `nick`, `avatar`, `micro`, `network`, `url`, `attag`, `addr` FROM `contact` WHERE `uid` = %d AND NOT `self` AND NOT `deleted` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND `network` IN ('%s', '%s', '%s') $sql_extra2 @@ -280,7 +279,7 @@ class Acl extends BaseModule case self::TYPE_ANY_CONTACT: default: - $r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv` FROM `contact` + $r = q("SELECT `id`, `name`, `nick`, `avatar`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv`, `avatar` FROM `contact` WHERE `uid` = %d AND NOT `deleted` AND NOT `pending` AND NOT `archive` $sql_extra2 ORDER BY `name`", @@ -350,7 +349,7 @@ class Acl extends BaseModule continue; } - $contact = Contact::getByURL($author, false, ['micro', 'name', 'id', 'network', 'nick', 'addr', 'url', 'forum']); + $contact = Contact::getByURL($author, false, ['micro', 'name', 'id', 'network', 'nick', 'addr', 'url', 'forum', 'avatar']); if (count($contact) > 0) { $unknown_contacts[] = [ diff --git a/src/Object/Post.php b/src/Object/Post.php index ee886c1576..a9d2f8634e 100644 --- a/src/Object/Post.php +++ b/src/Object/Post.php @@ -38,7 +38,6 @@ use Friendica\Model\User; use Friendica\Protocol\Activity; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Strings; use Friendica\Util\Temporal; @@ -447,7 +446,7 @@ class Post 'profile_url' => $profile_link, 'item_photo_menu' => item_photo_menu($item), 'name' => $name_e, - 'thumb' => DI::baseUrl()->remove(ProxyUtils::proxifyUrl($item['author-avatar'], false, ProxyUtils::SIZE_THUMB)), + 'thumb' => DI::baseUrl()->remove($item['author-avatar']), 'osparkle' => $osparkle, 'sparkle' => $sparkle, 'title' => $title_e, @@ -461,7 +460,7 @@ class Post 'shiny' => $shiny, 'owner_self' => $item['author-link'] == Session::get('my_url'), 'owner_url' => $this->getOwnerUrl(), - 'owner_photo' => DI::baseUrl()->remove(ProxyUtils::proxifyUrl($item['owner-avatar'], false, ProxyUtils::SIZE_THUMB)), + 'owner_photo' => DI::baseUrl()->remove($item['owner-avatar']), 'owner_name' => $owner_name_e, 'plink' => Item::getPlink($item), 'edpost' => $edpost, diff --git a/view/theme/vier/theme.php b/view/theme/vier/theme.php index fdcc8f11d5..3f7a1a66a9 100644 --- a/view/theme/vier/theme.php +++ b/view/theme/vier/theme.php @@ -18,7 +18,6 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\GContact; -use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Strings; function vier_init(App $a) @@ -127,11 +126,12 @@ function vier_community_info() $aside['$comunity_profiles_items'] = []; foreach ($r as $rr) { + $contact = Contact::getByURL($rr['url']); $entry = Renderer::replaceMacros($tpl, [ '$id' => $rr['id'], '$profile_link' => 'follow/?url='.urlencode($rr['url']), - '$photo' => ProxyUtils::proxifyUrl($rr['photo'], false, ProxyUtils::SIZE_MICRO), - '$alt_text' => $rr['name'], + '$photo' => Contact::getMicro($contact, $rr['photo']), + '$alt_text' => $contact['name'] ?? $rr['name'], ]); $aside['$comunity_profiles_items'][] = $entry; } @@ -207,7 +207,7 @@ function vier_community_info() 'name' => $contact['name'], 'cid' => $contact['id'], 'selected' => $selected, - 'micro' => DI::baseUrl()->remove(ProxyUtils::proxifyUrl($contact['micro'], false, ProxyUtils::SIZE_MICRO)), + 'micro' => Contact::getMicro($contact), 'id' => ++$id, ]; $entries[] = $entry; From e06d9f20cfd97daad441131c0e241239c70be7de Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 29 Jul 2020 05:12:16 +0000 Subject: [PATCH 123/573] AP: Always sign requests --- src/Model/Item.php | 4 ++++ src/Model/User.php | 23 +++++++++++++++++++++++ src/Module/Friendica.php | 22 +++++++--------------- src/Protocol/ActivityPub.php | 28 ++++++++++++++-------------- 4 files changed, 48 insertions(+), 29 deletions(-) diff --git a/src/Model/Item.php b/src/Model/Item.php index 6567e5e260..b6f8ffa3d1 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -3705,8 +3705,10 @@ class Item */ public static function fetchByLink(string $uri, int $uid = 0) { + Logger::info('Trying to fetch link', ['uid' => $uid, 'uri' => $uri]); $item_id = self::searchByLink($uri, $uid); if (!empty($item_id)) { + Logger::info('Link found', ['uid' => $uid, 'uri' => $uri, 'id' => $item_id]); return $item_id; } @@ -3717,9 +3719,11 @@ class Item } if (!empty($item_id)) { + Logger::info('Link fetched', ['uid' => $uid, 'uri' => $uri, 'id' => $item_id]); return $item_id; } + Logger::info('Link not found', ['uid' => $uid, 'uri' => $uri]); return 0; } diff --git a/src/Model/User.php b/src/Model/User.php index 78ae958041..0317edf9bf 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -185,6 +185,29 @@ class User return DBA::selectFirst('user', $fields, ['email' => $email]); } + /** + * Fetch the user array of the administrator. The first one if there are several. + * + * @param array $fields + * @return array user + */ + public static function getFirstAdmin(array $fields = []) + { + $condition = []; + if (!empty(DI::config()->get('config', 'admin_nickname'))) { + $condition['nickname'] = DI::config()->get('config', 'admin_nickname'); + } + if (!empty(DI::config()->get('config', 'admin_email'))) { + $adminList = explode(',', str_replace(' ', '', DI::config()->get('config', 'admin_email'))); + $condition['email'] = $adminList[0]; + $administrator = self::getByEmail($adminList[0], $fields); + if (!empty($administrator)) { + return $administrator; + } + } + return []; + } + /** * Get owner data by user id * diff --git a/src/Module/Friendica.php b/src/Module/Friendica.php index 3325b1ae82..ea693ce275 100644 --- a/src/Module/Friendica.php +++ b/src/Module/Friendica.php @@ -130,21 +130,13 @@ class Friendica extends BaseModule $register_policy = $register_policies[$register_policy_int]; } - $condition = []; - $admin = false; - if (!empty($config->get('config', 'admin_nickname'))) { - $condition['nickname'] = $config->get('config', 'admin_nickname'); - } - if (!empty($config->get('config', 'admin_email'))) { - $adminList = explode(',', str_replace(' ', '', $config->get('config', 'admin_email'))); - $condition['email'] = $adminList[0]; - $administrator = User::getByEmail($adminList[0], ['username', 'nickname']); - if (!empty($administrator)) { - $admin = [ - 'name' => $administrator['username'], - 'profile' => DI::baseUrl()->get() . '/profile/' . $administrator['nickname'], - ]; - } + $admin = []; + $administrator = User::getFirstAdmin(['username', 'nickname']); + if (!empty($administrator)) { + $admin = [ + 'name' => $administrator['username'], + 'profile' => DI::baseUrl()->get() . '/profile/' . $administrator['nickname'], + ]; } $visible_addons = Addon::getVisibleList(); diff --git a/src/Protocol/ActivityPub.php b/src/Protocol/ActivityPub.php index 3c4f4f2e67..c04b9e592d 100644 --- a/src/Protocol/ActivityPub.php +++ b/src/Protocol/ActivityPub.php @@ -22,6 +22,7 @@ namespace Friendica\Protocol; use Friendica\Core\Protocol; +use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\APContact; use Friendica\Model\User; @@ -89,22 +90,21 @@ class ActivityPub */ public static function fetchContent(string $url, int $uid = 0) { - if (!empty($uid)) { - return HTTPSignature::fetch($url, $uid); + if (empty($uid)) { + $user = User::getFirstAdmin(['uid']); + + if (empty($user['uid'])) { + // When the system setup is missing an admin we just take the first user + $condition = ['verified' => true, 'blocked' => false, 'account_removed' => false, 'account_expired' => false]; + $user = DBA::selectFirst('user', ['uid'], $condition); + } + + if (!empty($user['uid'])) { + $uid = $user['uid']; + } } - $curlResult = DI::httpRequest()->get($url, false, ['accept_content' => 'application/activity+json, application/ld+json']); - if (!$curlResult->isSuccess() || empty($curlResult->getBody())) { - return false; - } - - $content = json_decode($curlResult->getBody(), true); - - if (empty($content) || !is_array($content)) { - return false; - } - - return $content; + return HTTPSignature::fetch($url, $uid); } private static function getAccountType($apcontact) From 2a243b747d1bd34b880e1761b397738ce0b4de21 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 29 Jul 2020 14:55:55 +0000 Subject: [PATCH 124/573] Improved functionality to fetch the admin user --- src/Model/User.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Model/User.php b/src/Model/User.php index 0317edf9bf..7b88aac189 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -193,17 +193,14 @@ class User */ public static function getFirstAdmin(array $fields = []) { - $condition = []; if (!empty(DI::config()->get('config', 'admin_nickname'))) { - $condition['nickname'] = DI::config()->get('config', 'admin_nickname'); - } - if (!empty(DI::config()->get('config', 'admin_email'))) { + $administrator = self::getByNickname(DI::config()->get('config', 'admin_nickname'), $fields); + } elseif (!empty(DI::config()->get('config', 'admin_email'))) { $adminList = explode(',', str_replace(' ', '', DI::config()->get('config', 'admin_email'))); - $condition['email'] = $adminList[0]; $administrator = self::getByEmail($adminList[0], $fields); - if (!empty($administrator)) { - return $administrator; - } + } + if (!empty($administrator)) { + return $administrator; } return []; } From a3ba0ccc9367da640cd0e438c89fd62e4cef7916 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 29 Jul 2020 14:59:55 +0000 Subject: [PATCH 125/573] Simplified code --- src/Model/User.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Model/User.php b/src/Model/User.php index 7b88aac189..46f0776b4e 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -194,15 +194,13 @@ class User public static function getFirstAdmin(array $fields = []) { if (!empty(DI::config()->get('config', 'admin_nickname'))) { - $administrator = self::getByNickname(DI::config()->get('config', 'admin_nickname'), $fields); + return self::getByNickname(DI::config()->get('config', 'admin_nickname'), $fields); } elseif (!empty(DI::config()->get('config', 'admin_email'))) { $adminList = explode(',', str_replace(' ', '', DI::config()->get('config', 'admin_email'))); - $administrator = self::getByEmail($adminList[0], $fields); + return self::getByEmail($adminList[0], $fields); + } else { + return []; } - if (!empty($administrator)) { - return $administrator; - } - return []; } /** From 4fbec33af0a31b1cc046fadf3c0b4cd9a6b0026d Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 29 Jul 2020 15:39:45 +0000 Subject: [PATCH 126/573] Asynchronous contact relation check --- src/Model/Contact.php | 8 ++- src/Model/ContactRelation.php | 116 ++++++++++++++++++++------------ src/Worker/ContactDiscovery.php | 36 ++++++++++ 3 files changed, 115 insertions(+), 45 deletions(-) create mode 100644 src/Worker/ContactDiscovery.php diff --git a/src/Model/Contact.php b/src/Model/Contact.php index bb87640542..6dbba6ed40 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -2075,7 +2075,7 @@ class Contact * @throws HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function updateFromProbe($id, $network = '', $force = false) + public static function updateFromProbe(int $id, string $network = '', bool $force = false) { /* Warning: Never ever fetch the public key via Probe::uri and write it into the contacts. @@ -2123,6 +2123,10 @@ class Contact return false; } + if (ContactRelation::isDiscoverable($ret['url'])) { + Worker::add(PRIORITY_LOW, 'ContactDiscovery', $ret['url']); + } + if (isset($ret['hide']) && is_bool($ret['hide'])) { $ret['unsearchable'] = $ret['hide']; } @@ -2147,8 +2151,6 @@ class Contact GContact::updateFromPublicContactID($id); } - ContactRelation::discoverByUrl($ret['url']); - $update = false; // make sure to not overwrite existing values with blank entries except some technical fields diff --git a/src/Model/ContactRelation.php b/src/Model/ContactRelation.php index dfbea17cb4..e8b0f81f03 100644 --- a/src/Model/ContactRelation.php +++ b/src/Model/ContactRelation.php @@ -65,49 +65,16 @@ class ContactRelation */ 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('No discovery - Last was less than a month ago.', ['id' => $contact['id'], 'url' => $url, 'discovery' => $contact['last-discovery']]); + if (!self::isDiscoverable($url, $contact)) { 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')) { - // 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; - } - - 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 { - // The contact isn't obviously speaking AP, so we don't allow updating - $apcontact = APContact::getByURL($url, false); - } + $apcontact = APContact::getByURL($url, false); if (!empty($apcontact['followers']) && is_string($apcontact['followers'])) { $followers = ActivityPub::fetchItems($apcontact['followers']); @@ -122,6 +89,8 @@ class ContactRelation } if (empty($followers) && empty($followings)) { + DBA::update('contact', ['last-discovery' => DateTimeFormat::utcNow()], ['id' => $contact['id']]); + Logger::info('The contact does not offer discoverable data', ['id' => $contact['id'], 'url' => $url, 'network' => $contact['network']]); return; } @@ -142,20 +111,24 @@ class ContactRelation } $contacts = array_unique($contacts); + $follower_counter = 0; + $following_counter = 0; + 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); + $follower_counter++; } - DBA::update('contact-relation', ['follows' => true, 'follow-updated' => DateTimeFormat::utcNow()], $fields, true); + if (in_array($contact, $followings)) { + $fields = ['cid' => $actor, 'relation-cid' => $target]; + DBA::update('contact-relation', ['follows' => true, 'follow-updated' => DateTimeFormat::utcNow()], $fields, true); + $following_counter++; + } } } @@ -165,7 +138,66 @@ class ContactRelation } DBA::update('contact', ['last-discovery' => DateTimeFormat::utcNow()], ['id' => $target]); - Logger::info('Contacts discovery finished, "last-discovery" set', ['id' => $target, 'url' => $url]); + Logger::info('Contacts discovery finished', ['id' => $target, 'url' => $url, 'follower' => $follower_counter, 'following' => $following_counter]); return; } + + /** + * Tests if a given contact url is discoverable + * + * @param string $url Contact url + * @param array $contact Contact array + * @return boolean True if contact is discoverable + */ + public static function isDiscoverable(string $url, array $contact = []) + { + $contact_discovery = DI::config()->get('system', 'contact_discovery'); + + if ($contact_discovery == self::DISCOVERY_NONE) { + return false; + } + + if (empty($contact)) { + $contact = Contact::getByURL($url); + } + + if (empty($contact)) { + return false; + } + + if ($contact['last-discovery'] > DateTimeFormat::utc('now - 1 month')) { + Logger::info('No discovery - Last was less than a month ago.', ['id' => $contact['id'], 'url' => $url, 'discovery' => $contact['last-discovery']]); + return false; + } + + 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 false; + } + + 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 false; + } + } + } elseif ($contact['created'] > DateTimeFormat::utc('now - 1 day')) { + // 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 false; + } + + if (!in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::OSTATUS])) { + $apcontact = APContact::getByURL($url, false); + if (empty($apcontact)) { + Logger::info('No discovery - The contact does not seem to speak ActivityPub.', ['id' => $contact['id'], 'url' => $url, 'network' => $contact['network']]); + return false; + } + } + + return true; + } } diff --git a/src/Worker/ContactDiscovery.php b/src/Worker/ContactDiscovery.php new file mode 100644 index 0000000000..34d4a7da0a --- /dev/null +++ b/src/Worker/ContactDiscovery.php @@ -0,0 +1,36 @@ +. + * + */ + +namespace Friendica\Worker; + +use Friendica\Model\ContactRelation; + +class ContactDiscovery +{ + /** + * Discover contact relations + * @param string $url + */ + public static function execute(string $url) + { + ContactRelation::discoverByUrl($url); + } +} From 7441bd90c83ba9822d41ab0972c5530d22195557 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 29 Jul 2020 19:48:26 +0000 Subject: [PATCH 127/573] Possibly fix a fatal error --- src/Model/Contact.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 6dbba6ed40..855f90431c 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -207,7 +207,12 @@ class Contact if (empty($cid)) { return []; } - return self::getById($cid, $fields); + + $contact = self::getById($cid, $fields); + if (empty($contact)) { + return []; + } + return $contact; } // Add internal fields @@ -238,6 +243,10 @@ class Contact $contact = DBA::selectFirst('contact', $fields, $condition, $options); } + if (!DBA::isResult($contact)) { + return []; + } + // Update the contact in the background if needed if ((($contact['updated'] < DateTimeFormat::utc('now -7 days')) || empty($contact['avatar'])) && in_array($contact['network'], Protocol::FEDERATED)) { From cc85bc41560e937e070cf37ec295a4060a6e02d4 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 30 Jul 2020 14:08:32 +0000 Subject: [PATCH 128/573] Replace "gcontact" with "contact" - imroved suggestions --- mod/suggest.php | 46 ++++------ src/Core/Search.php | 54 ++++-------- src/Model/Contact.php | 153 ++++++++++++++++++++++++++++++++++ src/Model/GContact.php | 149 --------------------------------- src/Module/Search/Acl.php | 21 ++--- static/dbstructure.config.php | 1 + view/theme/vier/theme.php | 15 ++-- 7 files changed, 201 insertions(+), 238 deletions(-) diff --git a/mod/suggest.php b/mod/suggest.php index d592537f48..244427e986 100644 --- a/mod/suggest.php +++ b/mod/suggest.php @@ -60,9 +60,8 @@ function suggest_content(App $a) DI::page()['aside'] .= Widget::follow(); - $r = GContact::suggestionQuery(local_user()); - - if (! DBA::isResult($r)) { + $contacts = Contact::getSuggestions(local_user()); + if (!DBA::isResult($contacts)) { $o .= DI::l10n()->t('No suggestions available. If this is a new site, please try again in 24 hours.'); return $o; } @@ -94,35 +93,20 @@ function suggest_content(App $a) $id = 0; $entries = []; - foreach ($r as $rr) { - $connlnk = DI::baseUrl() . '/follow/?url=' . (($rr['connect']) ? $rr['connect'] : $rr['url']); - $ignlnk = DI::baseUrl() . '/suggest?ignore=' . $rr['id']; - $photo_menu = [ - 'profile' => [DI::l10n()->t("View Profile"), Contact::magicLink($rr["url"])], - 'follow' => [DI::l10n()->t("Connect/Follow"), $connlnk], - 'hide' => [DI::l10n()->t('Ignore/Hide'), $ignlnk] - ]; - - $contact_details = Contact::getByURLForUser($rr["url"], local_user()) ?: $rr; - + foreach ($contacts as $contact) { $entry = [ - 'url' => Contact::magicLink($rr['url']), - 'itemurl' => (($contact_details['addr'] != "") ? $contact_details['addr'] : $rr['url']), - 'img_hover' => $rr['url'], - 'name' => $contact_details['name'], - 'thumb' => Contact::getThumb($contact_details), - 'details' => $contact_details['location'], - 'tags' => $contact_details['keywords'], - 'about' => $contact_details['about'], - 'account_type' => Contact::getAccountType($contact_details), - 'ignlnk' => $ignlnk, - 'ignid' => $rr['id'], - 'conntxt' => DI::l10n()->t('Connect'), - 'connlnk' => $connlnk, - 'photo_menu' => $photo_menu, - 'ignore' => DI::l10n()->t('Ignore/Hide'), - 'network' => ContactSelector::networkToName($rr['network'], $rr['url']), - 'id' => ++$id, + 'url' => Contact::magicLink($contact['url']), + 'itemurl' => $contact['addr'] ?: $contact['url'], + 'name' => $contact['name'], + 'thumb' => Contact::getThumb($contact), + 'img_hover' => $contact['url'], + 'details' => $contact['location'], + 'tags' => $contact['keywords'], + 'about' => $contact['about'], + 'account_type' => Contact::getAccountType($contact), + 'network' => ContactSelector::networkToName($contact['network'], $contact['url']), + 'photo_menu' => Contact::photoMenu($contact), + 'id' => ++$id, ]; $entries[] = $entry; } diff --git a/src/Core/Search.php b/src/Core/Search.php index 577b11266e..7f9ffeec5d 100644 --- a/src/Core/Search.php +++ b/src/Core/Search.php @@ -24,7 +24,6 @@ namespace Friendica\Core; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Model\GContact; use Friendica\Network\HTTPException; use Friendica\Object\Search\ContactResult; use Friendica\Object\Search\ResultList; @@ -170,6 +169,8 @@ class Search */ public static function getContactsFromLocalDirectory($search, $type = self::TYPE_ALL, $start = 0, $itemPage = 80) { + Logger::info('Searching', ['search' => $search, 'type' => $type, 'start' => $start, 'itempage' => $itemPage]); + $config = DI::config(); $diaspora = $config->get('system', 'diaspora_enabled') ? Protocol::DIASPORA : Protocol::DFRN; @@ -177,18 +178,20 @@ class Search $wildcard = Strings::escapeHtml('%' . $search . '%'); - $count = DBA::count('gcontact', [ - 'NOT `hide` + $condition = [ + 'NOT `unsearchable` AND `network` IN (?, ?, ?, ?) - AND NOT `failed` + AND NOT `failed` AND `uid` = ? AND (`url` LIKE ? OR `name` LIKE ? OR `location` LIKE ? OR `addr` LIKE ? OR `about` LIKE ? OR `keywords` LIKE ?) - AND `community` = ?', - Protocol::ACTIVITYPUB, Protocol::DFRN, $ostatus, $diaspora, + AND `forum` = ?', + Protocol::ACTIVITYPUB, Protocol::DFRN, $ostatus, $diaspora, 0, $wildcard, $wildcard, $wildcard, $wildcard, $wildcard, $wildcard, ($type === self::TYPE_FORUM), - ]); + ]; + + $count = DBA::count('contact', $condition); $resultList = new ResultList($start, $itemPage, $count); @@ -196,18 +199,7 @@ class Search return $resultList; } - $data = DBA::select('gcontact', ['nurl', 'name', 'addr', 'url', 'photo', 'network', 'keywords'], [ - 'NOT `hide` - AND `network` IN (?, ?, ?, ?) - AND NOT `failed` - AND (`url` LIKE ? OR `name` LIKE ? OR `location` LIKE ? - OR `addr` LIKE ? OR `about` LIKE ? OR `keywords` LIKE ?) - AND `community` = ?', - Protocol::ACTIVITYPUB, Protocol::DFRN, $ostatus, $diaspora, - $wildcard, $wildcard, $wildcard, - $wildcard, $wildcard, $wildcard, - ($type === self::TYPE_FORUM), - ], [ + $data = DBA::select('contact', [], $condition, [ 'group_by' => ['nurl', 'updated'], 'limit' => [$start, $itemPage], 'order' => ['updated' => 'DESC'] @@ -217,21 +209,7 @@ class Search return $resultList; } - while ($row = DBA::fetch($data)) { - $urlParts = parse_url($row["nurl"]); - - // Ignore results that look strange. - // For historic reasons the gcontact table does contain some garbage. - if (!empty($urlParts['query']) || !empty($urlParts['fragment'])) { - continue; - } - - $contact = Contact::getByURLForUser($row["nurl"], local_user()) ?: $row; - - if ($contact["name"] == "") { - $contact["name"] = end(explode("/", $urlParts["path"])); - } - + while ($contact = DBA::fetch($data)) { $result = new ContactResult( $contact["name"], $contact["addr"], @@ -256,7 +234,7 @@ class Search } /** - * Searching for global contacts for autocompletion + * Searching for contacts for autocompletion * * @param string $search Name or part of a name or nick * @param string $mode Search mode (e.g. "community") @@ -264,8 +242,10 @@ class Search * @return array with the search results * @throws HTTPException\InternalServerErrorException */ - public static function searchGlobalContact($search, $mode, int $page = 1) + public static function searchContact($search, $mode, int $page = 1) { + Logger::info('Searching', ['search' => $search, 'mode' => $mode, 'page' => $page]); + if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) { return []; } @@ -281,7 +261,7 @@ class Search // check if searching in the local global contact table is enabled if (DI::config()->get('system', 'poco_local_search')) { - $return = GContact::searchByName($search, $mode); + $return = Contact::searchByName($search, $mode); } else { $p = $page > 1 ? 'p=' . $page : ''; $curlResult = DI::httpRequest()->get(self::getGlobalDirectory() . '/search/people?' . $p . '&q=' . urlencode($search), false, ['accept_content' => 'application/json']); diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 855f90431c..9ebc12375a 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -2915,4 +2915,157 @@ class Contact return in_array($protocol, [Protocol::DFRN, Protocol::DIASPORA, Protocol::ACTIVITYPUB]) && !$self; } + + /** + * Search contact table by nick or name + * + * @param string $search Name or nick + * @param string $mode Search mode (e.g. "community") + * + * @return array with search results + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public static function searchByName($search, $mode = '') + { + if (empty($search)) { + return []; + } + + // check supported networks + if (DI::config()->get('system', 'diaspora_enabled')) { + $diaspora = Protocol::DIASPORA; + } else { + $diaspora = Protocol::DFRN; + } + + if (!DI::config()->get('system', 'ostatus_disabled')) { + $ostatus = Protocol::OSTATUS; + } else { + $ostatus = Protocol::DFRN; + } + + // check if we search only communities or every contact + if ($mode === 'community') { + $extra_sql = sprintf(' AND `contact-type` = %d', Contact::TYPE_COMMUNITY); + } else { + $extra_sql = ''; + } + + $search .= '%'; + + $results = DBA::p("SELECT * FROM `contact` + WHERE NOT `unsearchable` AND `network` IN (?, ?, ?, ?) AND + NOT `failed` AND `uid` = ? AND + (`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?) $extra_sql + ORDER BY `nurl` DESC LIMIT 1000", + Protocol::DFRN, Protocol::ACTIVITYPUB, $ostatus, $diaspora, 0, $search, $search, $search + ); + + $contacts = DBA::toArray($results); + return $contacts; + } + + /** + * @param int $uid user + * @param int $start optional, default 0 + * @param int $limit optional, default 80 + * @return array + */ + static public function getSuggestions(int $uid, int $start = 0, int $limit = 80) + { + $cid = self::getPublicIdByUserId($uid); + $totallimit = $start + $limit; + $contacts = []; + + Logger::info('Collecting suggestions', ['uid' => $uid, 'cid' => $cid, 'start' => $start, 'limit' => $limit]); + + $diaspora = DI::config()->get('system', 'diaspora_enabled') ? Protocol::DIASPORA : Protocol::ACTIVITYPUB; + $ostatus = !DI::config()->get('system', 'ostatus_disabled') ? Protocol::OSTATUS : Protocol::ACTIVITYPUB; + + // The query returns contacts where contacts interacted with who the given user follows. + // Contacts who already are in the user's contact table are ignored. + $results = DBA::select('contact', [], + ["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` IN + (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ?) + AND NOT `cid` IN (SELECT `id` FROM `contact` WHERE `uid` = ? AND `nurl` IN + (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?)))) + AND NOT `hidden` AND `network` IN (?, ?, ?, ?)", + $cid, 0, $uid, Contact::FRIEND, Contact::SHARING, + Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $ostatus], + ['order' => ['last-item' => true], 'limit' => $totallimit] + ); + + while ($contact = DBA::fetch($results)) { + $contacts[$contact['id']] = $contact; + } + DBA::close($results); + + Logger::info('Contacts of contacts who are followed by the given user', ['uid' => $uid, 'cid' => $cid, 'count' => count($contacts)]); + + if (count($contacts) >= $totallimit) { + return array_slice($contacts, $start, $limit); + } + + // The query returns contacts where contacts interacted with who also interacted with the given user. + // Contacts who already are in the user's contact table are ignored. + $results = DBA::select('contact', [], + ["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` IN + (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ?) + AND NOT `cid` IN (SELECT `id` FROM `contact` WHERE `uid` = ? AND `nurl` IN + (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?)))) + AND NOT `hidden` AND `network` IN (?, ?, ?, ?)", + $cid, 0, $uid, Contact::FRIEND, Contact::SHARING, + Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $ostatus], + ['order' => ['last-item' => true], 'limit' => $totallimit] + ); + + while ($contact = DBA::fetch($results)) { + $contacts[$contact['id']] = $contact; + } + DBA::close($results); + + Logger::info('Contacts of contacts who are following the given user', ['uid' => $uid, 'cid' => $cid, 'count' => count($contacts)]); + + if (count($contacts) >= $totallimit) { + return array_slice($contacts, $start, $limit); + } + + // The query returns contacts that follow the given user but aren't followed by that user. + $results = DBA::select('contact', [], + ["`nurl` IN (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` = ?) + AND NOT `hidden` AND `uid` = ? AND `network` IN (?, ?, ?, ?)", + $uid, Contact::FOLLOWER, 0, + Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $ostatus], + ['order' => ['last-item' => true], 'limit' => $totallimit] + ); + + while ($contact = DBA::fetch($results)) { + $contacts[$contact['id']] = $contact; + } + DBA::close($results); + + Logger::info('Followers that are not followed by the given user', ['uid' => $uid, 'cid' => $cid, 'count' => count($contacts)]); + + if (count($contacts) >= $totallimit) { + return array_slice($contacts, $start, $limit); + } + + // The query returns any contact that isn't followed by that user. + $results = DBA::select('contact', [], + ["NOT `nurl` IN (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?)) + AND NOT `hidden` AND `uid` = ? AND `network` IN (?, ?, ?, ?)", + $uid, Contact::FRIEND, Contact::SHARING, 0, + Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $ostatus], + ['order' => ['last-item' => true], 'limit' => $totallimit] + ); + + while ($contact = DBA::fetch($results)) { + $contacts[$contact['id']] = $contact; + } + DBA::close($results); + + Logger::info('Any contact', ['uid' => $uid, 'cid' => $cid, 'count' => count($contacts)]); + + return array_slice($contacts, $start, $limit); + } } diff --git a/src/Model/GContact.php b/src/Model/GContact.php index ec53133c94..fdf2d14077 100644 --- a/src/Model/GContact.php +++ b/src/Model/GContact.php @@ -28,14 +28,12 @@ use Friendica\Core\Logger; use Friendica\Core\Protocol; use Friendica\Core\Search; use Friendica\Core\System; -use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Network\Probe; use Friendica\Protocol\ActivityPub; use Friendica\Protocol\PortableContact; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Network; use Friendica\Util\Strings; /** @@ -43,67 +41,6 @@ use Friendica\Util\Strings; */ class GContact { - /** - * Search global contact table by nick or name - * - * @param string $search Name or nick - * @param string $mode Search mode (e.g. "community") - * - * @return array with search results - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public static function searchByName($search, $mode = '') - { - if (empty($search)) { - return []; - } - - // check supported networks - if (DI::config()->get('system', 'diaspora_enabled')) { - $diaspora = Protocol::DIASPORA; - } else { - $diaspora = Protocol::DFRN; - } - - if (!DI::config()->get('system', 'ostatus_disabled')) { - $ostatus = Protocol::OSTATUS; - } else { - $ostatus = Protocol::DFRN; - } - - // check if we search only communities or every contact - if ($mode === 'community') { - $extra_sql = ' AND `community`'; - } else { - $extra_sql = ''; - } - - $search .= '%'; - - $results = DBA::p("SELECT `nurl` FROM `gcontact` - WHERE NOT `hide` AND `network` IN (?, ?, ?, ?) AND - NOT `failed` AND - (`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?) $extra_sql - GROUP BY `nurl` ORDER BY `nurl` DESC LIMIT 1000", - Protocol::DFRN, Protocol::ACTIVITYPUB, $ostatus, $diaspora, $search, $search, $search - ); - - $gcontacts = []; - while ($result = DBA::fetch($results)) { - $urlparts = parse_url($result['nurl']); - - // Ignore results that look strange. - // For historic reasons the gcontact table does contain some garbage. - if (empty($result['nurl']) || !empty($urlparts['query']) || !empty($urlparts['fragment'])) { - continue; - } - - $gcontacts[] = Contact::getByURLForUser($result['nurl'], local_user()); - } - DBA::close($results); - return $gcontacts; - } - /** * Link the gcontact entry with user, contact and global contact * @@ -424,92 +361,6 @@ class GContact return $r; } - /** - * @param int $uid user - * @param integer $start optional, default 0 - * @param integer $limit optional, default 80 - * @return array - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public static function suggestionQuery($uid, $start = 0, $limit = 80) - { - if (!$uid) { - return []; - } - - $network = [Protocol::DFRN, Protocol::ACTIVITYPUB]; - - if (DI::config()->get('system', 'diaspora_enabled')) { - $network[] = Protocol::DIASPORA; - } - - if (!DI::config()->get('system', 'ostatus_disabled')) { - $network[] = Protocol::OSTATUS; - } - - $sql_network = "'" . implode("', '", $network) . "'"; - - /// @todo This query is really slow - // By now we cache the data for five minutes - $r = q( - "SELECT count(glink.gcid) as `total`, gcontact.* from gcontact - INNER JOIN `glink` ON `glink`.`gcid` = `gcontact`.`id` - where uid = %d and not gcontact.nurl in ( select nurl from contact where uid = %d ) - AND NOT `gcontact`.`name` IN (SELECT `name` FROM `contact` WHERE `uid` = %d) - AND NOT `gcontact`.`id` IN (SELECT `gcid` FROM `gcign` WHERE `uid` = %d) - AND `gcontact`.`updated` >= '%s' AND NOT `gcontact`.`hide` - AND NOT `gcontact`.`failed` - AND `gcontact`.`network` IN (%s) - GROUP BY `glink`.`gcid` ORDER BY `gcontact`.`updated` DESC,`total` DESC LIMIT %d, %d", - intval($uid), - intval($uid), - intval($uid), - intval($uid), - DBA::NULL_DATETIME, - $sql_network, - intval($start), - intval($limit) - ); - - if (DBA::isResult($r) && count($r) >= ($limit -1)) { - return $r; - } - - $r2 = q( - "SELECT gcontact.* FROM gcontact - INNER JOIN `glink` ON `glink`.`gcid` = `gcontact`.`id` - WHERE `glink`.`uid` = 0 AND `glink`.`cid` = 0 AND `glink`.`zcid` = 0 AND NOT `gcontact`.`nurl` IN (SELECT `nurl` FROM `contact` WHERE `uid` = %d) - AND NOT `gcontact`.`name` IN (SELECT `name` FROM `contact` WHERE `uid` = %d) - AND NOT `gcontact`.`id` IN (SELECT `gcid` FROM `gcign` WHERE `uid` = %d) - AND `gcontact`.`updated` >= '%s' - AND NOT `gcontact`.`failed` - AND `gcontact`.`network` IN (%s) - ORDER BY rand() LIMIT %d, %d", - intval($uid), - intval($uid), - intval($uid), - DBA::NULL_DATETIME, - $sql_network, - intval($start), - intval($limit) - ); - - $list = []; - foreach ($r2 as $suggestion) { - $list[$suggestion['nurl']] = $suggestion; - } - - foreach ($r as $suggestion) { - $list[$suggestion['nurl']] = $suggestion; - } - - while (sizeof($list) > ($limit)) { - array_pop($list); - } - - return $list; - } - /** * @return void * @throws \Friendica\Network\HTTPException\InternalServerErrorException diff --git a/src/Module/Search/Acl.php b/src/Module/Search/Acl.php index 1e08adbe66..7e7a0d27f5 100644 --- a/src/Module/Search/Acl.php +++ b/src/Module/Search/Acl.php @@ -74,22 +74,17 @@ class Acl extends BaseModule $mode = $_REQUEST['smode']; $page = $_REQUEST['page'] ?? 1; - $r = Search::searchGlobalContact($search, $mode, $page); + $result = Search::searchContact($search, $mode, $page); $contacts = []; - foreach ($r as $g) { - if (empty($g['name'])) { - DI::logger()->warning('Wrong result item from Search::searchGlobalContact', ['$g' => $g, '$search' => $search, '$mode' => $mode, '$page' => $page]); - continue; - } - $contact = Contact::getByURL($g['url']); + foreach ($result as $contact) { $contacts[] = [ - 'photo' => Contact::getMicro($contact, $g['photo']), - 'name' => htmlspecialchars($contact['name'] ?? $g['name']), - 'nick' => $contact['nick'] ?? ($g['addr'] ?: $g['url']), - 'network' => $contact['network'] ?? $g['network'], - 'link' => $g['url'], - 'forum' => !empty($g['community']), + 'photo' => Contact::getMicro($contact), + 'name' => htmlspecialchars($contact['name']), + 'nick' => $contact['nick'], + 'network' => $contact['network'], + 'link' => $contact['url'], + 'forum' => $contact['contact-type'] == Contact::TYPE_COMMUNITY, ]; } diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 56412b20bd..e9f70bec20 100755 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -202,6 +202,7 @@ return [ "dfrn-id" => ["dfrn-id(64)"], "issued-id" => ["issued-id(64)"], "network_uid_lastupdate" => ["network", "uid", "last-update"], + "uid_lastitem" => ["uid", "last-item"], "gsid" => ["gsid"] ] ], diff --git a/view/theme/vier/theme.php b/view/theme/vier/theme.php index 3f7a1a66a9..f4452e3c76 100644 --- a/view/theme/vier/theme.php +++ b/view/theme/vier/theme.php @@ -118,20 +118,19 @@ function vier_community_info() // comunity_profiles if ($show_profiles) { - $r = GContact::suggestionQuery(local_user(), 0, 9); + $contacts = Contact::getSuggestions(local_user(), 0, 9); $tpl = Renderer::getMarkupTemplate('ch_directory_item.tpl'); - if (DBA::isResult($r)) { + if (DBA::isResult($contacts)) { $aside['$comunity_profiles_title'] = DI::l10n()->t('Community Profiles'); $aside['$comunity_profiles_items'] = []; - foreach ($r as $rr) { - $contact = Contact::getByURL($rr['url']); + foreach ($contacts as $contact) { $entry = Renderer::replaceMacros($tpl, [ - '$id' => $rr['id'], - '$profile_link' => 'follow/?url='.urlencode($rr['url']), - '$photo' => Contact::getMicro($contact, $rr['photo']), - '$alt_text' => $contact['name'] ?? $rr['name'], + '$id' => $contact['id'], + '$profile_link' => 'follow/?url='.urlencode($contact['url']), + '$photo' => Contact::getMicro($contact), + '$alt_text' => $contact['name'], ]); $aside['$comunity_profiles_items'][] = $entry; } From c260471de1ba69ccd5a06aee641ae92ef2bed026 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 30 Jul 2020 14:14:30 +0000 Subject: [PATCH 129/573] Remove unused "use" --- mod/suggest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/mod/suggest.php b/mod/suggest.php index 244427e986..f3421de47e 100644 --- a/mod/suggest.php +++ b/mod/suggest.php @@ -26,7 +26,6 @@ use Friendica\Core\Renderer; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Model\GContact; function suggest_init(App $a) { From 113e8d910bb54cced7ed03fa6515da0f5f35ae8d Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Thu, 30 Jul 2020 22:39:01 +0200 Subject: [PATCH 130/573] Update src/Model/Contact.php Co-authored-by: Hypolite Petovan --- src/Model/Contact.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 9ebc12375a..12cfabd3e4 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -2982,7 +2982,7 @@ class Contact $diaspora = DI::config()->get('system', 'diaspora_enabled') ? Protocol::DIASPORA : Protocol::ACTIVITYPUB; $ostatus = !DI::config()->get('system', 'ostatus_disabled') ? Protocol::OSTATUS : Protocol::ACTIVITYPUB; - // The query returns contacts where contacts interacted with who the given user follows. + // The query returns contacts where contacts interacted with whom the given user follows. // Contacts who already are in the user's contact table are ignored. $results = DBA::select('contact', [], ["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` IN From 43b4841fa6904ad8d7a828ce22c2f74c6732c19d Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Thu, 30 Jul 2020 22:39:09 +0200 Subject: [PATCH 131/573] Update src/Model/Contact.php Co-authored-by: Hypolite Petovan --- src/Model/Contact.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 12cfabd3e4..3e4683073e 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -3006,7 +3006,7 @@ class Contact return array_slice($contacts, $start, $limit); } - // The query returns contacts where contacts interacted with who also interacted with the given user. + // The query returns contacts where contacts interacted with whom also interacted with the given user. // Contacts who already are in the user's contact table are ignored. $results = DBA::select('contact', [], ["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` IN From 91b0f2c486840035d021d53fc5cdc1c5932283ca Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 30 Jul 2020 21:16:15 +0000 Subject: [PATCH 132/573] Use a single function to create the template data for contacts --- mod/common.php | 28 +++----------------- mod/match.php | 31 +++------------------- mod/suggest.php | 16 +---------- src/Model/Contact.php | 19 +++++++++++++ src/Module/AllFriends.php | 36 +++---------------------- src/Module/BaseSearch.php | 56 +++------------------------------------ 6 files changed, 34 insertions(+), 152 deletions(-) diff --git a/mod/common.php b/mod/common.php index b59b36ee7d..29b3a9432d 100644 --- a/mod/common.php +++ b/mod/common.php @@ -123,30 +123,10 @@ function common_content(App $a) $entries = []; foreach ($common_friends as $common_friend) { - //get further details of the contact - $contact_details = Model\Contact::getByURLForUser($common_friend['url'], $uid); - - // $rr['id'] is needed to use contact_photo_menu() - /// @TODO Adding '/" here avoids E_NOTICE on missing constants - $common_friend['id'] = $common_friend['cid']; - - $photo_menu = Model\Contact::photoMenu($common_friend); - - $entry = [ - 'url' => Model\Contact::magicLink($common_friend['url']), - 'itemurl' => ($contact_details['addr'] ?? '') ?: $common_friend['url'], - 'name' => $contact_details['name'], - 'thumb' => Contact::getThumb($contact_details), - 'img_hover' => $contact_details['name'], - 'details' => $contact_details['location'], - 'tags' => $contact_details['keywords'], - 'about' => $contact_details['about'], - 'account_type' => Model\Contact::getAccountType($contact_details), - 'network' => ContactSelector::networkToName($contact_details['network'], $contact_details['url']), - 'photo_menu' => $photo_menu, - 'id' => ++$id, - ]; - $entries[] = $entry; + $contact = Model\Contact::getByURL($common_friend['url']); + if (!empty($contact)) { + $entries[] = Model\Contact::getTemplateData($contact, ++$id); + } } $title = ''; diff --git a/mod/match.php b/mod/match.php index f50a454ba9..75728b7043 100644 --- a/mod/match.php +++ b/mod/match.php @@ -91,33 +91,10 @@ function match_content(App $a) continue; } - // Workaround for wrong directory photo URL - $profile->photo = str_replace('http:///photo/', Search::getGlobalDirectory() . '/photo/', $profile->photo); - - $connlnk = DI::baseUrl() . '/follow/?url=' . $profile->url; - $photo_menu = [ - 'profile' => [DI::l10n()->t("View Profile"), Contact::magicLink($profile->url)], - 'follow' => [DI::l10n()->t("Connect/Follow"), $connlnk] - ]; - - $contact_details = Contact::getByURL($profile->url, false); - - $entry = [ - 'url' => Contact::magicLink($profile->url), - 'itemurl' => $contact_details['addr'] ?? $profile->url, - 'name' => $profile->name, - 'details' => $contact_details['location'] ?? '', - 'tags' => $contact_details['keywords'] ?? '', - 'about' => $contact_details['about'] ?? '', - 'account_type' => Contact::getAccountType($contact_details), - 'thumb' => Contact::getThumb($contact_details, $profile->photo), - 'conntxt' => DI::l10n()->t('Connect'), - 'connlnk' => $connlnk, - 'img_hover' => $profile->tags, - 'photo_menu' => $photo_menu, - 'id' => $i, - ]; - $entries[] = $entry; + $contact = Contact::getByURL($profile->url); + if (!empty($contact)) { + $entries[] = Contact::getTemplateData($contact, $i); + } } $data = [ diff --git a/mod/suggest.php b/mod/suggest.php index f3421de47e..c818bd010e 100644 --- a/mod/suggest.php +++ b/mod/suggest.php @@ -93,21 +93,7 @@ function suggest_content(App $a) $entries = []; foreach ($contacts as $contact) { - $entry = [ - 'url' => Contact::magicLink($contact['url']), - 'itemurl' => $contact['addr'] ?: $contact['url'], - 'name' => $contact['name'], - 'thumb' => Contact::getThumb($contact), - 'img_hover' => $contact['url'], - 'details' => $contact['location'], - 'tags' => $contact['keywords'], - 'about' => $contact['about'], - 'account_type' => Contact::getAccountType($contact), - 'network' => ContactSelector::networkToName($contact['network'], $contact['url']), - 'photo_menu' => Contact::photoMenu($contact), - 'id' => ++$id, - ]; - $entries[] = $entry; + $entries[] = Contact::getTemplateData($contact, ++$id); } $tpl = Renderer::getMarkupTemplate('viewcontact_template.tpl'); diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 9ebc12375a..cceab3a02f 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -22,6 +22,7 @@ namespace Friendica\Model; use Friendica\App\BaseURL; +use Friendica\Content\ContactSelector; use Friendica\Content\Pager; use Friendica\Core\Hook; use Friendica\Core\Logger; @@ -3068,4 +3069,22 @@ class Contact return array_slice($contacts, $start, $limit); } + + public static function getTemplateData(array $contact, int $id) + { + return [ + 'url' => self::magicLink($contact['url']), + 'itemurl' => $contact['addr'] ?: $contact['url'], + 'name' => $contact['name'], + 'thumb' => self::getThumb($contact), + 'img_hover' => $contact['url'], + 'details' => $contact['location'], + 'tags' => $contact['keywords'], + 'about' => $contact['about'], + 'account_type' => self::getAccountType($contact), + 'network' => ContactSelector::networkToName($contact['network'], $contact['url']), + 'photo_menu' => self::photoMenu($contact), + 'id' => $id, + ]; + } } diff --git a/src/Module/AllFriends.php b/src/Module/AllFriends.php index 1c254b2094..10d3313465 100644 --- a/src/Module/AllFriends.php +++ b/src/Module/AllFriends.php @@ -77,40 +77,10 @@ class AllFriends extends BaseModule $entries = []; foreach ($friends as $friend) { - //get further details of the contact - $contactDetails = Model\Contact::getByURLForUser($friend['url'], $uid) ?: $friend; - - $connlnk = ''; - // $friend[cid] is only available for common contacts. So if the contact is a common one, use contact_photo_menu to generate the photoMenu - // If the contact is not common to the user, Connect/Follow' will be added to the photo menu - if ($friend['cid']) { - $friend['id'] = $friend['cid']; - $photoMenu = Model\Contact::photoMenu($friend); - } else { - $connlnk = DI::baseUrl()->get() . '/follow/?url=' . $friend['url']; - $photoMenu = [ - 'profile' => [DI::l10n()->t('View Profile'), Model\Contact::magicLinkbyId($friend['id'], $friend['url'])], - 'follow' => [DI::l10n()->t('Connect/Follow'), $connlnk] - ]; + $contact = Model\Contact::getByURL($friend['url']); + if (!empty($contact)) { + $entries[] = Model\Contact::getTemplateData($contact, ++$id); } - - $entry = [ - 'url' => Model\Contact::magicLinkbyId($friend['id'], $friend['url']), - 'itemurl' => ($contactDetails['addr'] ?? '') ?: $friend['url'], - 'name' => $contactDetails['name'], - 'thumb' => Model\Contact::getThumb($contactDetails), - 'img_hover' => $contactDetails['name'], - 'details' => $contactDetails['location'], - 'tags' => $contactDetails['keywords'], - 'about' => $contactDetails['about'], - 'account_type' => Model\Contact::getAccountType($contactDetails), - 'network' => ContactSelector::networkToName($contactDetails['network'], $contactDetails['url']), - 'photoMenu' => $photoMenu, - 'conntxt' => DI::l10n()->t('Connect'), - 'connlnk' => $connlnk, - 'id' => ++$id, - ]; - $entries[] = $entry; } $tab_str = Contact::getTabsHTML($app, $contact, 4); diff --git a/src/Module/BaseSearch.php b/src/Module/BaseSearch.php index 08970f67d7..594f562055 100644 --- a/src/Module/BaseSearch.php +++ b/src/Module/BaseSearch.php @@ -125,60 +125,10 @@ class BaseSearch extends BaseModule // in case the result is a contact result, add a contact-specific entry if ($result instanceof ContactResult) { - - $alt_text = ''; - $location = ''; - $about = ''; - $accountType = ''; - $photo_menu = []; - - // If We already know this contact then don't show the "connect" button - if ($result->getCid() > 0 || $result->getPCid() > 0) { - $connLink = ""; - $connTxt = ""; - $contact = Model\Contact::getById( - ($result->getCid() > 0) ? $result->getCid() : $result->getPCid() - ); - - if (!empty($contact)) { - $photo_menu = Model\Contact::photoMenu($contact); - $details = Contact::getContactTemplateVars($contact); - $alt_text = $details['alt_text']; - $location = $contact['location']; - $about = $contact['about']; - $accountType = Model\Contact::getAccountType($contact); - } else { - $photo_menu = []; - } - } else { - $connLink = DI::baseUrl()->get() . '/follow/?url=' . $result->getUrl(); - $connTxt = DI::l10n()->t('Connect'); - - $photo_menu['profile'] = [DI::l10n()->t("View Profile"), Model\Contact::magicLink($result->getUrl())]; - $photo_menu['follow'] = [DI::l10n()->t("Connect/Follow"), $connLink]; - } - - $photo = str_replace("http:///photo/", Search::getGlobalDirectory() . "/photo/", $result->getPhoto()); $contact = Model\Contact::getByURL($result->getUrl()); - - $entry = [ - 'alt_text' => $alt_text, - 'url' => Model\Contact::magicLink($result->getUrl()), - 'itemurl' => $result->getItem(), - 'name' => $contact['name'] ?? $result->getName(), - 'thumb' => Model\Contact::getThumb($contact, $photo), - 'img_hover' => $result->getTags(), - 'conntxt' => $connTxt, - 'connlnk' => $connLink, - 'photo_menu' => $photo_menu, - 'details' => $location, - 'tags' => $result->getTags(), - 'about' => $about, - 'account_type' => $accountType, - 'network' => ContactSelector::networkToName($result->getNetwork(), $result->getUrl()), - 'id' => ++$id, - ]; - $entries[] = $entry; + if (!empty($contact)) { + $entries[] = Model\Contact::getTemplateData($contact, ++$id); + } } } From dcf6926a08f9b5791dbf3a3d0ba5bdd40631bed0 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 30 Jul 2020 21:23:54 +0000 Subject: [PATCH 133/573] Some indentation --- src/Model/Contact.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 3e4683073e..61b4c9d05a 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -2987,8 +2987,8 @@ class Contact $results = DBA::select('contact', [], ["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ?) - AND NOT `cid` IN (SELECT `id` FROM `contact` WHERE `uid` = ? AND `nurl` IN - (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?)))) + AND NOT `cid` IN (SELECT `id` FROM `contact` WHERE `uid` = ? AND `nurl` IN + (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?)))) AND NOT `hidden` AND `network` IN (?, ?, ?, ?)", $cid, 0, $uid, Contact::FRIEND, Contact::SHARING, Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $ostatus], @@ -3011,8 +3011,8 @@ class Contact $results = DBA::select('contact', [], ["`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ?) - AND NOT `cid` IN (SELECT `id` FROM `contact` WHERE `uid` = ? AND `nurl` IN - (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?)))) + AND NOT `cid` IN (SELECT `id` FROM `contact` WHERE `uid` = ? AND `nurl` IN + (SELECT `nurl` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?)))) AND NOT `hidden` AND `network` IN (?, ?, ?, ?)", $cid, 0, $uid, Contact::FRIEND, Contact::SHARING, Protocol::ACTIVITYPUB, Protocol::DFRN, $diaspora, $ostatus], From 893f6bd692efa94202dd472ea160eef68a420cf9 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 31 Jul 2020 03:55:01 +0000 Subject: [PATCH 134/573] Use the function from the contact template instead --- mod/common.php | 6 ++---- mod/match.php | 5 +++-- mod/suggest.php | 4 ++-- src/Model/Contact.php | 18 ------------------ src/Module/AllFriends.php | 5 ++--- src/Module/BaseSearch.php | 5 ++--- src/Module/Contact.php | 13 ++++++++++++- 7 files changed, 23 insertions(+), 33 deletions(-) diff --git a/mod/common.php b/mod/common.php index 29b3a9432d..edcb81983f 100644 --- a/mod/common.php +++ b/mod/common.php @@ -20,13 +20,11 @@ */ use Friendica\App; -use Friendica\Content\ContactSelector; use Friendica\Content\Pager; use Friendica\Core\Renderer; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model; -use Friendica\Model\Contact; use Friendica\Module; use Friendica\Util\Strings; @@ -123,9 +121,9 @@ function common_content(App $a) $entries = []; foreach ($common_friends as $common_friend) { - $contact = Model\Contact::getByURL($common_friend['url']); + $contact = Model\Contact::getByURLForUser($common_friend['url'], local_user()); if (!empty($contact)) { - $entries[] = Model\Contact::getTemplateData($contact, ++$id); + $entries[] = Module\Contact::getContactTemplateVars($contact, ++$id); } } diff --git a/mod/match.php b/mod/match.php index 75728b7043..6a2e350bcc 100644 --- a/mod/match.php +++ b/mod/match.php @@ -27,6 +27,7 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Profile; +use Friendica\Module\Contact as ModuleContact; /** * Controller for /match. @@ -91,9 +92,9 @@ function match_content(App $a) continue; } - $contact = Contact::getByURL($profile->url); + $contact = Contact::getByURLForUser($profile->url, local_user()); if (!empty($contact)) { - $entries[] = Contact::getTemplateData($contact, $i); + $entries[] = ModuleContact::getContactTemplateVars($contact, $i); } } diff --git a/mod/suggest.php b/mod/suggest.php index c818bd010e..03d0e77fc7 100644 --- a/mod/suggest.php +++ b/mod/suggest.php @@ -20,12 +20,12 @@ */ use Friendica\App; -use Friendica\Content\ContactSelector; use Friendica\Content\Widget; use Friendica\Core\Renderer; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; +use Friendica\Module\Contact as ModuleContact; function suggest_init(App $a) { @@ -93,7 +93,7 @@ function suggest_content(App $a) $entries = []; foreach ($contacts as $contact) { - $entries[] = Contact::getTemplateData($contact, ++$id); + $entries[] = ModuleContact::getContactTemplateVars($contact, ++$id); } $tpl = Renderer::getMarkupTemplate('viewcontact_template.tpl'); diff --git a/src/Model/Contact.php b/src/Model/Contact.php index cceab3a02f..7443d32a2e 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -3069,22 +3069,4 @@ class Contact return array_slice($contacts, $start, $limit); } - - public static function getTemplateData(array $contact, int $id) - { - return [ - 'url' => self::magicLink($contact['url']), - 'itemurl' => $contact['addr'] ?: $contact['url'], - 'name' => $contact['name'], - 'thumb' => self::getThumb($contact), - 'img_hover' => $contact['url'], - 'details' => $contact['location'], - 'tags' => $contact['keywords'], - 'about' => $contact['about'], - 'account_type' => self::getAccountType($contact), - 'network' => ContactSelector::networkToName($contact['network'], $contact['url']), - 'photo_menu' => self::photoMenu($contact), - 'id' => $id, - ]; - } } diff --git a/src/Module/AllFriends.php b/src/Module/AllFriends.php index 10d3313465..30418f5c2a 100644 --- a/src/Module/AllFriends.php +++ b/src/Module/AllFriends.php @@ -22,7 +22,6 @@ namespace Friendica\Module; use Friendica\BaseModule; -use Friendica\Content\ContactSelector; use Friendica\Content\Pager; use Friendica\Core\Renderer; use Friendica\DI; @@ -77,9 +76,9 @@ class AllFriends extends BaseModule $entries = []; foreach ($friends as $friend) { - $contact = Model\Contact::getByURL($friend['url']); + $contact = Model\Contact::getByURLForUser($friend['url'], local_user()); if (!empty($contact)) { - $entries[] = Model\Contact::getTemplateData($contact, ++$id); + $entries[] = Contact::getContactTemplateVars($contact, ++$id); } } diff --git a/src/Module/BaseSearch.php b/src/Module/BaseSearch.php index 594f562055..5863a94050 100644 --- a/src/Module/BaseSearch.php +++ b/src/Module/BaseSearch.php @@ -22,7 +22,6 @@ namespace Friendica\Module; use Friendica\BaseModule; -use Friendica\Content\ContactSelector; use Friendica\Content\Pager; use Friendica\Core\Renderer; use Friendica\Core\Search; @@ -125,9 +124,9 @@ class BaseSearch extends BaseModule // in case the result is a contact result, add a contact-specific entry if ($result instanceof ContactResult) { - $contact = Model\Contact::getByURL($result->getUrl()); + $contact = Model\Contact::getByURLForUser($result->getUrl(), local_user()); if (!empty($contact)) { - $entries[] = Model\Contact::getTemplateData($contact, ++$id); + $entries[] = Contact::getContactTemplateVars($contact, ++$id); } } } diff --git a/src/Module/Contact.php b/src/Module/Contact.php index 3fa89cde91..2d06aa3240 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -1009,7 +1009,14 @@ class Contact extends BaseModule return $o; } - public static function getContactTemplateVars(array $rr) + /** + * Return the fields for the contact template + * + * @param array $rr Contact array + * @param integer $id + * @return array Template fields + */ + public static function getContactTemplateVars(array $rr, int $id = 0) { $dir_icon = ''; $alt_text = ''; @@ -1069,12 +1076,16 @@ class Contact extends BaseModule 'thumb' => Model\Contact::getThumb($rr), 'name' => $rr['name'], 'username' => $rr['name'], + 'details' => $rr['location'], + 'tags' => $rr['keywords'], + 'about' => $rr['about'], 'account_type' => Model\Contact::getAccountType($rr), 'sparkle' => $sparkle, 'itemurl' => ($rr['addr'] ?? '') ?: $rr['url'], 'url' => $url, 'network' => ContactSelector::networkToName($rr['network'], $rr['url'], $rr['protocol']), 'nick' => $rr['nick'], + 'id' => $id, ]; } From 48424334490b23b47fedbd58460f93f29a078be7 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 31 Jul 2020 03:58:19 +0000 Subject: [PATCH 135/573] Unused "use" removed --- src/Model/Contact.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 7443d32a2e..9ebc12375a 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -22,7 +22,6 @@ namespace Friendica\Model; use Friendica\App\BaseURL; -use Friendica\Content\ContactSelector; use Friendica\Content\Pager; use Friendica\Core\Hook; use Friendica\Core\Logger; From a4b5536d17559263f3395d8affc4bd0363abfbd7 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 31 Jul 2020 04:19:20 +0000 Subject: [PATCH 136/573] The local directory is now using the template function as well --- src/Module/Directory.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Module/Directory.php b/src/Module/Directory.php index 38be89b93c..93d14cc176 100644 --- a/src/Module/Directory.php +++ b/src/Module/Directory.php @@ -29,7 +29,7 @@ use Friendica\Core\Hook; use Friendica\Core\Session; use Friendica\Core\Renderer; use Friendica\DI; -use Friendica\Model\Contact; +use Friendica\Model; use Friendica\Model\Profile; use Friendica\Network\HTTPException; use Friendica\Util\Strings; @@ -83,7 +83,10 @@ class Directory extends BaseModule } foreach ($profiles['entries'] as $entry) { - $entries[] = self::formatEntry($entry, $photo); + $contact = Model\Contact::getByURLForUser($entry['url'], local_user()); + if (!empty($contact)) { + $entries[] = Contact::getContactTemplateVars($contact); + } } } @@ -160,18 +163,18 @@ class Directory extends BaseModule $location_e = $location; $photo_menu = [ - 'profile' => [DI::l10n()->t("View Profile"), Contact::magicLink($profile_link)] + 'profile' => [DI::l10n()->t("View Profile"), Model\Contact::magicLink($profile_link)] ]; $entry = [ 'id' => $contact['id'], - 'url' => Contact::magicLink($profile_link), + 'url' => Model\Contact::magicLink($profile_link), 'itemurl' => $itemurl, - 'thumb' => Contact::getThumb($contact), + 'thumb' => Model\Contact::getThumb($contact), 'img_hover' => $contact['name'], 'name' => $contact['name'], 'details' => $details, - 'account_type' => Contact::getAccountType($contact), + 'account_type' => Model\Contact::getAccountType($contact), 'profile' => $profile, 'location' => $location_e, 'tags' => $contact['pub_keywords'], From ce7f192f35cb0493c96283fac701d51bd30dc777 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 31 Jul 2020 04:28:26 +0000 Subject: [PATCH 137/573] "id" is not needed as a parameter --- mod/common.php | 4 +--- mod/match.php | 2 +- mod/suggest.php | 4 +--- src/Module/AllFriends.php | 4 +--- src/Module/BaseSearch.php | 3 +-- src/Module/Contact.php | 3 +-- 6 files changed, 6 insertions(+), 14 deletions(-) diff --git a/mod/common.php b/mod/common.php index edcb81983f..7d9caf8d1e 100644 --- a/mod/common.php +++ b/mod/common.php @@ -117,13 +117,11 @@ function common_content(App $a) return $o; } - $id = 0; - $entries = []; foreach ($common_friends as $common_friend) { $contact = Model\Contact::getByURLForUser($common_friend['url'], local_user()); if (!empty($contact)) { - $entries[] = Module\Contact::getContactTemplateVars($contact, ++$id); + $entries[] = Module\Contact::getContactTemplateVars($contact); } } diff --git a/mod/match.php b/mod/match.php index 6a2e350bcc..8e0baacd1e 100644 --- a/mod/match.php +++ b/mod/match.php @@ -94,7 +94,7 @@ function match_content(App $a) $contact = Contact::getByURLForUser($profile->url, local_user()); if (!empty($contact)) { - $entries[] = ModuleContact::getContactTemplateVars($contact, $i); + $entries[] = ModuleContact::getContactTemplateVars($contact); } } diff --git a/mod/suggest.php b/mod/suggest.php index 03d0e77fc7..d501e3a3f2 100644 --- a/mod/suggest.php +++ b/mod/suggest.php @@ -89,11 +89,9 @@ function suggest_content(App $a) ]); } - $id = 0; $entries = []; - foreach ($contacts as $contact) { - $entries[] = ModuleContact::getContactTemplateVars($contact, ++$id); + $entries[] = ModuleContact::getContactTemplateVars($contact); } $tpl = Renderer::getMarkupTemplate('viewcontact_template.tpl'); diff --git a/src/Module/AllFriends.php b/src/Module/AllFriends.php index 30418f5c2a..68a4f304e6 100644 --- a/src/Module/AllFriends.php +++ b/src/Module/AllFriends.php @@ -72,13 +72,11 @@ class AllFriends extends BaseModule return DI::l10n()->t('No friends to display.'); } - $id = 0; - $entries = []; foreach ($friends as $friend) { $contact = Model\Contact::getByURLForUser($friend['url'], local_user()); if (!empty($contact)) { - $entries[] = Contact::getContactTemplateVars($contact, ++$id); + $entries[] = Contact::getContactTemplateVars($contact); } } diff --git a/src/Module/BaseSearch.php b/src/Module/BaseSearch.php index 5863a94050..77abae007a 100644 --- a/src/Module/BaseSearch.php +++ b/src/Module/BaseSearch.php @@ -118,7 +118,6 @@ class BaseSearch extends BaseModule return ''; } - $id = 0; $entries = []; foreach ($results->getResults() as $result) { @@ -126,7 +125,7 @@ class BaseSearch extends BaseModule if ($result instanceof ContactResult) { $contact = Model\Contact::getByURLForUser($result->getUrl(), local_user()); if (!empty($contact)) { - $entries[] = Contact::getContactTemplateVars($contact, ++$id); + $entries[] = Contact::getContactTemplateVars($contact); } } } diff --git a/src/Module/Contact.php b/src/Module/Contact.php index 2d06aa3240..310bf8029b 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -1016,7 +1016,7 @@ class Contact extends BaseModule * @param integer $id * @return array Template fields */ - public static function getContactTemplateVars(array $rr, int $id = 0) + public static function getContactTemplateVars(array $rr) { $dir_icon = ''; $alt_text = ''; @@ -1085,7 +1085,6 @@ class Contact extends BaseModule 'url' => $url, 'network' => ContactSelector::networkToName($rr['network'], $rr['url'], $rr['protocol']), 'nick' => $rr['nick'], - 'id' => $id, ]; } From 45aff10ff866787f2ddf9fa98b435e82410f3278 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 31 Jul 2020 04:50:24 +0000 Subject: [PATCH 138/573] Replaced with general function --- src/Module/Profile/Contacts.php | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/src/Module/Profile/Contacts.php b/src/Module/Profile/Contacts.php index e7931bdb02..e55c0d9a84 100644 --- a/src/Module/Profile/Contacts.php +++ b/src/Module/Profile/Contacts.php @@ -21,7 +21,6 @@ namespace Friendica\Module\Profile; -use Friendica\Content\ContactSelector; use Friendica\Content\Nav; use Friendica\Content\Pager; use Friendica\Core\Protocol; @@ -29,9 +28,9 @@ use Friendica\Core\Renderer; use Friendica\Core\Session; use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Model\Contact; use Friendica\Model\Profile; use Friendica\Module\BaseProfile; +use Friendica\Module\Contact as ModuleContact; class Contacts extends BaseProfile { @@ -101,25 +100,7 @@ class Contacts extends BaseProfile if ($contact['self']) { continue; } - - $contact_details = Contact::getByURLForUser($contact['url'], $a->profile['uid']) ?: $contact; - - $contacts[] = [ - 'id' => $contact['id'], - 'img_hover' => DI::l10n()->t('Visit %s\'s profile [%s]', $contact_details['name'], $contact['url']), - 'photo_menu' => Contact::photoMenu($contact), - 'thumb' => Contact::getThumb($contact_details), - 'name' => substr($contact_details['name'], 0, 20), - 'username' => $contact_details['name'], - 'details' => $contact_details['location'], - 'tags' => $contact_details['keywords'], - 'about' => $contact_details['about'], - 'account_type' => Contact::getAccountType($contact_details), - 'url' => Contact::magicLink($contact['url']), - 'sparkle' => '', - 'itemurl' => $contact_details['addr'] ? : $contact['url'], - 'network' => ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol']), - ]; + $contacts[] = ModuleContact::getContactTemplateVars($contact); } DBA::close($contacts_stmt); From 71415094cb92de900926639bec1b3a8e2982c57e Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 31 Jul 2020 06:00:43 +0000 Subject: [PATCH 139/573] Removed unused template variables --- src/Module/Contact.php | 62 ++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 38 deletions(-) diff --git a/src/Module/Contact.php b/src/Module/Contact.php index 310bf8029b..40148c50d1 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -483,21 +483,17 @@ class Contact extends BaseModule $contact['blocked'] = Model\Contact::isBlockedByUser($contact['id'], local_user()); $contact['readonly'] = Model\Contact::isIgnoredByUser($contact['id'], local_user()); - $dir_icon = ''; $relation_text = ''; switch ($contact['rel']) { case Model\Contact::FRIEND: - $dir_icon = 'images/lrarrow.gif'; $relation_text = DI::l10n()->t('You are mutual friends with %s'); break; case Model\Contact::FOLLOWER; - $dir_icon = 'images/larrow.gif'; $relation_text = DI::l10n()->t('You are sharing with %s'); break; case Model\Contact::SHARING; - $dir_icon = 'images/rarrow.gif'; $relation_text = DI::l10n()->t('%s is sharing with you'); break; @@ -612,7 +608,6 @@ class Contact extends BaseModule '$ffi_keyword_denylist' => ['ffi_keyword_denylist', DI::l10n()->t('Keyword Deny List'), $contact['ffi_keyword_denylist'], DI::l10n()->t('Comma separated list of keywords that should not be converted to hashtags, when "Fetch information and keywords" is selected')], '$photo' => Model\Contact::getPhoto($contact), '$name' => $contact['name'], - '$dir_icon' => $dir_icon, '$sparkle' => $sparkle, '$url' => $url, '$profileurllabel'=> DI::l10n()->t('Profile URL'), @@ -1012,29 +1007,24 @@ class Contact extends BaseModule /** * Return the fields for the contact template * - * @param array $rr Contact array - * @param integer $id + * @param array $contact Contact array * @return array Template fields */ - public static function getContactTemplateVars(array $rr) + public static function getContactTemplateVars(array $contact) { - $dir_icon = ''; $alt_text = ''; - if (!empty($rr['uid']) && !empty($rr['rel'])) { - switch ($rr['rel']) { + if (!empty($contact['uid']) && !empty($contact['rel'])) { + switch ($contact['rel']) { case Model\Contact::FRIEND: - $dir_icon = 'images/lrarrow.gif'; $alt_text = DI::l10n()->t('Mutual Friendship'); break; case Model\Contact::FOLLOWER; - $dir_icon = 'images/larrow.gif'; $alt_text = DI::l10n()->t('is a fan of yours'); break; case Model\Contact::SHARING; - $dir_icon = 'images/rarrow.gif'; $alt_text = DI::l10n()->t('you are a fan of'); break; @@ -1043,7 +1033,7 @@ class Contact extends BaseModule } } - $url = Model\Contact::magicLink($rr['url']); + $url = Model\Contact::magicLink($contact['url']); if (strpos($url, 'redir/') === 0) { $sparkle = ' class="sparkle" '; @@ -1051,40 +1041,36 @@ class Contact extends BaseModule $sparkle = ''; } - if ($rr['pending']) { - if (in_array($rr['rel'], [Model\Contact::FRIEND, Model\Contact::SHARING])) { + if ($contact['pending']) { + if (in_array($contact['rel'], [Model\Contact::FRIEND, Model\Contact::SHARING])) { $alt_text = DI::l10n()->t('Pending outgoing contact request'); } else { $alt_text = DI::l10n()->t('Pending incoming contact request'); } } - if ($rr['self']) { - $dir_icon = 'images/larrow.gif'; + if ($contact['self']) { $alt_text = DI::l10n()->t('This is you'); - $url = $rr['url']; + $url = $contact['url']; $sparkle = ''; } return [ - 'img_hover' => DI::l10n()->t('Visit %s\'s profile [%s]', $rr['name'], $rr['url']), - 'edit_hover'=> DI::l10n()->t('Edit contact'), - 'photo_menu'=> Model\Contact::photoMenu($rr), - 'id' => $rr['id'], - 'alt_text' => $alt_text, - 'dir_icon' => $dir_icon, - 'thumb' => Model\Contact::getThumb($rr), - 'name' => $rr['name'], - 'username' => $rr['name'], - 'details' => $rr['location'], - 'tags' => $rr['keywords'], - 'about' => $rr['about'], - 'account_type' => Model\Contact::getAccountType($rr), - 'sparkle' => $sparkle, - 'itemurl' => ($rr['addr'] ?? '') ?: $rr['url'], - 'url' => $url, - 'network' => ContactSelector::networkToName($rr['network'], $rr['url'], $rr['protocol']), - 'nick' => $rr['nick'], + 'id' => $contact['id'], + 'url' => $url, + 'img_hover' => DI::l10n()->t('Visit %s\'s profile [%s]', $contact['name'], $contact['url']), + 'photo_menu' => Model\Contact::photoMenu($contact), + 'thumb' => Model\Contact::getThumb($contact), + 'alt_text' => $alt_text, + 'name' => $contact['name'], + 'nick' => $contact['nick'], + 'details' => $contact['location'], + 'tags' => $contact['keywords'], + 'about' => $contact['about'], + 'account_type' => Model\Contact::getAccountType($contact), + 'sparkle' => $sparkle, + 'itemurl' => ($contact['addr'] ?? '') ?: $contact['url'], + 'network' => ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol']), ]; } From d9a9876ddd846f583e75c46cd84be9330895770e Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 31 Jul 2020 09:08:51 +0000 Subject: [PATCH 140/573] Synchronize contacts with the directory server --- src/Module/Admin/Site.php | 3 + src/Worker/AddContact.php | 7 +++ src/Worker/Cron.php | 5 ++ src/Worker/PullDirectory.php | 76 ++++++++++++++++++++++++ view/templates/admin/site.tpl | 1 + view/theme/frio/templates/admin/site.tpl | 1 + 6 files changed, 93 insertions(+) create mode 100644 src/Worker/PullDirectory.php diff --git a/src/Module/Admin/Site.php b/src/Module/Admin/Site.php index c1d4479d9a..3bdcd71147 100644 --- a/src/Module/Admin/Site.php +++ b/src/Module/Admin/Site.php @@ -179,6 +179,7 @@ class Site extends BaseAdmin $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); + $synchronize_directory = (!empty($_POST['synchronize_directory']) ? intval(trim($_POST['synchronize_directory'])) : false); $poco_completion = (!empty($_POST['poco_completion']) ? intval(trim($_POST['poco_completion'])) : false); $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); @@ -309,6 +310,7 @@ class Site extends BaseAdmin DI::config()->set('system', 'optimize_fragmentation', $optimize_fragmentation); DI::config()->set('system', 'poco_completion' , $poco_completion); DI::config()->set('system', 'contact_discovery' , $contact_discovery); + DI::config()->set('system', 'synchronize_directory' , $synchronize_directory); 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); @@ -684,6 +686,7 @@ class Site extends BaseAdmin '
  • ' . 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], + '$synchronize_directory' => ['synchronize_directory', DI::l10n()->t('Synchronize the contacts with the directory server'), DI::config()->get('system', 'synchronize_directory'), DI::l10n()->t('if enabled, the system will check periodically for new contacts on the defined directory server.')], '$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.')], '$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.')], diff --git a/src/Worker/AddContact.php b/src/Worker/AddContact.php index 6bb8a41b59..fbec7abdab 100644 --- a/src/Worker/AddContact.php +++ b/src/Worker/AddContact.php @@ -34,6 +34,13 @@ class AddContact */ public static function execute(int $uid, string $url) { + if ($uid == 0) { + // Adding public contact + $result = Contact::getIdForURL($url); + Logger::info('Added public contact', ['url' => $url, 'result' => $result]); + return; + } + $user = User::getById($uid); if (empty($user)) { return; diff --git a/src/Worker/Cron.php b/src/Worker/Cron.php index 6749b9648d..5bdd22f330 100644 --- a/src/Worker/Cron.php +++ b/src/Worker/Cron.php @@ -105,6 +105,11 @@ class Cron // Hourly cron calls if (DI::config()->get('system', 'last_cron_hourly', 0) + 3600 < time()) { + // Search for new contacts in the directory + if (DI::config()->get('system', 'synchronize_directory')) { + Worker::add(PRIORITY_LOW, 'PullDirectory'); + } + // Delete all done workerqueue entries DBA::delete('workerqueue', ['`done` AND `executed` < UTC_TIMESTAMP() - INTERVAL 1 HOUR']); diff --git a/src/Worker/PullDirectory.php b/src/Worker/PullDirectory.php new file mode 100644 index 0000000000..af11a3fd06 --- /dev/null +++ b/src/Worker/PullDirectory.php @@ -0,0 +1,76 @@ +. + * + */ + +namespace Friendica\Worker; + +use Friendica\Core\Logger; +use Friendica\Core\Worker; +use Friendica\DI; +use Friendica\Model\Contact; + +class PullDirectory +{ + /** + * Pull contacts from a directory server + */ + public static function execute() + { + if (!DI::config()->get('system', 'synchronize_directory')) { + Logger::info('Synchronization deactivated'); + return; + } + + $directory = DI::config()->get('system', 'directory'); + if (empty($directory)) { + Logger::info('No directory configured'); + return; + } + + $now = (int)DI::config()->get('system', 'last-directory-sync', 0); + + Logger::info('Synchronization started.', ['now' => $now, 'directory' => $directory]); + + $result = DI::httpRequest()->fetch($directory . '/sync/pull/since/' . $now); + if (empty($result)) { + Logger::info('Directory server return empty result.', ['directory' => $directory]); + return; + } + + $contacts = json_decode($result, true); + if (empty($contacts['results'])) { + Logger::info('No results fetched.', ['directory' => $directory]); + return; + } + + $now = $contacts['now'] ?? 0; + $count = $contacts['count'] ?? 0; + $added = 0; + foreach ($contacts['results'] as $url) { + if (empty(Contact::getByURL($url, false, ['id']))) { + Worker::add(PRIORITY_LOW, 'AddContact', 0, $url); + ++$added; + } + } + DI::config()->set('system', 'last-directory-sync', $now); + + Logger::info('Synchronization ended.', ['now' => $now, 'count' => $count, 'added' => $added, 'directory' => $directory]); + } +} diff --git a/view/templates/admin/site.tpl b/view/templates/admin/site.tpl index 49bae09ef7..57e574725d 100644 --- a/view/templates/admin/site.tpl +++ b/view/templates/admin/site.tpl @@ -98,6 +98,7 @@

    {{$portable_contacts}}

    {{include file="field_select.tpl" field=$contact_discovery}} + {{include file="field_checkbox.tpl" field=$synchronize_directory}} {{include file="field_checkbox.tpl" field=$poco_completion}} {{include file="field_input.tpl" field=$poco_requery_days}} {{include file="field_select.tpl" field=$poco_discovery}} diff --git a/view/theme/frio/templates/admin/site.tpl b/view/theme/frio/templates/admin/site.tpl index 5f5df4aac3..61a9d1e500 100644 --- a/view/theme/frio/templates/admin/site.tpl +++ b/view/theme/frio/templates/admin/site.tpl @@ -219,6 +219,7 @@
    {{include file="field_select.tpl" field=$contact_discovery}} + {{include file="field_checkbox.tpl" field=$synchronize_directory}} {{include file="field_checkbox.tpl" field=$poco_completion}} {{include file="field_input.tpl" field=$poco_requery_days}} {{include file="field_select.tpl" field=$poco_discovery}} From 05bc59e445b585850ac937cdbfeaff71937d638c Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 31 Jul 2020 14:30:33 +0000 Subject: [PATCH 141/573] Direytory sync: Update contact when it already exists --- src/Worker/PullDirectory.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Worker/PullDirectory.php b/src/Worker/PullDirectory.php index af11a3fd06..a83b0a13be 100644 --- a/src/Worker/PullDirectory.php +++ b/src/Worker/PullDirectory.php @@ -63,14 +63,19 @@ class PullDirectory $now = $contacts['now'] ?? 0; $count = $contacts['count'] ?? 0; $added = 0; + $updated = 0; foreach ($contacts['results'] as $url) { - if (empty(Contact::getByURL($url, false, ['id']))) { + $contact = Contact::getByURL($url, false, ['id']); + if (empty($contact['id'])) { Worker::add(PRIORITY_LOW, 'AddContact', 0, $url); ++$added; + } else { + Worker::add(PRIORITY_LOW, "UpdateContact", $contact['id']); + ++$updated; } } DI::config()->set('system', 'last-directory-sync', $now); - Logger::info('Synchronization ended.', ['now' => $now, 'count' => $count, 'added' => $added, 'directory' => $directory]); + Logger::info('Synchronization ended.', ['now' => $now, 'count' => $count, 'added' => $added, 'updated' => $updated, 'directory' => $directory]); } } From 4c089a1f878c7c73068215ef632aef8bcee3cc69 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 31 Jul 2020 17:58:25 +0000 Subject: [PATCH 142/573] "gcign" is removed --- database.sql | 13 +------- mod/suggest.php | 57 +++-------------------------------- static/dbstructure.config.php | 15 +-------- 3 files changed, 7 insertions(+), 78 deletions(-) diff --git a/database.sql b/database.sql index ea89847c19..3f15af1a44 100644 --- a/database.sql +++ b/database.sql @@ -144,6 +144,7 @@ CREATE TABLE IF NOT EXISTS `contact` ( INDEX `dfrn-id` (`dfrn-id`(64)), INDEX `issued-id` (`issued-id`(64)), INDEX `network_uid_lastupdate` (`network`,`uid`,`last-update`), + INDEX `uid_lastitem` (`uid`,`last-item`), INDEX `gsid` (`gsid`), FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='contact table'; @@ -464,18 +465,6 @@ CREATE TABLE IF NOT EXISTS `fsuggest` ( PRIMARY KEY(`id`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='friend suggestion stuff'; --- --- TABLE gcign --- -CREATE TABLE IF NOT EXISTS `gcign` ( - `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', - `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Local User id', - `gcid` int unsigned NOT NULL DEFAULT 0 COMMENT 'gcontact.id of ignored contact', - PRIMARY KEY(`id`), - INDEX `uid` (`uid`), - INDEX `gcid` (`gcid`) -) DEFAULT COLLATE utf8mb4_general_ci COMMENT='contacts ignored by friend suggestions'; - -- -- TABLE gcontact -- diff --git a/mod/suggest.php b/mod/suggest.php index d501e3a3f2..192f59d910 100644 --- a/mod/suggest.php +++ b/mod/suggest.php @@ -26,31 +26,12 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; use Friendica\Module\Contact as ModuleContact; - -function suggest_init(App $a) -{ - if (! local_user()) { - return; - } -} - -function suggest_post(App $a) -{ - if (!empty($_POST['ignore']) && !empty($_POST['confirm'])) { - DBA::insert('gcign', ['uid' => local_user(), 'gcid' => $_POST['ignore']]); - notice(DI::l10n()->t('Contact suggestion successfully ignored.')); - } - - DI::baseUrl()->redirect('suggest'); -} +use Friendica\Network\HTTPException; function suggest_content(App $a) { - $o = ''; - - if (! local_user()) { - notice(DI::l10n()->t('Permission denied.')); - return; + if (!local_user()) { + throw new HTTPException\ForbiddenException(DI::l10n()->t('Permission denied.')); } $_SESSION['return_path'] = DI::args()->getCommand(); @@ -58,35 +39,9 @@ function suggest_content(App $a) DI::page()['aside'] .= Widget::findPeople(); DI::page()['aside'] .= Widget::follow(); - $contacts = Contact::getSuggestions(local_user()); if (!DBA::isResult($contacts)) { - $o .= DI::l10n()->t('No suggestions available. If this is a new site, please try again in 24 hours.'); - return $o; - } - - - if (!empty($_GET['ignore'])) { - //
    can't take arguments in its "action" parameter - // so add any arguments as hidden inputs - $query = explode_querystring(DI::args()->getQueryString()); - $inputs = []; - foreach ($query['args'] as $arg) { - if (strpos($arg, 'confirm=') === false) { - $arg_parts = explode('=', $arg); - $inputs[] = ['name' => $arg_parts[0], 'value' => $arg_parts[1]]; - } - } - - return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [ - '$method' => 'post', - '$message' => DI::l10n()->t('Do you really want to delete this suggestion?'), - '$extra_inputs' => $inputs, - '$confirm' => DI::l10n()->t('Yes'), - '$confirm_url' => $query['base'], - '$confirm_name' => 'confirm', - '$cancel' => DI::l10n()->t('Cancel'), - ]); + return DI::l10n()->t('No suggestions available. If this is a new site, please try again in 24 hours.'); } $entries = []; @@ -96,10 +51,8 @@ function suggest_content(App $a) $tpl = Renderer::getMarkupTemplate('viewcontact_template.tpl'); - $o .= Renderer::replaceMacros($tpl,[ + return Renderer::replaceMacros($tpl,[ '$title' => DI::l10n()->t('Friend Suggestions'), '$contacts' => $entries, ]); - - return $o; } diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index e9f70bec20..8287ecd4e7 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', 1358); + define('DB_UPDATE_VERSION', 1359); } return [ @@ -538,19 +538,6 @@ return [ "PRIMARY" => ["id"], ] ], - "gcign" => [ - "comment" => "contacts ignored by friend suggestions", - "fields" => [ - "id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "sequential ID"], - "uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "relation" => ["user" => "uid"], "comment" => "Local User id"], - "gcid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "relation" => ["gcontact" => "id"], "comment" => "gcontact.id of ignored contact"], - ], - "indexes" => [ - "PRIMARY" => ["id"], - "uid" => ["uid"], - "gcid" => ["gcid"], - ] - ], "gcontact" => [ "comment" => "global contacts", "fields" => [ From 86c924b820e7560356ee6695bd44ed6f5c0a5022 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 31 Jul 2020 18:59:41 +0000 Subject: [PATCH 143/573] Fix description --- src/Worker/UpdateServerDirectory.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Worker/UpdateServerDirectory.php b/src/Worker/UpdateServerDirectory.php index 87bbee7e8c..c23d01c5ac 100644 --- a/src/Worker/UpdateServerDirectory.php +++ b/src/Worker/UpdateServerDirectory.php @@ -28,9 +28,9 @@ class UpdateServerDirectory { /** * Query the given server for their users - * @param string $gserver Server URL + * @param array $gserver Server record */ - public static function execute($gserver) + public static function execute(array $gserver) { GServer::updateDirectory($gserver); return; From 5b8961a88e8f173129ea99cfe089bd6bf2809657 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 31 Jul 2020 19:00:34 +0000 Subject: [PATCH 144/573] Removed unused stuff --- src/Worker/UpdateServerDirectory.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Worker/UpdateServerDirectory.php b/src/Worker/UpdateServerDirectory.php index c23d01c5ac..11455b795f 100644 --- a/src/Worker/UpdateServerDirectory.php +++ b/src/Worker/UpdateServerDirectory.php @@ -21,7 +21,6 @@ namespace Friendica\Worker; -use Friendica\Core\Logger; use Friendica\Model\GServer; class UpdateServerDirectory @@ -33,6 +32,5 @@ class UpdateServerDirectory public static function execute(array $gserver) { GServer::updateDirectory($gserver); - return; } } From bb70258d48085058c75dd82714eca27819f84061 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 1 Aug 2020 05:55:27 +0000 Subject: [PATCH 145/573] Discovery of peers of other servers --- src/Model/GServer.php | 13 +++++-- src/Worker/UpdateServerPeers.php | 66 ++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 src/Worker/UpdateServerPeers.php diff --git a/src/Model/GServer.php b/src/Model/GServer.php index 0f47146eb7..9a8166158b 100644 --- a/src/Model/GServer.php +++ b/src/Model/GServer.php @@ -1591,9 +1591,9 @@ class GServer $gservers = DBA::p("SELECT `id`, `url`, `nurl`, `network`, `poco` FROM `gserver` WHERE NOT `failed` - AND `poco` != '' + AND `directory-type` != ? AND `last_poco_query` < ? - ORDER BY RAND()", $last_update + ORDER BY RAND()", self::DT_NONE, $last_update ); while ($gserver = DBA::fetch($gservers)) { @@ -1604,8 +1604,13 @@ class GServer continue; } - Logger::info('Update directory', ['server' => $gserver['url'], 'id' => $gserver['id']]); - Worker::add(PRIORITY_LOW, 'UpdateServerDirectory', $gserver); + Logger::info('Update peer list', ['server' => $gserver['url'], 'id' => $gserver['id']]); + Worker::add(PRIORITY_LOW, 'UpdateServerPeers', $gserver['url']); + + if ($gserver['directory-type'] == self::DT_POCO) { + Logger::info('Update directory', ['server' => $gserver['url'], 'id' => $gserver['id']]); + Worker::add(PRIORITY_LOW, 'UpdateServerDirectory', $gserver); + } if (--$no_of_queries == 0) { break; diff --git a/src/Worker/UpdateServerPeers.php b/src/Worker/UpdateServerPeers.php new file mode 100644 index 0000000000..ff0cdfa730 --- /dev/null +++ b/src/Worker/UpdateServerPeers.php @@ -0,0 +1,66 @@ +. + * + */ + +namespace Friendica\Worker; + +use Friendica\Core\Logger; +use Friendica\Core\Worker; +use Friendica\Database\DBA; +use Friendica\DI; +use Friendica\Util\Strings; + +class UpdateServerPeers +{ + /** + * Query the given server for their known peers + * @param string $gserver Server URL + */ + public static function execute(string $url) + { + $ret = DI::httpRequest()->get($url . '/api/v1/instance/peers'); + if (!$ret->isSuccess() || empty($ret->getBody())) { + Logger::info('Server is not reachable or does not offer the "peers" endpoint', ['url' => $url]); + return; + } + + $peers = json_decode($ret->getBody()); + if (empty($peers) || !is_array($peers)) { + Logger::info('Server does not have any peers listed', ['url' => $url]); + return; + } + + Logger::info('Server peer update start', ['url' => $url]); + + $total = 0; + $added = 0; + foreach ($peers as $peer) { + ++$total; + if (DBA::exists('gserver', ['nurl' => Strings::normaliseLink('http://' . $peer)])) { + // We already know this server + continue; + } + // This endpoint doesn't offer the schema. So we assume that it is HTTPS. + Worker::add(PRIORITY_LOW, 'UpdateGServer', 'https://' . $peer); + ++$added; + } + Logger::info('Server peer update ended', ['total' => $total, 'added' => $added, 'url' => $url]); + } +} From a9a9f7d51d4c0f3d8d83028179ea6cc394260e92 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 1 Aug 2020 08:56:07 +0000 Subject: [PATCH 146/573] Update the last query at a single place --- src/Model/GServer.php | 9 +++++---- src/Protocol/PortableContact.php | 7 ------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Model/GServer.php b/src/Model/GServer.php index 9a8166158b..07a5eaad3e 100644 --- a/src/Model/GServer.php +++ b/src/Model/GServer.php @@ -1607,11 +1607,12 @@ class GServer Logger::info('Update peer list', ['server' => $gserver['url'], 'id' => $gserver['id']]); Worker::add(PRIORITY_LOW, 'UpdateServerPeers', $gserver['url']); - if ($gserver['directory-type'] == self::DT_POCO) { - Logger::info('Update directory', ['server' => $gserver['url'], 'id' => $gserver['id']]); - Worker::add(PRIORITY_LOW, 'UpdateServerDirectory', $gserver); - } + Logger::info('Update directory', ['server' => $gserver['url'], 'id' => $gserver['id']]); + Worker::add(PRIORITY_LOW, 'UpdateServerDirectory', $gserver); + $fields = ['last_poco_query' => DateTimeFormat::utcNow()]; + DBA::update('gserver', $fields, ['nurl' => $gserver['nurl']]); + if (--$no_of_queries == 0) { break; } diff --git a/src/Protocol/PortableContact.php b/src/Protocol/PortableContact.php index cfc140d66d..8109ef2373 100644 --- a/src/Protocol/PortableContact.php +++ b/src/Protocol/PortableContact.php @@ -330,18 +330,11 @@ class PortableContact } } - $fields = ['last_poco_query' => DateTimeFormat::utcNow()]; - DBA::update('gserver', $fields, ['nurl' => $server["nurl"]]); - return true; } else { // If the server hadn't replied correctly, then force a sanity check GServer::check($server["url"], $server["network"], true); - // If we couldn't reach the server, we will try it some time later - $fields = ['last_poco_query' => DateTimeFormat::utcNow()]; - DBA::update('gserver', $fields, ['nurl' => $server["nurl"]]); - return false; } } From 29762119927168bd75732bb2c22f675ca3359a99 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 1 Aug 2020 09:26:41 +0000 Subject: [PATCH 147/573] Add "Nextcloud" to the statisrics --- src/Module/Admin/Federation.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Module/Admin/Federation.php b/src/Module/Admin/Federation.php index fd60b07152..aef8aa7493 100644 --- a/src/Module/Admin/Federation.php +++ b/src/Module/Admin/Federation.php @@ -42,6 +42,7 @@ class Federation extends BaseAdmin 'hubzilla' => ['name' => 'Hubzilla/Red Matrix', 'color' => '#43488a'], // blue from the logo 'mastodon' => ['name' => 'Mastodon', 'color' => '#1a9df9'], // blue from the Mastodon logo 'misskey' => ['name' => 'Misskey', 'color' => '#ccfefd'], // Font color of the homepage + 'nextcloud' => ['name' => 'Nextcloud', 'color' => '#1cafff'], // Logo color 'peertube' => ['name' => 'Peertube', 'color' => '#ffad5c'], // One of the logo colors 'pixelfed' => ['name' => 'Pixelfed', 'color' => '#11da47'], // One of the logo colors 'pleroma' => ['name' => 'Pleroma', 'color' => '#E46F0F'], // Orange from the text that is used on Pleroma instances From 0c73531da1d93f218a47c798ae5db1d00144a7be Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 1 Aug 2020 16:15:18 +0000 Subject: [PATCH 148/573] Poco and gcontact (mostly) removed --- mod/poco.php | 37 +- mod/settings.php | 4 - src/Content/ContactSelector.php | 8 - src/Database/PostUpdate.php | 56 -- src/Factory/Notification/Introduction.php | 65 +- src/Model/Contact.php | 227 +++++- src/Model/GContact.php | 920 ---------------------- src/Model/GServer.php | 45 +- src/Module/Admin/Federation.php | 3 - src/Module/Admin/Site.php | 27 +- src/Module/Contact.php | 3 - src/Module/NoScrape.php | 3 +- src/Module/Settings/Profile/Index.php | 4 - src/Protocol/DFRN.php | 25 - src/Protocol/Diaspora.php | 25 +- src/Protocol/OStatus.php | 10 - src/Protocol/PortableContact.php | 486 ------------ src/Worker/Cron.php | 5 - src/Worker/CronJobs.php | 9 - src/Worker/FetchPoCo.php | 41 - src/Worker/OnePoll.php | 8 - src/Worker/PullDirectory.php | 17 +- src/Worker/SearchDirectory.php | 43 +- src/Worker/UpdateGContact.php | 44 -- src/Worker/UpdateGContacts.php | 101 --- src/Worker/UpdateServerDirectories.php | 10 +- src/Worker/UpdateServerDirectory.php | 77 +- src/Worker/UpdateServerPeers.php | 37 + src/Worker/UpdateSuggestions.php | 36 - update.php | 13 - view/templates/admin/federation.tpl | 4 - view/templates/admin/site.tpl | 4 +- view/theme/frio/templates/admin/site.tpl | 4 +- view/theme/vier/theme.php | 1 - 34 files changed, 370 insertions(+), 2032 deletions(-) delete mode 100644 src/Protocol/PortableContact.php delete mode 100644 src/Worker/FetchPoCo.php delete mode 100644 src/Worker/UpdateGContact.php delete mode 100644 src/Worker/UpdateGContacts.php delete mode 100644 src/Worker/UpdateSuggestions.php diff --git a/mod/poco.php b/mod/poco.php index abe10ee39c..41a1627271 100644 --- a/mod/poco.php +++ b/mod/poco.php @@ -27,7 +27,7 @@ use Friendica\Core\Protocol; use Friendica\Core\Renderer; use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Protocol\PortableContact; +use Friendica\Model\GServer; use Friendica\Util\DateTimeFormat; use Friendica\Util\Strings; use Friendica\Util\XML; @@ -56,16 +56,15 @@ function poco_init(App $a) { if ($a->argc > 1 && $a->argv[1] === '@server') { // List of all servers that this server knows - $ret = PortableContact::serverlist(); + $ret = GServer::getActive(); header('Content-type: application/json'); echo json_encode($ret); exit(); } if ($a->argc > 1 && $a->argv[1] === '@global') { - // List of all profiles that this server recently had data from - $global = true; - $update_limit = date(DateTimeFormat::MYSQL, time() - 30 * 86400); + // Global is not supported anymore + throw new \Friendica\Network\HTTPException\NotFoundException(); } if ($a->argc > 2 && $a->argv[2] === '@me') { $justme = true; @@ -99,17 +98,10 @@ function poco_init(App $a) { if (!empty($_GET['updatedSince'])) { $update_limit = date(DateTimeFormat::MYSQL, strtotime($_GET['updatedSince'])); } - if ($global) { - $contacts = q("SELECT count(*) AS `total` FROM `gcontact` WHERE `updated` >= '%s' AND `updated` >= `last_failure` AND NOT `hide` AND `network` IN ('%s', '%s', '%s')", - DBA::escape($update_limit), - DBA::escape(Protocol::DFRN), - DBA::escape(Protocol::DIASPORA), - DBA::escape(Protocol::OSTATUS) - ); - } elseif ($system_mode) { + if ($system_mode) { $totalResults = DBA::count('profile', ['net-publish' => true]); } else { - $contacts = q("SELECT count(*) AS `total` FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0 + $contacts = q("SELECT count(*) AS `total` FROM `contact` WHERE `uid` = %d AND NOT `blocked` AND NOT `pending` AND NOT `unsearchable` AND NOT `archive` AND NOT `failed` AND `network` IN ('%s', '%s', '%s', '%s') $sql_extra", intval($user['uid']), @@ -131,24 +123,13 @@ function poco_init(App $a) { } $itemsPerPage = ((!empty($_GET['count'])) ? intval($_GET['count']) : $totalResults); - if ($global) { - Logger::log("Start global query", Logger::DEBUG); - $contacts = q("SELECT * FROM `gcontact` WHERE `updated` > '%s' AND NOT `hide` AND `network` IN ('%s', '%s', '%s') AND `updated` > `last_failure` - ORDER BY `updated` DESC LIMIT %d, %d", - DBA::escape($update_limit), - DBA::escape(Protocol::DFRN), - DBA::escape(Protocol::DIASPORA), - DBA::escape(Protocol::OSTATUS), - intval($startIndex), - intval($itemsPerPage) - ); - } elseif ($system_mode) { + if ($system_mode) { Logger::log("Start system mode query", Logger::DEBUG); $contacts = DBA::selectToArray('owner-view', [], ['net-publish' => true], ['limit' => [$startIndex, $itemsPerPage]]); } else { Logger::log("Start query for user " . $user['nickname'], Logger::DEBUG); - $contacts = q("SELECT * FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0 - AND NOT `failed` + $contacts = q("SELECT * FROM `contact` WHERE `uid` = %d AND NOT `blocked` AND NOT `pending` AND NOT `hiddden` AND NOT `archive` + AND NOT `failed` AND NOT `unsearchable` AND `network` IN ('%s', '%s', '%s', '%s') $sql_extra LIMIT %d, %d", intval($user['uid']), DBA::escape(Protocol::DFRN), diff --git a/mod/settings.php b/mod/settings.php index d02375ed7c..e147144e21 100644 --- a/mod/settings.php +++ b/mod/settings.php @@ -31,7 +31,6 @@ use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Model\GContact; use Friendica\Model\Group; use Friendica\Model\Notify\Type; use Friendica\Model\User; @@ -471,9 +470,6 @@ function settings_post(App $a) Worker::add(PRIORITY_LOW, 'ProfileUpdate', local_user()); - // Update the global contact for the user - GContact::updateForUser(local_user()); - DI::baseUrl()->redirect('settings'); return; // NOTREACHED } diff --git a/src/Content/ContactSelector.php b/src/Content/ContactSelector.php index c834f8c514..ec7bcab95c 100644 --- a/src/Content/ContactSelector.php +++ b/src/Content/ContactSelector.php @@ -76,14 +76,6 @@ class ContactSelector $server_url = Strings::normaliseLink($contact['baseurl']); } - if (empty($server_url)) { - // Fetch the server url from the gcontact table - $gcontact = DBA::selectFirst('gcontact', ['server_url'], ['nurl' => Strings::normaliseLink($profile)]); - if (!empty($gcontact) && !empty($gcontact['server_url'])) { - $server_url = Strings::normaliseLink($gcontact['server_url']); - } - } - if (empty($server_url)) { // Create the server url out of the profile url $parts = parse_url($profile); diff --git a/src/Database/PostUpdate.php b/src/Database/PostUpdate.php index 6d8b197db1..6a8d663c5a 100644 --- a/src/Database/PostUpdate.php +++ b/src/Database/PostUpdate.php @@ -93,9 +93,6 @@ class PostUpdate if (!self::update1349()) { return false; } - if (!self::update1350()) { - return false; - } return true; } @@ -997,57 +994,4 @@ class PostUpdate return false; } - - /** - * update the "gsid" (global server id) field in the gcontact table - * - * @return bool "true" when the job is done - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - private static function update1350() - { - // Was the script completed? - if (DI::config()->get("system", "post_update_version") >= 1350) { - return true; - } - - $id = DI::config()->get("system", "post_update_version_1350_id", 0); - - Logger::info('Start', ['gcontact' => $id]); - - $start_id = $id; - $rows = 0; - $condition = ["`id` > ? AND `gsid` IS NULL AND `server_url` != '' AND NOT `server_url` IS NULL", $id]; - $params = ['order' => ['id'], 'limit' => 10000]; - $gcontacts = DBA::select('gcontact', ['id', 'server_url'], $condition, $params); - - if (DBA::errorNo() != 0) { - Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]); - return false; - } - - while ($gcontact = DBA::fetch($gcontacts)) { - $id = $gcontact['id']; - - DBA::update('gcontact', - ['gsid' => GServer::getID($gcontact['server_url'], true), 'server_url' => GServer::cleanURL($gcontact['server_url'])], - ['id' => $gcontact['id']]); - - ++$rows; - } - DBA::close($gcontacts); - - DI::config()->set("system", "post_update_version_1350_id", $id); - - Logger::info('Processed', ['rows' => $rows, 'last' => $id]); - - if ($start_id == $id) { - DI::config()->set("system", "post_update_version", 1350); - Logger::info('Done'); - return true; - } - - return false; - } } diff --git a/src/Factory/Notification/Introduction.php b/src/Factory/Notification/Introduction.php index 21aef9297a..a16926f967 100644 --- a/src/Factory/Notification/Introduction.php +++ b/src/Factory/Notification/Introduction.php @@ -99,17 +99,13 @@ class Introduction extends BaseFactory $formattedNotifications = []; try { - /// @todo Fetch contact details by "Contact::getByUrl" instead of queries to contact, fcontact and gcontact + /// @todo Fetch contact details by "Contact::getByUrl" instead of queries to contact and fcontact $stmtNotifications = $this->dba->p( "SELECT `intro`.`id` AS `intro_id`, `intro`.*, `contact`.*, `fcontact`.`name` AS `fname`, `fcontact`.`url` AS `furl`, `fcontact`.`addr` AS `faddr`, - `fcontact`.`photo` AS `fphoto`, `fcontact`.`request` AS `frequest`, - `gcontact`.`location` AS `glocation`, `gcontact`.`about` AS `gabout`, - `gcontact`.`keywords` AS `gkeywords`, - `gcontact`.`network` AS `gnetwork`, `gcontact`.`addr` AS `gaddr` + `fcontact`.`photo` AS `fphoto`, `fcontact`.`request` AS `frequest` FROM `intro` LEFT JOIN `contact` ON `contact`.`id` = `intro`.`contact-id` - LEFT JOIN `gcontact` ON `gcontact`.`nurl` = `contact`.`nurl` LEFT JOIN `fcontact` ON `intro`.`fid` = `fcontact`.`id` WHERE `intro`.`uid` = ? $sql_extra LIMIT ?, ?", @@ -147,16 +143,14 @@ class Introduction extends BaseFactory // Normal connection requests } else { - $notification = $this->getMissingData($notification); - if (empty($notification['url'])) { continue; } // Don't show these data until you are connected. Diaspora is doing the same. - if ($notification['gnetwork'] === Protocol::DIASPORA) { - $notification['glocation'] = ""; - $notification['gabout'] = ""; + if ($notification['network'] === Protocol::DIASPORA) { + $notification['location'] = ""; + $notification['about'] = ""; } $formattedNotifications[] = new Notification\Introduction([ @@ -166,17 +160,17 @@ class Introduction extends BaseFactory 'uid' => $this->session->get('uid'), 'intro_id' => $notification['intro_id'], 'contact_id' => $notification['contact-id'], - 'photo' => (!empty($notification['photo']) ? Proxy::proxifyUrl($notification['photo'], false, Proxy::SIZE_SMALL) : "images/person-300.jpg"), + 'photo' => Contact::getPhoto($notification), 'name' => $notification['name'], - 'location' => BBCode::convert($notification['glocation'], false), - 'about' => BBCode::convert($notification['gabout'], false), - 'keywords' => $notification['gkeywords'], + 'location' => BBCode::convert($notification['location'], false), + 'about' => BBCode::convert($notification['about'], false), + 'keywords' => $notification['keywords'], 'hidden' => $notification['hidden'] == 1, 'post_newfriend' => (intval($this->pConfig->get(local_user(), 'system', 'post_newfriend')) ? '1' : 0), 'url' => $notification['url'], 'zrl' => Contact::magicLink($notification['url']), - 'addr' => $notification['gaddr'], - 'network' => $notification['gnetwork'], + 'addr' => $notification['addr'], + 'network' => $notification['network'], 'knowyou' => $notification['knowyou'], 'note' => $notification['note'], ]); @@ -188,41 +182,4 @@ class Introduction extends BaseFactory return $formattedNotifications; } - - /** - * Check for missing contact data and try to fetch the data from - * from other sources - * - * @param array $intro The input array with the intro data - * - * @return array The array with the intro data - * - * @throws InternalServerErrorException - */ - private function getMissingData(array $intro) - { - // If the network and the addr isn't available from the gcontact - // table entry, take the one of the contact table entry - if (empty($intro['gnetwork']) && !empty($intro['network'])) { - $intro['gnetwork'] = $intro['network']; - } - if (empty($intro['gaddr']) && !empty($intro['addr'])) { - $intro['gaddr'] = $intro['addr']; - } - - // If the network and addr is still not available - // get the missing data data from other sources - if (empty($intro['gnetwork']) || empty($intro['gaddr'])) { - $ret = Contact::getByURL($intro['url'], false, ['network', 'addr']); - - if (empty($intro['gnetwork']) && !empty($ret['network'])) { - $intro['gnetwork'] = $ret['network']; - } - if (empty($intro['gaddr']) && !empty($ret['addr'])) { - $intro['gaddr'] = $ret['addr']; - } - } - - return $intro; - } } diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 61b4c9d05a..18c378179e 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -21,6 +21,8 @@ namespace Friendica\Model; +use DOMDocument; +use DOMXPath; use Friendica\App\BaseURL; use Friendica\Content\Pager; use Friendica\Core\Hook; @@ -88,7 +90,7 @@ class Contact /** * Account types * - * TYPE_UNKNOWN - the account has been imported from gcontact where this is the default type value + * TYPE_UNKNOWN - unknown type * * TYPE_PERSON - the account belongs to a person * Associated page types: PAGE_NORMAL, PAGE_SOAPBOX, PAGE_FREELOVE @@ -1021,7 +1023,6 @@ class Contact */ DBA::update('contact', ['archive' => true], ['id' => $contact['id']]); DBA::update('contact', ['archive' => true], ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]); - GContact::updateFromPublicContactURL($contact['url']); } } } @@ -1066,7 +1067,6 @@ class Contact $fields = ['failed' => false, 'term-date' => DBA::NULL_DATETIME, 'archive' => false]; DBA::update('contact', $fields, ['id' => $contact['id']]); DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]); - GContact::updateFromPublicContactURL($contact['url']); } /** @@ -1269,12 +1269,8 @@ class Contact $fields = ['url', 'addr', 'alias', 'notify', 'name', 'nick', 'photo', 'keywords', 'location', 'about', 'network']; - $data = DBA::selectFirst('gcontact', $fields, ['nurl' => Strings::normaliseLink($url)]); - - if (!DBA::isResult($data)) { - $condition = ['alias' => [$url, Strings::normaliseLink($url), $ssl_url]]; - $data = DBA::selectFirst('contact', $fields, $condition); - } + $condition = ['alias' => [$url, Strings::normaliseLink($url), $ssl_url]]; + $data = DBA::selectFirst('contact', $fields, $condition); if (DBA::isResult($data)) { $data["pubkey"] = ''; @@ -1706,7 +1702,6 @@ class Contact // There are several fields that indicate that the contact or user is a forum // "page-flags" is a field in the user table, // "forum" and "prv" are used in the contact table. They stand for User::PAGE_FLAGS_COMMUNITY and User::PAGE_FLAGS_PRVGROUP. - // "community" is used in the gcontact table and is true if the contact is User::PAGE_FLAGS_COMMUNITY or User::PAGE_FLAGS_PRVGROUP. if ((isset($contact['page-flags']) && (intval($contact['page-flags']) == User::PAGE_FLAGS_COMMUNITY)) || (isset($contact['page-flags']) && (intval($contact['page-flags']) == User::PAGE_FLAGS_PRVGROUP)) || (isset($contact['forum']) && intval($contact['forum'])) @@ -1994,9 +1989,6 @@ class Contact return; } - // Update the corresponding gcontact entry - GContact::updateFromPublicContactID($id); - // Archive or unarchive the contact. We only need to do this for the public contact. // The archive/unarchive function will update the personal contacts by themselves. $contact = DBA::selectFirst('contact', [], ['id' => $id]); @@ -2155,11 +2147,6 @@ class Contact $new_pubkey = $ret['pubkey']; - // Update the gcontact entry - if ($uid == 0) { - GContact::updateFromPublicContactID($id); - } - $update = false; // make sure to not overwrite existing values with blank entries except some technical fields @@ -3068,4 +3055,208 @@ class Contact return array_slice($contacts, $start, $limit); } + + /** + * Add public contacts from an array + * + * @param array $urls + * @return array result "count", "added" and "updated" + */ + public static function addContactsByArray(array $urls) + { + $added = 0; + $updated = 0; + $count = 0; + + foreach ($urls as $url) { + $contact = Contact::getByURL($url, false, ['id']); + if (empty($contact['id'])) { + Worker::add(PRIORITY_LOW, 'AddContact', 0, $url); + ++$added; + } else { + Worker::add(PRIORITY_LOW, 'UpdateContact', $contact['id']); + ++$updated; + } + ++$count; + } + + return ['count' => $count, 'added' => $added, 'updated' => $updated]; + } + + /** + * Set the last date that the contact had posted something + * + * This functionality is currently unused + * + * @param string $data probing result + * @param bool $force force updating + */ + private static function setLastUpdate(array $data, bool $force = false) + { + $contact = self::getByURL($data['url'], false, []); + if (empty($contact)) { + return; + } + if (!$force && !GServer::updateNeeded($contact['created'], $contact['updated'], $contact['last_failure'], $contact['last_contact'])) { + Logger::info("Don't update profile", ['url' => $data['url'], 'updated' => $contact['updated']]); + return; + } + + if (self::updateFromNoScrape($data)) { + return; + } + + if (!empty($data['outbox'])) { + self::updateFromOutbox($data['outbox'], $data); + } elseif (!empty($data['poll']) && ($data['network'] == Protocol::ACTIVITYPUB)) { + self::updateFromOutbox($data['poll'], $data); + } elseif (!empty($data['poll'])) { + self::updateFromFeed($data); + } + } + + /** + * Update a global contact via the "noscrape" endpoint + * + * @param string $data Probing result + * + * @return bool 'true' if update was successful or the server was unreachable + */ + private static function updateFromNoScrape(array $data) + { + // Check the 'noscrape' endpoint when it is a Friendica server + $gserver = DBA::selectFirst('gserver', ['noscrape'], ["`nurl` = ? AND `noscrape` != ''", + Strings::normaliseLink($data['baseurl'])]); + if (!DBA::isResult($gserver)) { + return false; + } + + $curlResult = DI::httpRequest()->get($gserver['noscrape'] . '/' . $data['nick']); + + if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { + $noscrape = json_decode($curlResult->getBody(), true); + if (!empty($noscrape) && !empty($noscrape['updated'])) { + $noscrape['updated'] = DateTimeFormat::utc($noscrape['updated'], DateTimeFormat::MYSQL); + $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $noscrape['updated']]; + DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); + return true; + } + } elseif ($curlResult->isTimeout()) { + // On a timeout return the existing value, but mark the contact as failure + $fields = ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()]; + DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); + return true; + } + return false; + } + + /** + * Update a global contact via an ActivityPub Outbox + * + * @param string $feed + * @param array $data Probing result + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + private static function updateFromOutbox(string $feed, array $data) + { + $outbox = ActivityPub::fetchContent($feed); + if (empty($outbox)) { + return; + } + + if (!empty($outbox['orderedItems'])) { + $items = $outbox['orderedItems']; + } elseif (!empty($outbox['first']['orderedItems'])) { + $items = $outbox['first']['orderedItems']; + } elseif (!empty($outbox['first']['href']) && ($outbox['first']['href'] != $feed)) { + self::updateFromOutbox($outbox['first']['href'], $data); + return; + } elseif (!empty($outbox['first'])) { + if (is_string($outbox['first']) && ($outbox['first'] != $feed)) { + self::updateFromOutbox($outbox['first'], $data); + } else { + Logger::warning('Unexpected data', ['outbox' => $outbox]); + } + return; + } else { + $items = []; + } + + $last_updated = ''; + foreach ($items as $activity) { + if (!empty($activity['published'])) { + $published = DateTimeFormat::utc($activity['published']); + } elseif (!empty($activity['object']['published'])) { + $published = DateTimeFormat::utc($activity['object']['published']); + } else { + continue; + } + + if ($last_updated < $published) { + $last_updated = $published; + } + } + + if (empty($last_updated)) { + return; + } + + $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $last_updated]; + DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); + } + + /** + * Update a global contact via an XML feed + * + * @param string $data Probing result + */ + private static function updateFromFeed(array $data) + { + // Search for the newest entry in the feed + $curlResult = DI::httpRequest()->get($data['poll']); + if (!$curlResult->isSuccess()) { + $fields = ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()]; + DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); + + Logger::info("Profile wasn't reachable (no feed)", ['url' => $data['url']]); + return; + } + + $doc = new DOMDocument(); + @$doc->loadXML($curlResult->getBody()); + + $xpath = new DOMXPath($doc); + $xpath->registerNamespace('atom', 'http://www.w3.org/2005/Atom'); + + $entries = $xpath->query('/atom:feed/atom:entry'); + + $last_updated = ''; + + foreach ($entries as $entry) { + $published_item = $xpath->query('atom:published/text()', $entry)->item(0); + $updated_item = $xpath->query('atom:updated/text()' , $entry)->item(0); + $published = !empty($published_item->nodeValue) ? DateTimeFormat::utc($published_item->nodeValue) : null; + $updated = !empty($updated_item->nodeValue) ? DateTimeFormat::utc($updated_item->nodeValue) : null; + + if (empty($published) || empty($updated)) { + Logger::notice('Invalid entry for XPath.', ['entry' => $entry, 'url' => $data['url']]); + continue; + } + + if ($last_updated < $published) { + $last_updated = $published; + } + + if ($last_updated < $updated) { + $last_updated = $updated; + } + } + + if (empty($last_updated)) { + return; + } + + $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $last_updated]; + DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); + } } diff --git a/src/Model/GContact.php b/src/Model/GContact.php index fdf2d14077..6549e9efe2 100644 --- a/src/Model/GContact.php +++ b/src/Model/GContact.php @@ -21,171 +21,16 @@ namespace Friendica\Model; -use DOMDocument; -use DOMXPath; use Exception; -use Friendica\Core\Logger; use Friendica\Core\Protocol; -use Friendica\Core\Search; -use Friendica\Core\System; use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Network\Probe; -use Friendica\Protocol\ActivityPub; -use Friendica\Protocol\PortableContact; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Strings; /** * This class handles GlobalContact related functions */ class GContact { - /** - * Link the gcontact entry with user, contact and global contact - * - * @param integer $gcid Global contact ID - * @param integer $uid User ID - * @param integer $cid Contact ID - * @param integer $zcid Global Contact ID - * @return void - * @throws Exception - */ - public static function link($gcid, $uid = 0, $cid = 0, $zcid = 0) - { - if ($gcid <= 0) { - return; - } - - $condition = ['cid' => $cid, 'uid' => $uid, 'gcid' => $gcid, 'zcid' => $zcid]; - DBA::update('glink', ['updated' => DateTimeFormat::utcNow()], $condition, true); - } - - /** - * Sanitize the given gcontact data - * - * Generation: - * 0: No definition - * 1: Profiles on this server - * 2: Contacts of profiles on this server - * 3: Contacts of contacts of profiles on this server - * 4: ... - * - * @param array $gcontact array with gcontact data - * @return array $gcontact - * @throws Exception - */ - public static function sanitize($gcontact) - { - if (empty($gcontact['url'])) { - throw new Exception('URL is empty'); - } - - $gcontact['server_url'] = $gcontact['server_url'] ?? ''; - - $urlparts = parse_url($gcontact['url']); - if (empty($urlparts['scheme'])) { - throw new Exception('This (' . $gcontact['url'] . ") doesn't seem to be an url."); - } - - if (in_array($urlparts['host'], ['twitter.com', 'identi.ca'])) { - throw new Exception('Contact from a non federated network ignored. (' . $gcontact['url'] . ')'); - } - - // Don't store the statusnet connector as network - // We can't simply set this to Protocol::OSTATUS since the connector could have fetched posts from friendica as well - if ($gcontact['network'] == Protocol::STATUSNET) { - $gcontact['network'] = ''; - } - - // Assure that there are no parameter fragments in the profile url - if (empty($gcontact['*network']) || in_array($gcontact['network'], Protocol::FEDERATED)) { - $gcontact['url'] = self::cleanContactUrl($gcontact['url']); - } - - // The global contacts should contain the original picture, not the cached one - if (($gcontact['generation'] != 1) && stristr(Strings::normaliseLink($gcontact['photo']), Strings::normaliseLink(DI::baseUrl() . '/photo/'))) { - $gcontact['photo'] = ''; - } - - if (empty($gcontact['network'])) { - $gcontact['network'] = ''; - - $condition = ["`uid` = 0 AND `nurl` = ? AND `network` != '' AND `network` != ?", - Strings::normaliseLink($gcontact['url']), Protocol::STATUSNET]; - $contact = DBA::selectFirst('contact', ['network'], $condition); - if (DBA::isResult($contact)) { - $gcontact['network'] = $contact['network']; - } - - if (($gcontact['network'] == '') || ($gcontact['network'] == Protocol::OSTATUS)) { - $condition = ["`uid` = 0 AND `alias` IN (?, ?) AND `network` != '' AND `network` != ?", - $gcontact['url'], Strings::normaliseLink($gcontact['url']), Protocol::STATUSNET]; - $contact = DBA::selectFirst('contact', ['network'], $condition); - if (DBA::isResult($contact)) { - $gcontact['network'] = $contact['network']; - } - } - } - - $fields = ['network', 'updated', 'server_url', 'url', 'addr']; - $gcnt = DBA::selectFirst('gcontact', $fields, ['nurl' => Strings::normaliseLink($gcontact['url'])]); - if (DBA::isResult($gcnt)) { - if (!isset($gcontact['network']) && ($gcnt['network'] != Protocol::STATUSNET)) { - $gcontact['network'] = $gcnt['network']; - } - if ($gcontact['updated'] <= DBA::NULL_DATETIME) { - $gcontact['updated'] = $gcnt['updated']; - } - if (!isset($gcontact['server_url']) && (Strings::normaliseLink($gcnt['server_url']) != Strings::normaliseLink($gcnt['url']))) { - $gcontact['server_url'] = $gcnt['server_url']; - } - if (!isset($gcontact['addr'])) { - $gcontact['addr'] = $gcnt['addr']; - } - } - - if ((!isset($gcontact['network']) || !isset($gcontact['name']) || !isset($gcontact['addr']) || !isset($gcontact['photo']) || !isset($gcontact['server_url'])) - && GServer::reachable($gcontact['url'], $gcontact['server_url'], $gcontact['network'], false) - ) { - $data = Probe::uri($gcontact['url']); - - if ($data['network'] == Protocol::PHANTOM) { - throw new Exception('Probing for URL ' . $gcontact['url'] . ' failed'); - } - - $gcontact['server_url'] = $data['baseurl']; - $gcontact['failed'] = false; - - $gcontact = array_merge($gcontact, $data); - } - - if (!isset($gcontact['name']) || !isset($gcontact['photo'])) { - throw new Exception('No name and photo for URL '.$gcontact['url']); - } - - if (!in_array($gcontact['network'], Protocol::FEDERATED)) { - throw new Exception('No federated network (' . $gcontact['network'] . ') detected for URL ' . $gcontact['url']); - } - - if (empty($gcontact['server_url'])) { - // We check the server url to be sure that it is a real one - $server_url = self::getBasepath($gcontact['url']); - - // We are now sure that it is a correct URL. So we use it in the future - if ($server_url != '') { - $gcontact['server_url'] = $server_url; - } - } - - // The server URL doesn't seem to be valid, so we don't store it. - if (!GServer::check($gcontact['server_url'], $gcontact['network'])) { - $gcontact['server_url'] = ''; - } - - return $gcontact; - } - /** * @param integer $uid id * @param integer $cid id @@ -361,771 +206,6 @@ class GContact return $r; } - /** - * @return void - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public static function updateSuggestions() - { - $done = []; - - /// @TODO Check if it is really neccessary to poll the own server - PortableContact::loadWorker(0, 0, 0, DI::baseUrl() . '/poco'); - - $done[] = DI::baseUrl() . '/poco'; - - if (strlen(DI::config()->get('system', 'directory'))) { - $x = DI::httpRequest()->fetch(Search::getGlobalDirectory() . '/pubsites'); - if (!empty($x)) { - $j = json_decode($x); - if (!empty($j->entries)) { - foreach ($j->entries as $entry) { - GServer::check($entry->url); - - $url = $entry->url . '/poco'; - if (!in_array($url, $done)) { - PortableContact::loadWorker(0, 0, 0, $url); - $done[] = $url; - } - } - } - } - } - - // Query your contacts from Friendica and Redmatrix/Hubzilla for their contacts - $contacts = DBA::p("SELECT DISTINCT(`poco`) AS `poco` FROM `contact` WHERE `network` IN (?, ?)", Protocol::DFRN, Protocol::DIASPORA); - while ($contact = DBA::fetch($contacts)) { - $base = substr($contact['poco'], 0, strrpos($contact['poco'], '/')); - if (!in_array($base, $done)) { - PortableContact::loadWorker(0, 0, 0, $base); - } - } - DBA::close($contacts); - } - - /** - * Removes unwanted parts from a contact url - * - * @param string $url Contact url - * - * @return string Contact url with the wanted parts - * @throws Exception - */ - public static function cleanContactUrl($url) - { - $parts = parse_url($url); - - if (empty($parts['scheme']) || empty($parts['host'])) { - return $url; - } - - $new_url = $parts['scheme'] . '://' . $parts['host']; - - if (!empty($parts['port'])) { - $new_url .= ':' . $parts['port']; - } - - if (!empty($parts['path'])) { - $new_url .= $parts['path']; - } - - if ($new_url != $url) { - Logger::info('Cleaned contact url', ['url' => $url, 'new_url' => $new_url, 'callstack' => System::callstack()]); - } - - return $new_url; - } - - /** - * Fetch the gcontact id, add an entry if not existed - * - * @param array $contact contact array - * - * @return bool|int Returns false if not found, integer if contact was found - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function getId($contact) - { - if (empty($contact['network'])) { - Logger::notice('Empty network', ['url' => $contact['url'], 'callstack' => System::callstack()]); - return false; - } - - if (in_array($contact['network'], [Protocol::PHANTOM])) { - Logger::notice('Invalid network', ['url' => $contact['url'], 'callstack' => System::callstack()]); - return false; - } - - if ($contact['network'] == Protocol::STATUSNET) { - $contact['network'] = Protocol::OSTATUS; - } - - // Remove unwanted parts from the contact url (e.g. '?zrl=...') - if (in_array($contact['network'], Protocol::FEDERATED)) { - $contact['url'] = self::cleanContactUrl($contact['url']); - } - - $condition = ['nurl' => Strings::normaliseLink($contact['url'])]; - $gcontact = DBA::selectFirst('gcontact', ['id'], $condition, ['order' => ['id']]); - if (DBA::isResult($gcontact)) { - return $gcontact['id']; - } - - $contact['location'] = $contact['location'] ?? ''; - $contact['about'] = $contact['about'] ?? ''; - $contact['generation'] = $contact['generation'] ?? 0; - $contact['hide'] = $contact['hide'] ?? true; - - $fields = ['name' => $contact['name'], 'nick' => $contact['nick'] ?? '', 'addr' => $contact['addr'] ?? '', 'network' => $contact['network'], - 'url' => $contact['url'], 'nurl' => Strings::normaliseLink($contact['url']), 'photo' => $contact['photo'], - 'created' => DateTimeFormat::utcNow(), 'updated' => DateTimeFormat::utcNow(), 'location' => $contact['location'], - 'about' => $contact['about'], 'hide' => $contact['hide'], 'generation' => $contact['generation'], 'failed' => false]; - - DBA::insert('gcontact', $fields); - - // We intentionally aren't using lastInsertId here. There is a chance for duplicates. - $gcontact = DBA::selectFirst('gcontact', ['id'], $condition, ['order' => ['id']]); - if (!DBA::isResult($gcontact)) { - Logger::info('GContact creation failed', $fields); - // Shouldn't happen - return 0; - } - return $gcontact['id']; - } - - /** - * Updates the gcontact table from a given array - * - * @param array $contact contact array - * - * @return bool|int Returns false if not found, integer if contact was found - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function update($contact) - { - // Check for invalid "contact-type" value - if (isset($contact['contact-type']) && (intval($contact['contact-type']) < 0)) { - $contact['contact-type'] = 0; - } - - /// @todo update contact table as well - - $gcontact_id = self::getId($contact); - - if (!$gcontact_id) { - return false; - } - - $public_contact = DBA::selectFirst('gcontact', [ - 'name', 'nick', 'photo', 'location', 'about', 'addr', 'generation', 'birthday', 'keywords', 'gsid', 'failed', - 'contact-type', 'hide', 'nsfw', 'network', 'alias', 'notify', 'server_url', 'connect', 'updated', 'url' - ], ['id' => $gcontact_id]); - - if (!DBA::isResult($public_contact)) { - return false; - } - - // Get all field names - $fields = []; - foreach ($public_contact as $field => $data) { - $fields[$field] = $data; - } - - unset($fields['url']); - unset($fields['updated']); - unset($fields['hide']); - - // Bugfix: We had an error in the storing of keywords which lead to the "0" - // This value is still transmitted via poco. - if (isset($contact['keywords']) && ($contact['keywords'] == '0')) { - unset($contact['keywords']); - } - - if (isset($public_contact['keywords']) && ($public_contact['keywords'] == '0')) { - $public_contact['keywords'] = ''; - } - - // assign all unassigned fields from the database entry - foreach ($fields as $field => $data) { - if (empty($contact[$field])) { - $contact[$field] = $public_contact[$field]; - } - } - - if (!isset($contact['hide'])) { - $contact['hide'] = $public_contact['hide']; - } - - $fields['hide'] = $public_contact['hide']; - - if ($contact['network'] == Protocol::STATUSNET) { - $contact['network'] = Protocol::OSTATUS; - } - - if (!isset($contact['updated'])) { - $contact['updated'] = DateTimeFormat::utcNow(); - } - - if ($contact['network'] == Protocol::TWITTER) { - $contact['server_url'] = 'http://twitter.com'; - } - - if (empty($contact['server_url'])) { - $data = Probe::uri($contact['url']); - if ($data['network'] != Protocol::PHANTOM) { - $contact['server_url'] = $data['baseurl']; - } - } else { - $contact['server_url'] = Strings::normaliseLink($contact['server_url']); - } - - if (!empty($contact['server_url']) && empty($contact['gsid'])) { - $contact['gsid'] = GServer::getID($contact['server_url']); - } - - if (empty($contact['addr']) && !empty($contact['server_url']) && !empty($contact['nick'])) { - $hostname = str_replace('http://', '', $contact['server_url']); - $contact['addr'] = $contact['nick'] . '@' . $hostname; - } - - // Check if any field changed - $update = false; - unset($fields['generation']); - - if ((($contact['generation'] > 0) && ($contact['generation'] <= $public_contact['generation'])) || ($public_contact['generation'] == 0)) { - foreach ($fields as $field => $data) { - if ($contact[$field] != $public_contact[$field]) { - Logger::debug('Difference found.', ['contact' => $contact['url'], 'field' => $field, 'new' => $contact[$field], 'old' => $public_contact[$field]]); - $update = true; - } - } - - if ($contact['generation'] < $public_contact['generation']) { - Logger::debug('Difference found.', ['contact' => $contact['url'], 'field' => 'generation', 'new' => $contact['generation'], 'old' => $public_contact['generation']]); - $update = true; - } - } - - if ($update) { - Logger::debug('Update gcontact.', ['contact' => $contact['url']]); - $condition = ["`nurl` = ? AND (`generation` = 0 OR `generation` >= ?)", - Strings::normaliseLink($contact['url']), $contact['generation']]; - $contact['updated'] = DateTimeFormat::utc($contact['updated']); - - $updated = [ - 'photo' => $contact['photo'], 'name' => $contact['name'], - 'nick' => $contact['nick'], 'addr' => $contact['addr'], - 'network' => $contact['network'], 'birthday' => $contact['birthday'], - 'keywords' => $contact['keywords'], - 'hide' => $contact['hide'], 'nsfw' => $contact['nsfw'], - 'contact-type' => $contact['contact-type'], 'alias' => $contact['alias'], - 'notify' => $contact['notify'], 'url' => $contact['url'], - 'location' => $contact['location'], 'about' => $contact['about'], - 'generation' => $contact['generation'], 'updated' => $contact['updated'], - 'server_url' => $contact['server_url'], 'connect' => $contact['connect'], - 'failed' => $contact['failed'], 'gsid' => $contact['gsid'] - ]; - - DBA::update('gcontact', $updated, $condition, $fields); - } - - return $gcontact_id; - } - - /** - * Set the last date that the contact had posted something - * - * @param string $data Probing result - * @param bool $force force updating - */ - public static function setLastUpdate(array $data, bool $force = false) - { - // Fetch the global contact - $gcontact = DBA::selectFirst('gcontact', ['created', 'updated', 'last_contact', 'last_failure'], - ['nurl' => Strings::normaliseLink($data['url'])]); - if (!DBA::isResult($gcontact)) { - return; - } - - if (!$force && !GServer::updateNeeded($gcontact['created'], $gcontact['updated'], $gcontact['last_failure'], $gcontact['last_contact'])) { - Logger::info("Don't update profile", ['url' => $data['url'], 'updated' => $gcontact['updated']]); - return; - } - - if (self::updateFromNoScrape($data)) { - return; - } - - if (!empty($data['outbox'])) { - self::updateFromOutbox($data['outbox'], $data); - } elseif (!empty($data['poll']) && ($data['network'] == Protocol::ACTIVITYPUB)) { - self::updateFromOutbox($data['poll'], $data); - } elseif (!empty($data['poll'])) { - self::updateFromFeed($data); - } - } - - /** - * Update a global contact via the "noscrape" endpoint - * - * @param string $data Probing result - * - * @return bool 'true' if update was successful or the server was unreachable - */ - private static function updateFromNoScrape(array $data) - { - // Check the 'noscrape' endpoint when it is a Friendica server - $gserver = DBA::selectFirst('gserver', ['noscrape'], ["`nurl` = ? AND `noscrape` != ''", - Strings::normaliseLink($data['baseurl'])]); - if (!DBA::isResult($gserver)) { - return false; - } - - $curlResult = DI::httpRequest()->get($gserver['noscrape'] . '/' . $data['nick']); - - if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { - $noscrape = json_decode($curlResult->getBody(), true); - if (!empty($noscrape) && !empty($noscrape['updated'])) { - $noscrape['updated'] = DateTimeFormat::utc($noscrape['updated'], DateTimeFormat::MYSQL); - $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $noscrape['updated']]; - DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); - return true; - } - } elseif ($curlResult->isTimeout()) { - // On a timeout return the existing value, but mark the contact as failure - $fields = ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()]; - DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); - return true; - } - return false; - } - - /** - * Update a global contact via an ActivityPub Outbox - * - * @param string $feed - * @param array $data Probing result - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - private static function updateFromOutbox(string $feed, array $data) - { - $outbox = ActivityPub::fetchContent($feed); - if (empty($outbox)) { - return; - } - - if (!empty($outbox['orderedItems'])) { - $items = $outbox['orderedItems']; - } elseif (!empty($outbox['first']['orderedItems'])) { - $items = $outbox['first']['orderedItems']; - } elseif (!empty($outbox['first']['href']) && ($outbox['first']['href'] != $feed)) { - self::updateFromOutbox($outbox['first']['href'], $data); - return; - } elseif (!empty($outbox['first'])) { - if (is_string($outbox['first']) && ($outbox['first'] != $feed)) { - self::updateFromOutbox($outbox['first'], $data); - } else { - Logger::warning('Unexpected data', ['outbox' => $outbox]); - } - return; - } else { - $items = []; - } - - $last_updated = ''; - foreach ($items as $activity) { - if (!empty($activity['published'])) { - $published = DateTimeFormat::utc($activity['published']); - } elseif (!empty($activity['object']['published'])) { - $published = DateTimeFormat::utc($activity['object']['published']); - } else { - continue; - } - - if ($last_updated < $published) { - $last_updated = $published; - } - } - - if (empty($last_updated)) { - return; - } - - $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $last_updated]; - DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); - } - - /** - * Update a global contact via an XML feed - * - * @param string $data Probing result - */ - private static function updateFromFeed(array $data) - { - // Search for the newest entry in the feed - $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'])]); - - Logger::info("Profile wasn't reachable (no feed)", ['url' => $data['url']]); - return; - } - - $doc = new DOMDocument(); - @$doc->loadXML($curlResult->getBody()); - - $xpath = new DOMXPath($doc); - $xpath->registerNamespace('atom', 'http://www.w3.org/2005/Atom'); - - $entries = $xpath->query('/atom:feed/atom:entry'); - - $last_updated = ''; - - foreach ($entries as $entry) { - $published_item = $xpath->query('atom:published/text()', $entry)->item(0); - $updated_item = $xpath->query('atom:updated/text()' , $entry)->item(0); - $published = !empty($published_item->nodeValue) ? DateTimeFormat::utc($published_item->nodeValue) : null; - $updated = !empty($updated_item->nodeValue) ? DateTimeFormat::utc($updated_item->nodeValue) : null; - - if (empty($published) || empty($updated)) { - Logger::notice('Invalid entry for XPath.', ['entry' => $entry, 'url' => $data['url']]); - continue; - } - - if ($last_updated < $published) { - $last_updated = $published; - } - - if ($last_updated < $updated) { - $last_updated = $updated; - } - } - - if (empty($last_updated)) { - return; - } - - $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $last_updated]; - DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); - } - /** - * Updates the gcontact entry from a given public contact id - * - * @param integer $cid contact id - * @return void - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function updateFromPublicContactID($cid) - { - self::updateFromPublicContact(['id' => $cid]); - } - - /** - * Updates the gcontact entry from a given public contact url - * - * @param string $url contact url - * @return integer gcontact id - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function updateFromPublicContactURL($url) - { - return self::updateFromPublicContact(['nurl' => Strings::normaliseLink($url)]); - } - - /** - * Helper function for updateFromPublicContactID and updateFromPublicContactURL - * - * @param array $condition contact condition - * @return integer gcontact id - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - private static function updateFromPublicContact($condition) - { - $fields = ['name', 'nick', 'url', 'nurl', 'location', 'about', 'keywords', - 'bd', 'contact-type', 'network', 'addr', 'notify', 'alias', 'archive', 'term-date', - 'created', 'updated', 'avatar', 'success_update', 'failure_update', 'forum', 'prv', - 'baseurl', 'gsid', 'sensitive', 'unsearchable', 'failed']; - - $contact = DBA::selectFirst('contact', $fields, array_merge($condition, ['uid' => 0, 'network' => Protocol::FEDERATED])); - if (!DBA::isResult($contact)) { - return 0; - } - - $fields = ['name', 'nick', 'url', 'nurl', 'location', 'about', 'keywords', 'generation', - 'birthday', 'contact-type', 'network', 'addr', 'notify', 'alias', 'archived', 'archive_date', - 'created', 'updated', 'photo', 'last_contact', 'last_failure', 'community', 'connect', - 'server_url', 'gsid', 'nsfw', 'hide', 'id', 'failed']; - - $old_gcontact = DBA::selectFirst('gcontact', $fields, ['nurl' => $contact['nurl']]); - $do_insert = !DBA::isResult($old_gcontact); - if ($do_insert) { - $old_gcontact = []; - } - - $gcontact = []; - - // These fields are identical in both contact and gcontact - $fields = ['name', 'nick', 'url', 'nurl', 'location', 'about', 'keywords', 'gsid', - 'contact-type', 'network', 'addr', 'notify', 'alias', 'created', 'updated', 'failed']; - - foreach ($fields as $field) { - $gcontact[$field] = $contact[$field]; - } - - // These fields are having different names but the same content - $gcontact['server_url'] = $contact['baseurl'] ?? ''; // "baseurl" can be null, "server_url" not - $gcontact['nsfw'] = $contact['sensitive']; - $gcontact['hide'] = $contact['unsearchable']; - $gcontact['archived'] = $contact['archive']; - $gcontact['archive_date'] = $contact['term-date']; - $gcontact['birthday'] = $contact['bd']; - $gcontact['photo'] = $contact['avatar']; - $gcontact['last_contact'] = $contact['success_update']; - $gcontact['last_failure'] = $contact['failure_update']; - $gcontact['community'] = ($contact['forum'] || $contact['prv']); - - foreach (['last_contact', 'last_failure', 'updated'] as $field) { - if (!empty($old_gcontact[$field]) && ($old_gcontact[$field] >= $gcontact[$field])) { - unset($gcontact[$field]); - } - } - - if (!$gcontact['archived']) { - $gcontact['archive_date'] = DBA::NULL_DATETIME; - } - - if (!empty($old_gcontact['created']) && ($old_gcontact['created'] > DBA::NULL_DATETIME) - && ($old_gcontact['created'] <= $gcontact['created'])) { - unset($gcontact['created']); - } - - if (empty($gcontact['birthday']) && ($gcontact['birthday'] <= DBA::NULL_DATETIME)) { - unset($gcontact['birthday']); - } - - if (empty($old_gcontact['generation']) || ($old_gcontact['generation'] > 2)) { - $gcontact['generation'] = 2; // We fetched the data directly from the other server - } - - if (!$do_insert) { - DBA::update('gcontact', $gcontact, ['nurl' => $contact['nurl']], $old_gcontact); - return $old_gcontact['id']; - } elseif (!$gcontact['archived']) { - DBA::insert('gcontact', $gcontact); - return DBA::lastInsertId(); - } - } - - /** - * Updates the gcontact entry from probe - * - * @param string $url profile link - * @param boolean $force Optional forcing of network probing (otherwise we use the cached data) - * - * @return boolean 'true' when contact had been updated - * - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function updateFromProbe($url, $force = false) - { - $data = Probe::uri($url, $force); - - if (in_array($data['network'], [Protocol::PHANTOM])) { - $fields = ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()]; - DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($url)]); - Logger::info('Invalid network for contact', ['url' => $data['url'], 'callstack' => System::callstack()]); - return false; - } - - $data['server_url'] = $data['baseurl']; - $data['failed'] = false; - - self::update($data); - - // Set the date of the latest post - self::setLastUpdate($data, $force); - - return true; - } - - /** - * Update the gcontact entry for a given user id - * - * @param int $uid User ID - * @return bool - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function updateForUser($uid) - { - $profile = Profile::getByUID($uid); - if (empty($profile)) { - Logger::error('Cannot find profile', ['uid' => $uid]); - return false; - } - - $user = User::getOwnerDataById($uid); - if (empty($user)) { - Logger::error('Cannot find user', ['uid' => $uid]); - return false; - } - - $userdata = array_merge($profile, $user); - - $location = Profile::formatLocation( - ['locality' => $userdata['locality'], 'region' => $userdata['region'], 'country-name' => $userdata['country-name']] - ); - - $gcontact = ['name' => $userdata['name'], 'location' => $location, 'about' => $userdata['about'], - 'keywords' => $userdata['pub_keywords'], - 'birthday' => $userdata['dob'], 'photo' => $userdata['photo'], - "notify" => $userdata['notify'], 'url' => $userdata['url'], - "hide" => !$userdata['net-publish'], - 'nick' => $userdata['nickname'], 'addr' => $userdata['addr'], - "connect" => $userdata['addr'], "server_url" => DI::baseUrl(), - "generation" => 1, 'network' => Protocol::DFRN]; - - self::update($gcontact); - } - - /** - * Get the basepath for a given contact link - * - * @param string $url The gcontact link - * @param boolean $dont_update Don't update the contact - * - * @return string basepath - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function getBasepath($url, $dont_update = false) - { - $gcontact = DBA::selectFirst('gcontact', ['server_url'], ['nurl' => Strings::normaliseLink($url)]); - if (!empty($gcontact['server_url'])) { - return $gcontact['server_url']; - } elseif ($dont_update) { - return ''; - } - - self::updateFromProbe($url, true); - - // Fetch the result - $gcontact = DBA::selectFirst('gcontact', ['server_url'], ['nurl' => Strings::normaliseLink($url)]); - if (empty($gcontact['server_url'])) { - Logger::info('No baseurl for gcontact', ['url' => $url]); - return ''; - } - - Logger::info('Found baseurl for gcontact', ['url' => $url, 'baseurl' => $gcontact['server_url']]); - return $gcontact['server_url']; - } - - /** - * Fetches users of given GNU Social server - * - * If the "Statistics" addon is enabled (See http://gstools.org/ for details) we query user data with this. - * - * @param string $server Server address - * @return bool - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function fetchGsUsers($server) - { - Logger::info('Fetching users from GNU Social server', ['server' => $server]); - - $url = $server . '/main/statistics'; - - $curlResult = DI::httpRequest()->get($url); - if (!$curlResult->isSuccess()) { - return false; - } - - $statistics = json_decode($curlResult->getBody()); - - if (!empty($statistics->config->instance_address)) { - if (!empty($statistics->config->instance_with_ssl)) { - $server = 'https://'; - } else { - $server = 'http://'; - } - - $server .= $statistics->config->instance_address; - - $hostname = $statistics->config->instance_address; - } elseif (!empty($statistics->instance_address)) { - if (!empty($statistics->instance_with_ssl)) { - $server = 'https://'; - } else { - $server = 'http://'; - } - - $server .= $statistics->instance_address; - - $hostname = $statistics->instance_address; - } - - if (!empty($statistics->users)) { - foreach ($statistics->users as $nick => $user) { - $profile_url = $server . '/' . $user->nickname; - - $contact = ['url' => $profile_url, - 'name' => $user->fullname, - 'addr' => $user->nickname . '@' . $hostname, - 'nick' => $user->nickname, - "network" => Protocol::OSTATUS, - 'photo' => DI::baseUrl() . '/images/person-300.jpg']; - - if (isset($user->bio)) { - $contact['about'] = $user->bio; - } - - self::getId($contact); - } - } - } - - /** - * Asking GNU Social server on a regular base for their user data - * - * @return void - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function discoverGsUsers() - { - $requery_days = intval(DI::config()->get('system', 'poco_requery_days')); - - $last_update = date("c", time() - (60 * 60 * 24 * $requery_days)); - - $r = DBA::select('gserver', ['nurl', 'url'], [ - '`network` = ? - AND NOT `failed` - AND `last_poco_query` < ?', - Protocol::OSTATUS, - $last_update - ], [ - 'limit' => 5, - 'order' => ['RAND()'] - ]); - - if (!DBA::isResult($r)) { - return; - } - - foreach ($r as $server) { - self::fetchGsUsers($server['url']); - DBA::update('gserver', ['last_poco_query' => DateTimeFormat::utcNow()], ['nurl' => $server['nurl']]); - } - } - /** * Returns a random, global contact of the current node * diff --git a/src/Model/GServer.php b/src/Model/GServer.php index 07a5eaad3e..aa598a729a 100644 --- a/src/Model/GServer.php +++ b/src/Model/GServer.php @@ -32,7 +32,6 @@ use Friendica\DI; use Friendica\Module\Register; use Friendica\Network\CurlResult; use Friendica\Protocol\Diaspora; -use Friendica\Protocol\PortableContact; use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; use Friendica\Util\Strings; @@ -111,7 +110,10 @@ class GServer public static function reachable(string $profile, string $server = '', string $network = '', bool $force = false) { if ($server == '') { - $server = GContact::getBasepath($profile); + $contact = Contact::getByURL($profile, null, ['baseurl']); + if (!empty($contact['baseurl'])) { + $server = $contact['baseurl']; + } } if ($server == '') { @@ -471,10 +473,9 @@ class GServer } if (!empty($serverdata['network']) && !empty($id) && ($serverdata['network'] != Protocol::PHANTOM)) { - $gcontacts = DBA::count('gcontact', ['gsid' => $id]); $apcontacts = DBA::count('apcontact', ['gsid' => $id]); $contacts = DBA::count('contact', ['uid' => 0, 'gsid' => $id]); - $max_users = max($gcontacts, $apcontacts, $contacts, $registeredUsers); + $max_users = max($apcontacts, $contacts, $registeredUsers); if ($max_users > $registeredUsers) { Logger::info('Update registered users', ['id' => $id, 'url' => $serverdata['nurl'], 'registered-users' => $max_users]); DBA::update('gserver', ['registered-users' => $max_users], ['id' => $id]); @@ -959,12 +960,6 @@ class GServer { $contacts = []; - $gcontacts = DBA::select('gcontact', ['url', 'nurl'], ['server_url' => [$url, $serverdata['nurl']]]); - while ($gcontact = DBA::fetch($gcontacts)) { - $contacts[$gcontact['nurl']] = $gcontact['url']; - } - DBA::close($gcontacts); - $apcontacts = DBA::select('apcontact', ['url'], ['baseurl' => [$url, $serverdata['nurl']]]); while ($apcontact = DBA::fetch($apcontacts)) { $contacts[Strings::normaliseLink($apcontact['url'])] = $apcontact['url']; @@ -1556,20 +1551,6 @@ class GServer return !strpos($body, '>'); } - /** - * Update the user directory of a given gserver record - * - * @param array $gserver gserver record - */ - public static function updateDirectory(array $gserver) - { - /// @todo Add Mastodon API directory - - if (!empty($gserver['poco'])) { - PortableContact::discoverSingleServer($gserver['id']); - } - } - /** * Update GServer entries */ @@ -1588,7 +1569,7 @@ class GServer $last_update = date('c', time() - (60 * 60 * 24 * $requery_days)); - $gservers = DBA::p("SELECT `id`, `url`, `nurl`, `network`, `poco` + $gservers = DBA::p("SELECT `id`, `url`, `nurl`, `network`, `poco`, `directory-type` FROM `gserver` WHERE NOT `failed` AND `directory-type` != ? @@ -1672,4 +1653,18 @@ class GServer DI::config()->set('poco', 'last_federation_discovery', time()); } + + /** + * Returns a list of 1,000 active servers order by the last contact + * + * @return array List of server urls + * @throws Exception + */ + public static function getActive() + { + $result = DBA::p("SELECT `url`, `site_name` AS `displayName`, `network`, `platform`, `version` FROM `gserver` + WHERE `network` IN (?, ?, ?, ?) AND NOT `failed` ORDER BY `last_contact` LIMIT ?", + Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS, Protocol::ACTIVITYPUB, 1000); + return DBA::toArray($result); + } } diff --git a/src/Module/Admin/Federation.php b/src/Module/Admin/Federation.php index fd60b07152..d0d30e85e1 100644 --- a/src/Module/Admin/Federation.php +++ b/src/Module/Admin/Federation.php @@ -132,7 +132,6 @@ class Federation extends BaseAdmin // some helpful text $intro = DI::l10n()->t('This page offers you some numbers to the known part of the federated social network your Friendica node is part of. These numbers are not complete but only reflect the part of the network your node is aware of.'); - $hint = DI::l10n()->t('The Auto Discovered Contact Directory feature is not enabled, it will improve the data displayed here.'); // load the template, replace the macros and return the page content $t = Renderer::getMarkupTemplate('admin/federation.tpl'); @@ -140,8 +139,6 @@ class Federation extends BaseAdmin '$title' => DI::l10n()->t('Administration'), '$page' => DI::l10n()->t('Federation Statistics'), '$intro' => $intro, - '$hint' => $hint, - '$autoactive' => DI::config()->get('system', 'poco_completion'), '$counts' => $counts, '$version' => FRIENDICA_VERSION, '$legendtext' => DI::l10n()->t('Currently this node is aware of %d nodes with %d registered users from the following platforms:', $total, $users), diff --git a/src/Module/Admin/Site.php b/src/Module/Admin/Site.php index 3bdcd71147..7b298cd937 100644 --- a/src/Module/Admin/Site.php +++ b/src/Module/Admin/Site.php @@ -31,7 +31,6 @@ use Friendica\DI; use Friendica\Model\ContactRelation; use Friendica\Module\BaseAdmin; use Friendica\Module\Register; -use Friendica\Protocol\PortableContact; use Friendica\Util\BasePath; use Friendica\Util\EMailer\MailBuilder; use Friendica\Util\Strings; @@ -104,12 +103,10 @@ class Site extends BaseAdmin // update profile links in the format "http://server.tld" update_table($a, "profile", ['photo', 'thumb'], $old_url, $new_url); update_table($a, "contact", ['photo', 'thumb', 'micro', 'url', 'nurl', 'alias', 'request', 'notify', 'poll', 'confirm', 'poco', 'avatar'], $old_url, $new_url); - update_table($a, "gcontact", ['url', 'nurl', 'photo', 'server_url', 'notify', 'alias'], $old_url, $new_url); update_table($a, "item", ['owner-link', 'author-link', 'body', 'plink', 'tag'], $old_url, $new_url); // update profile addresses in the format "user@server.tld" update_table($a, "contact", ['addr'], $old_host, $new_host); - update_table($a, "gcontact", ['connect', 'addr'], $old_host, $new_host); // update config DI::config()->set('system', 'url', $new_url); @@ -180,10 +177,8 @@ class Site extends BaseAdmin $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); $synchronize_directory = (!empty($_POST['synchronize_directory']) ? intval(trim($_POST['synchronize_directory'])) : false); - $poco_completion = (!empty($_POST['poco_completion']) ? intval(trim($_POST['poco_completion'])) : false); $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); + $poco_discovery = (!empty($_POST['poco_discovery']) ? intval(trim($_POST['poco_discovery'])) : false); $poco_local_search = !empty($_POST['poco_local_search']); $nodeinfo = !empty($_POST['nodeinfo']); $dfrn_only = !empty($_POST['dfrn_only']); @@ -308,12 +303,10 @@ class Site extends BaseAdmin DI::config()->set('system', 'min_memory' , $min_memory); 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', 'contact_discovery' , $contact_discovery); DI::config()->set('system', 'synchronize_directory' , $synchronize_directory); 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); DI::config()->set('system', 'poco_local_search' , $poco_local_search); DI::config()->set('system', 'nodeinfo' , $nodeinfo); DI::config()->set('config', 'sitename' , $sitename); @@ -490,20 +483,6 @@ class Site extends BaseAdmin CP_USERS_AND_GLOBAL => DI::l10n()->t('Public postings from local users and the federated network') ]; - $poco_discovery_choices = [ - PortableContact::DISABLED => DI::l10n()->t('Disabled'), - PortableContact::USERS => DI::l10n()->t('Users'), - PortableContact::USERS_GCONTACTS => DI::l10n()->t('Users, Global Contacts'), - PortableContact::USERS_GCONTACTS_FALLBACK => DI::l10n()->t('Users, Global Contacts/fallback'), - ]; - - $poco_discovery_since_choices = [ - '30' => DI::l10n()->t('One month'), - '91' => DI::l10n()->t('Three months'), - '182' => DI::l10n()->t('Half a year'), - '365' => DI::l10n()->t('One year'), - ]; - /* get user names to make the install a personal install of X */ // @TODO Move to Model\User::getNames() $user_names = []; @@ -688,10 +667,8 @@ class Site extends BaseAdmin $discovery_choices], '$synchronize_directory' => ['synchronize_directory', DI::l10n()->t('Synchronize the contacts with the directory server'), DI::config()->get('system', 'synchronize_directory'), DI::l10n()->t('if enabled, the system will check periodically for new contacts on the defined directory server.')], - '$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.')], '$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], + '$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. The system queries Friendica, Mastodon and Hubzilla servers.')], '$poco_local_search' => ['poco_local_search', DI::l10n()->t('Search the local directory'), DI::config()->get('system', 'poco_local_search'), DI::l10n()->t('Search the local directory instead of the global directory. When searching locally, every search will be executed on the global directory in the background. This improves the search results when the search is repeated.')], '$nodeinfo' => ['nodeinfo', DI::l10n()->t('Publish server information'), DI::config()->get('system', 'nodeinfo'), DI::l10n()->t('If enabled, general server and usage data will be published. The data contains the name and version of the server, number of users with public profiles, number of posts and the activated protocols and connectors. See the-federation.info for details.')], diff --git a/src/Module/Contact.php b/src/Module/Contact.php index 40148c50d1..bf22470ed4 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -186,9 +186,6 @@ class Contact extends BaseModule // Update the entry in the contact table Model\Contact::updateFromProbe($contact_id, '', true); - - // Update the entry in the gcontact table - Model\GContact::updateFromProbe($contact['url']); } /** diff --git a/src/Module/NoScrape.php b/src/Module/NoScrape.php index 1457a1125f..4ad0e53069 100644 --- a/src/Module/NoScrape.php +++ b/src/Module/NoScrape.php @@ -26,14 +26,13 @@ use Friendica\Core\Protocol; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Model\GContact; use Friendica\Model\Profile; use Friendica\Model\User; /** * Endpoint for getting current user infos * - * @see GContact::updateFromNoScrape() for usage + * @see Contact::updateFromNoScrape() for usage */ class NoScrape extends BaseModule { diff --git a/src/Module/Settings/Profile/Index.php b/src/Module/Settings/Profile/Index.php index a7e02f4299..f4c902b829 100644 --- a/src/Module/Settings/Profile/Index.php +++ b/src/Module/Settings/Profile/Index.php @@ -31,7 +31,6 @@ use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Model\GContact; use Friendica\Model\Profile; use Friendica\Model\ProfileField; use Friendica\Model\User; @@ -151,9 +150,6 @@ class Index extends BaseSettings } Worker::add(PRIORITY_LOW, 'ProfileUpdate', local_user()); - - // Update the global contact for the user - GContact::updateForUser(local_user()); } public static function content(array $parameters = []) diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index f213af8db3..281220bc34 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -33,7 +33,6 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Conversation; use Friendica\Model\Event; -use Friendica\Model\GContact; use Friendica\Model\Item; use Friendica\Model\ItemURI; use Friendica\Model\Mail; @@ -1686,21 +1685,6 @@ class DFRN if (!empty($pcid)) { Contact::updateAvatar($pcid, $author['avatar']); } - - /* - * The generation is a sign for the reliability of the provided data. - * It is used in the socgraph.php to prevent that old contact data - * that was relayed over several servers can overwrite contact - * data that we received directly. - */ - - $poco["generation"] = 2; - $poco["photo"] = $author["avatar"]; - $poco["hide"] = $hide; - $poco["contact-type"] = $contact["contact-type"]; - $gcid = GContact::update($poco); - - GContact::link($gcid, $importer["importer_uid"], $contact["id"]); } return $author; @@ -1943,15 +1927,6 @@ class DFRN $old = $r[0]; - // Update the gcontact entry - $relocate["server_url"] = preg_replace("=(https?://)(.*)/profile/(.*)=ism", "$1$2", $relocate["url"]); - - $fields = ['name' => $relocate["name"], 'photo' => $relocate["avatar"], - 'url' => $relocate["url"], 'nurl' => Strings::normaliseLink($relocate["url"]), - 'addr' => $relocate["addr"], 'connect' => $relocate["addr"], - 'notify' => $relocate["notify"], 'server_url' => $relocate["server_url"]]; - DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($old["url"])]); - // Update the contact table. We try to find every entry. $fields = ['name' => $relocate["name"], 'avatar' => $relocate["avatar"], 'url' => $relocate["url"], 'nurl' => Strings::normaliseLink($relocate["url"]), diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index f3b95db68f..feb1111443 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -34,7 +34,6 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Conversation; -use Friendica\Model\GContact; use Friendica\Model\Item; use Friendica\Model\ItemURI; use Friendica\Model\Mail; @@ -1656,7 +1655,7 @@ class Diaspora // Update the profile self::receiveProfile($importer, $data->profile); - // change the technical stuff in contact and gcontact + // change the technical stuff in contact $data = Probe::uri($new_handle); if ($data['network'] == Protocol::PHANTOM) { Logger::log('Account for '.$new_handle." couldn't be probed."); @@ -1671,14 +1670,6 @@ class Diaspora DBA::update('contact', $fields, ['addr' => $old_handle]); - $fields = ['url' => $data['url'], 'nurl' => Strings::normaliseLink($data['url']), - 'name' => $data['name'], 'nick' => $data['nick'], - 'addr' => $data['addr'], 'connect' => $data['addr'], - 'notify' => $data['notify'], 'photo' => $data['photo'], - 'server_url' => $data['baseurl'], 'network' => $data['network']]; - - DBA::update('gcontact', $fields, ['addr' => $old_handle]); - Logger::log('Contacts are updated.'); return true; @@ -1702,8 +1693,6 @@ class Diaspora } DBA::close($contacts); - DBA::delete('gcontact', ['addr' => $author]); - Logger::log('Removed contacts for ' . $author); return true; @@ -2438,18 +2427,6 @@ class Diaspora DBA::update('contact', $fields, ['id' => $contact['id']]); - // @todo Update the public contact, then update the gcontact from that - - $gcontact = ["url" => $contact["url"], "network" => Protocol::DIASPORA, "generation" => 2, - "photo" => $image_url, "name" => $name, "location" => $location, - "about" => $about, "birthday" => $birthday, - "addr" => $author, "nick" => $nick, "keywords" => $keywords, - "hide" => !$searchable, "nsfw" => $nsfw]; - - $gcid = GContact::update($gcontact); - - GContact::link($gcid, $importer["uid"], $contact["id"]); - Logger::log("Profile of contact ".$contact["id"]." stored for user ".$importer["uid"], Logger::DEBUG); return true; diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 1cf2894eb5..d9e18fa613 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -34,7 +34,6 @@ use Friendica\DI; use Friendica\Model\APContact; use Friendica\Model\Contact; use Friendica\Model\Conversation; -use Friendica\Model\GContact; use Friendica\Model\Item; use Friendica\Model\ItemURI; use Friendica\Model\Tag; @@ -240,15 +239,6 @@ class OStatus Contact::updateAvatar($cid, $author["author-avatar"]); } } - - $contact["generation"] = 2; - $contact["hide"] = false; // OStatus contacts are never hidden - if (!empty($author["author-avatar"])) { - $contact["photo"] = $author["author-avatar"]; - } - $gcid = GContact::update($contact); - - GContact::link($gcid, $contact["uid"], $contact["id"]); } elseif (empty($contact["network"]) || ($contact["network"] != Protocol::DFRN)) { $contact = []; } diff --git a/src/Protocol/PortableContact.php b/src/Protocol/PortableContact.php deleted file mode 100644 index 8109ef2373..0000000000 --- a/src/Protocol/PortableContact.php +++ /dev/null @@ -1,486 +0,0 @@ -. - * - */ - -namespace Friendica\Protocol; - -use Exception; -use Friendica\Content\Text\HTML; -use Friendica\Core\Logger; -use Friendica\Core\Protocol; -use Friendica\Core\Worker; -use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Model\GContact; -use Friendica\Model\GServer; -use Friendica\Util\DateTimeFormat; -use Friendica\Util\Strings; - -/** - * - * @todo Move GNU Social URL schemata (http://server.tld/user/number) to http://server.tld/username - * @todo Fetch profile data from profile page for Redmatrix users - * @todo Detect if it is a forum - */ -class PortableContact -{ - const DISABLED = 0; - const USERS = 1; - const USERS_GCONTACTS = 2; - const USERS_GCONTACTS_FALLBACK = 3; - - /** - * Fetch POCO data - * - * @param integer $cid Contact ID - * @param integer $uid User ID - * @param integer $zcid Global Contact ID - * @param integer $url POCO address that should be polled - * - * Given a contact-id (minimum), load the PortableContacts friend list for that contact, - * and add the entries to the gcontact (Global Contact) table, or update existing entries - * if anything (name or photo) has changed. - * We use normalised urls for comparison which ignore http vs https and www.domain vs domain - * - * Once the global contact is stored add (if necessary) the contact linkage which associates - * the given uid, cid to the global contact entry. There can be many uid/cid combinations - * pointing to the same global contact id. - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public static function loadWorker($cid, $uid = 0, $zcid = 0, $url = null) - { - // Call the function "load" via the worker - Worker::add(PRIORITY_LOW, 'FetchPoCo', (int)$cid, (int)$uid, (int)$zcid, $url); - } - - /** - * Fetch POCO data from the worker - * - * @param integer $cid Contact ID - * @param integer $uid User ID - * @param integer $zcid Global Contact ID - * @param integer $url POCO address that should be polled - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public static function load($cid, $uid, $zcid, $url) - { - if ($cid) { - if (!$url || !$uid) { - $contact = DBA::selectFirst('contact', ['poco', 'uid'], ['id' => $cid]); - if (DBA::isResult($contact)) { - $url = $contact['poco']; - $uid = $contact['uid']; - } - } - if (!$uid) { - return; - } - } - - if (!$url) { - return; - } - - $url = $url . (($uid) ? '/@me/@all?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,contactType,generation' : '?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,contactType,generation'); - - Logger::log('load: ' . $url, Logger::DEBUG); - - $fetchresult = DI::httpRequest()->fetchFull($url); - $s = $fetchresult->getBody(); - - Logger::log('load: returns ' . $s, Logger::DATA); - - Logger::log('load: return code: ' . $fetchresult->getReturnCode(), Logger::DEBUG); - - if (($fetchresult->getReturnCode() > 299) || (! $s)) { - return; - } - - $j = json_decode($s, true); - - Logger::debug('load', ['json' => $j]); - - if (!isset($j['entry'])) { - return; - } - - $total = 0; - foreach ($j['entry'] as $entry) { - $total ++; - $profile_url = ''; - $profile_photo = ''; - $connect_url = ''; - $name = ''; - $network = ''; - $updated = DBA::NULL_DATETIME; - $location = ''; - $about = ''; - $keywords = ''; - $contact_type = -1; - $generation = 0; - - if (!empty($entry['displayName'])) { - $name = $entry['displayName']; - } - - if (isset($entry['urls'])) { - foreach ($entry['urls'] as $url) { - if ($url['type'] == 'profile') { - $profile_url = $url['value']; - continue; - } - if ($url['type'] == 'webfinger') { - $connect_url = str_replace('acct:', '', $url['value']); - continue; - } - } - } - if (isset($entry['photos'])) { - foreach ($entry['photos'] as $photo) { - if ($photo['type'] == 'profile') { - $profile_photo = $photo['value']; - continue; - } - } - } - - if (isset($entry['updated'])) { - $updated = date(DateTimeFormat::MYSQL, strtotime($entry['updated'])); - } - - if (isset($entry['network'])) { - $network = $entry['network']; - } - - if (isset($entry['currentLocation'])) { - $location = $entry['currentLocation']; - } - - if (isset($entry['aboutMe'])) { - $about = HTML::toBBCode($entry['aboutMe']); - } - - if (isset($entry['generation']) && ($entry['generation'] > 0)) { - $generation = ++$entry['generation']; - } - - if (isset($entry['tags'])) { - foreach ($entry['tags'] as $tag) { - $keywords = implode(", ", $tag); - } - } - - if (isset($entry['contactType']) && ($entry['contactType'] >= 0)) { - $contact_type = $entry['contactType']; - } - - $gcontact = ["url" => $profile_url, - "name" => $name, - "network" => $network, - "photo" => $profile_photo, - "about" => $about, - "location" => $location, - "keywords" => $keywords, - "connect" => $connect_url, - "updated" => $updated, - "contact-type" => $contact_type, - "generation" => $generation]; - - try { - $gcontact = GContact::sanitize($gcontact); - $gcid = GContact::update($gcontact); - - GContact::link($gcid, $uid, $cid, $zcid); - } catch (Exception $e) { - Logger::log($e->getMessage(), Logger::DEBUG); - } - } - Logger::log("load: loaded $total entries", Logger::DEBUG); - - $condition = ["`cid` = ? AND `uid` = ? AND `zcid` = ? AND `updated` < UTC_TIMESTAMP - INTERVAL 2 DAY", $cid, $uid, $zcid]; - DBA::delete('glink', $condition); - } - - /** - * Returns a list of all known servers - * @return array List of server urls - * @throws Exception - */ - public static function serverlist() - { - $r = q( - "SELECT `url`, `site_name` AS `displayName`, `network`, `platform`, `version` FROM `gserver` - WHERE `network` IN ('%s', '%s', '%s') AND NOT `failed` - ORDER BY `last_contact` - LIMIT 1000", - DBA::escape(Protocol::DFRN), - DBA::escape(Protocol::DIASPORA), - DBA::escape(Protocol::OSTATUS) - ); - - if (!DBA::isResult($r)) { - return false; - } - - return $r; - } - - /** - * Fetch server list from remote servers and adds them when they are new. - * - * @param string $poco URL to the POCO endpoint - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - private static function fetchServerlist($poco) - { - $curlResult = DI::httpRequest()->get($poco . "/@server"); - - if (!$curlResult->isSuccess()) { - return; - } - - $serverlist = json_decode($curlResult->getBody(), true); - - if (!is_array($serverlist)) { - return; - } - - foreach ($serverlist as $server) { - $server_url = str_replace("/index.php", "", $server['url']); - - $r = q("SELECT `nurl` FROM `gserver` WHERE `nurl` = '%s'", DBA::escape(Strings::normaliseLink($server_url))); - - if (!DBA::isResult($r)) { - Logger::log("Call server check for server ".$server_url, Logger::DEBUG); - Worker::add(PRIORITY_LOW, 'UpdateGServer', $server_url); - } - } - } - - public static function discoverSingleServer($id) - { - $server = DBA::selectFirst('gserver', ['poco', 'nurl', 'url', 'network'], ['id' => $id]); - - if (!DBA::isResult($server)) { - return false; - } - - // Discover new servers out there (Works from Friendica version 3.5.2) - self::fetchServerlist($server["poco"]); - - // Fetch all users from the other server - $url = $server["poco"] . "/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,contactType,generation"; - - Logger::info("Fetch all users from the server " . $server["url"]); - - $curlResult = DI::httpRequest()->get($url); - - if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { - $data = json_decode($curlResult->getBody(), true); - - if (!empty($data)) { - self::discoverServer($data, 2); - } - - if (DI::config()->get('system', 'poco_discovery') >= self::USERS_GCONTACTS) { - $timeframe = DI::config()->get('system', 'poco_discovery_since'); - - if ($timeframe == 0) { - $timeframe = 30; - } - - $updatedSince = date(DateTimeFormat::MYSQL, time() - $timeframe * 86400); - - // Fetch all global contacts from the other server (Not working with Redmatrix and Friendica versions before 3.3) - $url = $server["poco"]."/@global?updatedSince=".$updatedSince."&fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,contactType,generation"; - - $success = false; - - $curlResult = DI::httpRequest()->get($url); - - if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { - Logger::info("Fetch all global contacts from the server " . $server["nurl"]); - $data = json_decode($curlResult->getBody(), true); - - if (!empty($data)) { - $success = self::discoverServer($data); - } - } - - if (!$success && !empty($data) && DI::config()->get('system', 'poco_discovery') >= self::USERS_GCONTACTS_FALLBACK) { - Logger::info("Fetch contacts from users of the server " . $server["nurl"]); - self::discoverServerUsers($data, $server); - } - } - - return true; - } else { - // If the server hadn't replied correctly, then force a sanity check - GServer::check($server["url"], $server["network"], true); - - return false; - } - } - - private static function discoverServerUsers(array $data, array $server) - { - if (!isset($data['entry'])) { - return; - } - - foreach ($data['entry'] as $entry) { - $username = ''; - - if (isset($entry['urls'])) { - foreach ($entry['urls'] as $url) { - if ($url['type'] == 'profile') { - $profile_url = $url['value']; - $path_array = explode('/', parse_url($profile_url, PHP_URL_PATH)); - $username = end($path_array); - } - } - } - - if ($username != '') { - Logger::log('Fetch contacts for the user ' . $username . ' from the server ' . $server['nurl'], Logger::DEBUG); - - // 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()->get($url); - - if ($curlResult->isSuccess()) { - $data = json_decode($curlResult->getBody(), true); - - if (!empty($data)) { - self::discoverServer($data, 3); - } - } - } - } - } - - private static function discoverServer(array $data, $default_generation = 0) - { - if (empty($data['entry'])) { - return false; - } - - $success = false; - - foreach ($data['entry'] as $entry) { - $profile_url = ''; - $profile_photo = ''; - $connect_url = ''; - $name = ''; - $network = ''; - $updated = DBA::NULL_DATETIME; - $location = ''; - $about = ''; - $keywords = ''; - $contact_type = -1; - $generation = $default_generation; - - if (!empty($entry['displayName'])) { - $name = $entry['displayName']; - } - - if (isset($entry['urls'])) { - foreach ($entry['urls'] as $url) { - if ($url['type'] == 'profile') { - $profile_url = $url['value']; - continue; - } - if ($url['type'] == 'webfinger') { - $connect_url = str_replace('acct:' , '', $url['value']); - continue; - } - } - } - - if (isset($entry['photos'])) { - foreach ($entry['photos'] as $photo) { - if ($photo['type'] == 'profile') { - $profile_photo = $photo['value']; - continue; - } - } - } - - if (isset($entry['updated'])) { - $updated = date(DateTimeFormat::MYSQL, strtotime($entry['updated'])); - } - - if (isset($entry['network'])) { - $network = $entry['network']; - } - - if (isset($entry['currentLocation'])) { - $location = $entry['currentLocation']; - } - - if (isset($entry['aboutMe'])) { - $about = HTML::toBBCode($entry['aboutMe']); - } - - if (isset($entry['generation']) && ($entry['generation'] > 0)) { - $generation = ++$entry['generation']; - } - - if (isset($entry['contactType']) && ($entry['contactType'] >= 0)) { - $contact_type = $entry['contactType']; - } - - if (isset($entry['tags'])) { - foreach ($entry['tags'] as $tag) { - $keywords = implode(", ", $tag); - } - } - - if ($generation > 0) { - $success = true; - - Logger::log("Store profile ".$profile_url, Logger::DEBUG); - - $gcontact = ["url" => $profile_url, - "name" => $name, - "network" => $network, - "photo" => $profile_photo, - "about" => $about, - "location" => $location, - "keywords" => $keywords, - "connect" => $connect_url, - "updated" => $updated, - "contact-type" => $contact_type, - "generation" => $generation]; - - try { - $gcontact = GContact::sanitize($gcontact); - GContact::update($gcontact); - } catch (Exception $e) { - Logger::log($e->getMessage(), Logger::DEBUG); - } - - Logger::log("Done for profile ".$profile_url, Logger::DEBUG); - } - } - return $success; - } -} diff --git a/src/Worker/Cron.php b/src/Worker/Cron.php index 5bdd22f330..4a49103e09 100644 --- a/src/Worker/Cron.php +++ b/src/Worker/Cron.php @@ -57,9 +57,6 @@ class Cron // run the process to update server directories in the background Worker::add(PRIORITY_LOW, 'UpdateServerDirectories'); - // run the process to update locally stored global contacts in the background - Worker::add(PRIORITY_LOW, 'UpdateGContacts'); - // Expire and remove user entries Worker::add(PRIORITY_MEDIUM, "CronJobs", "expire_and_remove_users"); @@ -88,8 +85,6 @@ class Cron Worker::add(PRIORITY_LOW, 'UpdateGServers'); - Worker::add(PRIORITY_LOW, 'UpdateSuggestions'); - Worker::add(PRIORITY_LOW, 'Expire'); Worker::add(PRIORITY_MEDIUM, 'DBClean'); diff --git a/src/Worker/CronJobs.php b/src/Worker/CronJobs.php index 4f988b6e14..5b5c01acab 100644 --- a/src/Worker/CronJobs.php +++ b/src/Worker/CronJobs.php @@ -29,7 +29,6 @@ use Friendica\Database\DBA; use Friendica\Database\PostUpdate; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Model\GContact; use Friendica\Model\Nodeinfo; use Friendica\Model\Photo; use Friendica\Model\User; @@ -258,14 +257,6 @@ class CronJobs // There was an issue where the nick vanishes from the contact table q("UPDATE `contact` INNER JOIN `user` ON `contact`.`uid` = `user`.`uid` SET `nick` = `nickname` WHERE `self` AND `nick`=''"); - // Update the global contacts for local users - $r = q("SELECT `uid` FROM `user` WHERE `verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired`"); - if (DBA::isResult($r)) { - foreach ($r AS $user) { - GContact::updateForUser($user["uid"]); - } - } - /// @todo /// - remove thread entries without item /// - remove sign entries without item diff --git a/src/Worker/FetchPoCo.php b/src/Worker/FetchPoCo.php deleted file mode 100644 index 477d001d29..0000000000 --- a/src/Worker/FetchPoCo.php +++ /dev/null @@ -1,41 +0,0 @@ -. - * - */ - -namespace Friendica\Worker; - -use Friendica\Core\Logger; -use Friendica\Protocol\PortableContact; - -class FetchPoCo -{ - /** - * Fetch PortableContacts from a given PoCo server address - * - * @param integer $cid Contact ID - * @param integer $uid User ID - * @param integer $zcid Global Contact ID - * @param integer $url PoCo address that should be polled - */ - public static function execute($cid, $uid, $zcid, $url) - { - PortableContact::load($cid, $uid, $zcid, $url); - } -} diff --git a/src/Worker/OnePoll.php b/src/Worker/OnePoll.php index fbd1ab4e59..40681175d4 100644 --- a/src/Worker/OnePoll.php +++ b/src/Worker/OnePoll.php @@ -32,7 +32,6 @@ 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\Strings; use Friendica\Util\XML; @@ -95,13 +94,6 @@ class OnePoll $contact = DBA::selectFirst('contact', [], ['id' => $contact_id]); } - // load current friends if possible. - if (!empty($contact['poco']) && !$contact['failed']) { - if (!DBA::exists('glink', ["`cid` = ? AND updated > UTC_TIMESTAMP() - INTERVAL 1 DAY", $contact['id']])) { - PortableContact::loadWorker($contact['id'], $importer_uid, 0, $contact['poco']); - } - } - // Don't poll if polling is deactivated (But we poll feeds and mails anyway) if (!in_array($protocol, [Protocol::FEED, Protocol::MAIL]) && DI::config()->get('system', 'disable_polling')) { Logger::log('Polling is disabled'); diff --git a/src/Worker/PullDirectory.php b/src/Worker/PullDirectory.php index a83b0a13be..250496009d 100644 --- a/src/Worker/PullDirectory.php +++ b/src/Worker/PullDirectory.php @@ -60,22 +60,11 @@ class PullDirectory return; } + $result = Contact::addContactsByArray($contacts['results']); + $now = $contacts['now'] ?? 0; - $count = $contacts['count'] ?? 0; - $added = 0; - $updated = 0; - foreach ($contacts['results'] as $url) { - $contact = Contact::getByURL($url, false, ['id']); - if (empty($contact['id'])) { - Worker::add(PRIORITY_LOW, 'AddContact', 0, $url); - ++$added; - } else { - Worker::add(PRIORITY_LOW, "UpdateContact", $contact['id']); - ++$updated; - } - } DI::config()->set('system', 'last-directory-sync', $now); - Logger::info('Synchronization ended.', ['now' => $now, 'count' => $count, 'added' => $added, 'updated' => $updated, 'directory' => $directory]); + Logger::info('Synchronization ended', ['now' => $now, 'count' => $result['count'], 'added' => $result['added'], 'updated' => $result['updated'], 'directory' => $directory]); } } diff --git a/src/Worker/SearchDirectory.php b/src/Worker/SearchDirectory.php index 546c369b2c..3bb5f1b8b2 100644 --- a/src/Worker/SearchDirectory.php +++ b/src/Worker/SearchDirectory.php @@ -23,14 +23,9 @@ namespace Friendica\Worker; use Friendica\Core\Cache\Duration; use Friendica\Core\Logger; -use Friendica\Core\Protocol; use Friendica\Core\Search; -use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Model\GContact; -use Friendica\Model\GServer; -use Friendica\Util\Strings; class SearchDirectory { @@ -56,43 +51,7 @@ class SearchDirectory if (!empty($j->results)) { foreach ($j->results as $jj) { - // Check if the contact already exists - $gcontact = DBA::selectFirst('gcontact', ['failed'], ['nurl' => Strings::normaliseLink($jj->url)]); - if (DBA::isResult($gcontact)) { - Logger::info('Profile already exists', ['profile' => $jj->url, 'search' => $search]); - - if ($gcontact['failed']) { - continue; - } - - // Update the contact - GContact::updateFromProbe($jj->url); - continue; - } - - $server_url = GContact::getBasepath($jj->url, true); - if ($server_url != '') { - if (!GServer::check($server_url)) { - Logger::info("Friendica server doesn't answer.", ['server' => $server_url]); - continue; - } - Logger::info('Friendica server seems to be okay.', ['server' => $server_url]); - } - - $data = Contact::getByURL($jj->url); - if ($data['network'] == Protocol::DFRN) { - Logger::info('Add profile to local directory', ['profile' => $jj->url]); - - if ($jj->tags != '') { - $data['keywords'] = $jj->tags; - } - - $data['server_url'] = $data['baseurl']; - - GContact::update($data); - } else { - Logger::info('Profile is not responding or no Friendica contact', ['profile' => $jj->url, 'network' => $data['network']]); - } + Contact::getByURL($jj->url); } } DI::cache()->set('SearchDirectory:' . $search, time(), Duration::DAY); diff --git a/src/Worker/UpdateGContact.php b/src/Worker/UpdateGContact.php deleted file mode 100644 index 3f71241fa5..0000000000 --- a/src/Worker/UpdateGContact.php +++ /dev/null @@ -1,44 +0,0 @@ -. - * - */ - -namespace Friendica\Worker; - -use Friendica\Core\Logger; -use Friendica\DI; -use Friendica\Model\GContact; - -class UpdateGContact -{ - /** - * Update global contact via probe - * @param string $url Global contact url - * @param string $command - */ - public static function execute(string $url, string $command = '') - { - $force = ($command == "force"); - $nodiscover = ($command == "nodiscover"); - - $success = GContact::updateFromProbe($url, $force); - - Logger::info('Updated from probe', ['url' => $url, 'force' => $force, 'success' => $success]); - } -} diff --git a/src/Worker/UpdateGContacts.php b/src/Worker/UpdateGContacts.php deleted file mode 100644 index 9d9519241b..0000000000 --- a/src/Worker/UpdateGContacts.php +++ /dev/null @@ -1,101 +0,0 @@ -. - * - */ - -namespace Friendica\Worker; - -use Friendica\Core\Logger; -use Friendica\Core\Protocol; -use Friendica\Core\Worker; -use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Model\GContact; -use Friendica\Model\GServer; -use Friendica\Util\DateTimeFormat; -use Friendica\Util\Strings; - -class UpdateGContacts -{ - /** - * Updates global contacts - */ - public static function execute() - { - if (!DI::config()->get('system', 'poco_completion')) { - return; - } - - Logger::info('Update global contacts'); - - $starttime = time(); - - $contacts = DBA::p("SELECT `url`, `created`, `updated`, `last_failure`, `last_contact`, `server_url`, `network` FROM `gcontact` - WHERE `last_contact` < UTC_TIMESTAMP - INTERVAL 1 MONTH AND - `last_failure` < UTC_TIMESTAMP - INTERVAL 1 MONTH AND - `network` IN (?, ?, ?, ?, ?, '') ORDER BY rand()", - Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS, Protocol::FEED); - - $checked = 0; - - while ($contact = DBA::fetch($contacts)) { - $urlparts = parse_url($contact['url']); - if (empty($urlparts['scheme'])) { - DBA::update('gcontact', ['network' => Protocol::PHANTOM], - ['nurl' => Strings::normaliseLink($contact['url'])]); - continue; - } - - if (in_array($urlparts['host'], ['twitter.com', 'identi.ca'])) { - $networks = ['twitter.com' => Protocol::TWITTER, 'identi.ca' => Protocol::PUMPIO]; - - DBA::update('gcontact', ['network' => $networks[$urlparts['host']]], - ['nurl' => Strings::normaliseLink($contact['url'])]); - continue; - } - - $server_url = GContact::getBasepath($contact['url'], true); - $force_update = false; - - if (!empty($contact['server_url'])) { - $force_update = (Strings::normaliseLink($contact['server_url']) != Strings::normaliseLink($server_url)); - - $server_url = $contact['server_url']; - } - - if ((empty($server_url) && ($contact['network'] == Protocol::FEED)) || $force_update || GServer::check($server_url, $contact['network'])) { - Logger::info('Check profile', ['profile' => $contact['url']]); - Worker::add(PRIORITY_LOW, 'UpdateGContact', $contact['url'], 'force'); - - if (++$checked > 100) { - return; - } - } else { - DBA::update('gcontact', ['last_failure' => DateTimeFormat::utcNow()], - ['nurl' => Strings::normaliseLink($contact['url'])]); - } - - // Quit the loop after 3 minutes - if (time() > ($starttime + 180)) { - return; - } - } - DBA::close($contacts); - } -} diff --git a/src/Worker/UpdateServerDirectories.php b/src/Worker/UpdateServerDirectories.php index 74f75fcd76..ba5ef1017e 100644 --- a/src/Worker/UpdateServerDirectories.php +++ b/src/Worker/UpdateServerDirectories.php @@ -22,9 +22,7 @@ namespace Friendica\Worker; use Friendica\DI; -use Friendica\Model\GContact; use Friendica\Model\GServer; -use Friendica\Protocol\PortableContact; class UpdateServerDirectories { @@ -33,16 +31,10 @@ class UpdateServerDirectories */ public static function execute() { - if (DI::config()->get('system', 'poco_discovery') == PortableContact::DISABLED) { + if (!DI::config()->get('system', 'poco_discovery')) { return; } - // Query Friendica and Hubzilla servers for their users GServer::discover(); - - // Query GNU Social servers for their users ("statistics" addon has to be enabled on the GS server) - if (!DI::config()->get('system', 'ostatus_disabled')) { - GContact::discoverGsUsers(); - } } } diff --git a/src/Worker/UpdateServerDirectory.php b/src/Worker/UpdateServerDirectory.php index 87bbee7e8c..2066ce7cb9 100644 --- a/src/Worker/UpdateServerDirectory.php +++ b/src/Worker/UpdateServerDirectory.php @@ -22,17 +22,86 @@ namespace Friendica\Worker; use Friendica\Core\Logger; +use Friendica\Database\DBA; +use Friendica\DI; +use Friendica\Model\Contact; use Friendica\Model\GServer; class UpdateServerDirectory { /** * Query the given server for their users - * @param string $gserver Server URL + * + * @param array $gserver Server record */ - public static function execute($gserver) + public static function execute(array $gserver) { - GServer::updateDirectory($gserver); - return; + $gserver = DBA::selectFirst('gserver', [], ['url' => $gserver['url']]); + if ($gserver['directory-type'] == GServer::DT_MASTODON) { + self::discoverMastodonDirectory($gserver); + } elseif (!empty($gserver['poco'])) { + self::discoverPoCo($gserver); + } + } + + private static function discoverPoCo(array $gserver) + { + $result = DI::httpRequest()->fetch($gserver['poco'] . '?fields=urls'); + if (empty($result)) { + Logger::info('Empty result', ['url' => $gserver['url']]); + return; + } + + $contacts = json_decode($result, true); + if (empty($contacts['entry'])) { + Logger::info('No contacts', ['url' => $gserver['url']]); + return; + } + + Logger::info('PoCo discovery started', ['poco' => $gserver['poco']]); + + $urls = []; + foreach ($contacts['entry'] as $entry) { + foreach ($entry['urls'] as $url_entry) { + if (empty($url_entry['type']) || empty($url_entry['value'])) { + continue; + } + if ($url_entry['type'] == 'profile') { + $urls[] = $url_entry['value']; + } + } + } + + $result = Contact::addContactsByArray($urls); + + Logger::info('PoCo discovery ended', ['count' => $result['count'], 'added' => $result['added'], 'updated' => $result['updated'], 'poco' => $gserver['poco']]); + } + + private static function discoverMastodonDirectory(array $gserver) + { + $result = DI::httpRequest()->fetch($gserver['url'] . '/api/v1/directory?order=new&local=true&limit=200&offset=0'); + if (empty($result)) { + Logger::info('Empty result', ['url' => $gserver['url']]); + return; + } + + $accounts = json_decode($result, true); + if (empty($accounts)) { + Logger::info('No contacts', ['url' => $gserver['url']]); + return; + } + + Logger::info('Account discovery started', ['url' => $gserver['url']]); + + $urls = []; + foreach ($accounts as $account) { + if (!empty($account['url'])) { + $urls[] = $account['url']; + } + } + + $result = Contact::addContactsByArray($urls); + + Logger::info('Account discovery ended', ['count' => $result['count'], 'added' => $result['added'], 'updated' => $result['updated'], 'url' => $gserver['url']]); } } diff --git a/src/Worker/UpdateServerPeers.php b/src/Worker/UpdateServerPeers.php index ff0cdfa730..ca45344459 100644 --- a/src/Worker/UpdateServerPeers.php +++ b/src/Worker/UpdateServerPeers.php @@ -63,4 +63,41 @@ class UpdateServerPeers } Logger::info('Server peer update ended', ['total' => $total, 'added' => $added, 'url' => $url]); } + + /** + * Fetch server list from remote servers and adds them when they are new. + * + * @param string $poco URL to the POCO endpoint + */ + private static function fetchServerlist($poco) + { + $curlResult = DI::httpRequest()->get($poco . '/@server'); + if (!$curlResult->isSuccess()) { + Logger::info('Server is not reachable or does not offer the "poco" endpoint', ['poco' => $poco]); + return; + } + + $serverlist = json_decode($curlResult->getBody(), true); + if (!is_array($serverlist)) { + Logger::info('Server does not have any servers listed', ['poco' => $poco]); + return; + } + + Logger::info('PoCo Server update start', ['poco' => $poco]); + + $total = 0; + $added = 0; + foreach ($serverlist as $server) { + ++$total; + if (DBA::exists('gserver', ['nurl' => Strings::normaliseLink($server['url'])])) { + // We already know this server + continue; + } + // This endpoint doesn't offer the schema. So we assume that it is HTTPS. + Worker::add(PRIORITY_LOW, 'UpdateGServer', $server['url']); + ++$added; + } + + Logger::info('PoCo Server update ended', ['total' => $total, 'added' => $added, 'poco' => $poco]); + } } diff --git a/src/Worker/UpdateSuggestions.php b/src/Worker/UpdateSuggestions.php deleted file mode 100644 index 103a3cf4ca..0000000000 --- a/src/Worker/UpdateSuggestions.php +++ /dev/null @@ -1,36 +0,0 @@ -. - * - */ - -namespace Friendica\Worker; - -use Friendica\Core\Logger; -use Friendica\Model\GContact; - -class UpdateSuggestions -{ - /** - * Discover other servers for their contacts. - */ - public static function execute() - { - GContact::updateSuggestions(); - } -} diff --git a/update.php b/update.php index a4d71bc4fd..f335b5292e 100644 --- a/update.php +++ b/update.php @@ -48,7 +48,6 @@ use Friendica\Database\DBA; use Friendica\Database\DBStructure; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Model\GContact; use Friendica\Model\Item; use Friendica\Model\User; use Friendica\Model\Storage; @@ -315,7 +314,6 @@ function update_1298() 'was' => $data[$translateKey]]); Worker::add(PRIORITY_LOW, 'ProfileUpdate', $data['id']); Contact::updateSelfFromUserID($data['id']); - GContact::updateForUser($data['id']); $success++; } } @@ -562,16 +560,5 @@ function update_1357() return Update::FAILED; } - if (!DBA::e("UPDATE `gcontact` SET `failed` = true WHERE `last_contact` < `last_failure` AND `failed` IS NULL")) { - return Update::FAILED; - } - - if (!DBA::e("UPDATE `gcontact` SET `failed` = false WHERE `last_contact` > `last_failure` AND `failed` IS NULL")) { - return Update::FAILED; - } - - if (!DBA::e("UPDATE `gcontact` SET `failed` = false WHERE `updated` > `last_failure` AND `failed` IS NULL")) { - return Update::FAILED; - } return Update::SUCCESS; } diff --git a/view/templates/admin/federation.tpl b/view/templates/admin/federation.tpl index 177d5406f6..37e3cb847a 100644 --- a/view/templates/admin/federation.tpl +++ b/view/templates/admin/federation.tpl @@ -5,10 +5,6 @@

    {{$intro}}

    - {{if not $autoactive}} -

    {{$hint nofilter}}

    - {{/if}} -

    {{$legendtext}}

      diff --git a/view/templates/admin/site.tpl b/view/templates/admin/site.tpl index 57e574725d..ab97d8218d 100644 --- a/view/templates/admin/site.tpl +++ b/view/templates/admin/site.tpl @@ -99,10 +99,8 @@

      {{$portable_contacts}}

      {{include file="field_select.tpl" field=$contact_discovery}} {{include file="field_checkbox.tpl" field=$synchronize_directory}} - {{include file="field_checkbox.tpl" field=$poco_completion}} {{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}} + {{include file="field_checkbox.tpl" field=$poco_discovery}} {{include file="field_checkbox.tpl" field=$poco_local_search}}
      diff --git a/view/theme/frio/templates/admin/site.tpl b/view/theme/frio/templates/admin/site.tpl index 61a9d1e500..c5a24ffd34 100644 --- a/view/theme/frio/templates/admin/site.tpl +++ b/view/theme/frio/templates/admin/site.tpl @@ -220,10 +220,8 @@
      {{include file="field_select.tpl" field=$contact_discovery}} {{include file="field_checkbox.tpl" field=$synchronize_directory}} - {{include file="field_checkbox.tpl" field=$poco_completion}} + {{include file="field_checkbox.tpl" field=$poco_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}} {{include file="field_checkbox.tpl" field=$poco_local_search}}
    {{if $contacts}}
      From 018abb4d1dfbe0bf29b441dce281d89a437075b4 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 5 Aug 2020 03:36:37 +0000 Subject: [PATCH 181/573] Renamed function --- src/Model/Contact/Group.php | 2 +- src/Module/Group.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Model/Contact/Group.php b/src/Model/Contact/Group.php index bb05d3cc3f..b2d165f6a7 100644 --- a/src/Model/Contact/Group.php +++ b/src/Model/Contact/Group.php @@ -73,7 +73,7 @@ class Group * @return array * @throws \Exception */ - public static function getUngrouped(int $uid) + public static function listUngrouped(int $uid) { return q("SELECT * FROM `contact` diff --git a/src/Module/Group.php b/src/Module/Group.php index b7fffd4f11..d9caac32da 100644 --- a/src/Module/Group.php +++ b/src/Module/Group.php @@ -316,7 +316,7 @@ class Group extends BaseModule } if ($nogroup) { - $contacts = Model\Contact\Group::getUngrouped(local_user()); + $contacts = Model\Contact\Group::listUngrouped(local_user()); } else { $contacts_stmt = DBA::select('contact', [], ['uid' => local_user(), 'pending' => false, 'blocked' => false, 'self' => false], From 0224bc8367731ec11003971e90cdb92b4969c815 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Wed, 5 Aug 2020 05:39:00 +0200 Subject: [PATCH 182/573] Update view/theme/frio/theme.php Co-authored-by: Hypolite Petovan --- view/theme/frio/theme.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/theme/frio/theme.php b/view/theme/frio/theme.php index 3ac04fced5..efba167798 100644 --- a/view/theme/frio/theme.php +++ b/view/theme/frio/theme.php @@ -195,7 +195,7 @@ function frio_remote_nav($a, &$nav) // this isn't optimal because the contact query will be done now twice $fields = ['id', 'url', 'avatar', 'micro', 'name', 'nick', 'baseurl']; if (local_user() && !empty($a->user['uid'])) { - $remoteUser = DBA::selectFirst('contact', $fields, ['uid' => $a->user['uid'], 'self' => true]); + $remoteUser = Contact::selectFirst($fields, ['uid' => $a->user['uid'], 'self' => true]); } elseif (!local_user() && remote_user()) { $remoteUser = Contact::getById(remote_user(), $fields); $nav['remote'] = DI::l10n()->t('Guest'); From c26b72a426c1ddeaa60c12b3ff00f5dfa66f26da Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Tue, 4 Aug 2020 23:06:11 -0400 Subject: [PATCH 183/573] Rework commonFriendsVisitor widget - Use new Contact\Relation method to fetch common contacts - Replace reference to /common by /{nickname}/contacts/common --- src/Content/Widget.php | 86 +++++++------------ src/Module/Profile/Status.php | 2 +- .../widget/remote_friends_common.tpl | 14 ++- 3 files changed, 38 insertions(+), 64 deletions(-) diff --git a/src/Content/Widget.php b/src/Content/Widget.php index e8e70c0e11..a7ce52cc46 100644 --- a/src/Content/Widget.php +++ b/src/Content/Widget.php @@ -374,81 +374,59 @@ class Widget } /** - * Return common friends visitor widget + * Show a random selection of five common contacts between the visitor and the viewed profile user. * - * @param string $profile_uid uid + * @param int $uid Viewed profile user ID + * @param string $nickname Viewed profile user nickname * @return string|void * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \ImagickException */ - public static function commonFriendsVisitor($profile_uid) + public static function commonFriendsVisitor(int $uid, string $nickname) { - if (local_user() == $profile_uid) { - return; + if (local_user() == $uid) { + return ''; } - $zcid = 0; - - $cid = Session::getRemoteContactID($profile_uid); - - if (!$cid) { - if (Profile::getMyURL()) { - $contact = DBA::selectFirst('contact', ['id'], - ['nurl' => Strings::normaliseLink(Profile::getMyURL()), 'uid' => $profile_uid]); - if (DBA::isResult($contact)) { - $cid = $contact['id']; - } else { - $gcontact = DBA::selectFirst('gcontact', ['id'], ['nurl' => Strings::normaliseLink(Profile::getMyURL())]); - if (DBA::isResult($gcontact)) { - $zcid = $gcontact['id']; - } - } - } + $visitorPCid = local_user() ? Contact::getPublicIdByUserId(local_user()) : remote_user(); + if (!$visitorPCid) { + return ''; } - if ($cid == 0 && $zcid == 0) { - return; + $localPCid = Contact::getPublicIdByUserId($uid); + + $condition = [ + 'NOT `self` AND NOT `blocked` AND NOT `hidden` AND `id` != ?', + $localPCid, + ]; + + $total = Contact\Relation::countCommon($localPCid, $visitorPCid, $condition); + if (!$total) { + return ''; } - if ($cid) { - $t = GContact::countCommonFriends($profile_uid, $cid); - } else { - $t = GContact::countCommonFriendsZcid($profile_uid, $zcid); - } - - if (!$t) { - return; - } - - if ($cid) { - $r = GContact::commonFriends($profile_uid, $cid, 0, 5, true); - } else { - $r = GContact::commonFriendsZcid($profile_uid, $zcid, 0, 5, true); - } - - if (!DBA::isResult($r)) { - return; + $commonContacts = Contact\Relation::listCommon($localPCid, $visitorPCid, $condition, 0, 5, true); + if (!DBA::isResult($commonContacts)) { + return ''; } $entries = []; - foreach ($r as $rr) { - $contact = Contact::getByURL($rr['url']); - $entry = [ - 'url' => Contact::magicLink($rr['url']), - 'name' => $contact['name'] ?? $rr['name'], - 'photo' => Contact::getThumb($contact, $rr['photo']), + foreach ($commonContacts as $contact) { + $entries[] = [ + 'url' => Contact::magicLink($contact['url']), + 'name' => $contact['name'], + 'photo' => Contact::getThumb($contact), ]; - $entries[] = $entry; } $tpl = Renderer::getMarkupTemplate('widget/remote_friends_common.tpl'); return Renderer::replaceMacros($tpl, [ - '$desc' => DI::l10n()->tt("%d contact in common", "%d contacts in common", $t), + '$desc' => DI::l10n()->tt("%d contact in common", "%d contacts in common", $total), '$base' => DI::baseUrl(), - '$uid' => $profile_uid, - '$cid' => (($cid) ? $cid : '0'), - '$linkmore' => (($t > 5) ? 'true' : ''), + '$nickname' => $nickname, + '$linkmore' => $total > 5 ? 'true' : '', '$more' => DI::l10n()->t('show more'), - '$items' => $entries + '$contacts' => $entries ]); } diff --git a/src/Module/Profile/Status.php b/src/Module/Profile/Status.php index 4b36cdd4de..200e03ca75 100644 --- a/src/Module/Profile/Status.php +++ b/src/Module/Profile/Status.php @@ -108,7 +108,7 @@ class Status extends BaseProfile $o .= self::getTabsHTML($a, 'status', $is_owner, $a->profile['nickname']); - $o .= Widget::commonFriendsVisitor($a->profile['uid']); + $o .= Widget::commonFriendsVisitor($a->profile['uid'], $a->profile['nickname']); $commpage = $a->profile['page-flags'] == User::PAGE_FLAGS_COMMUNITY; $commvisitor = $commpage && $remote_contact; diff --git a/view/templates/widget/remote_friends_common.tpl b/view/templates/widget/remote_friends_common.tpl index 4ae682f436..74d8e66804 100644 --- a/view/templates/widget/remote_friends_common.tpl +++ b/view/templates/widget/remote_friends_common.tpl @@ -1,22 +1,18 @@ -
      -
      {{$desc nofilter}}      {{if $linkmore}}{{$more}}{{/if}}
      - {{if $items}} - {{foreach $items as $item}} +
      {{$desc nofilter}}      {{if $linkmore}}{{$more}}{{/if}}
      + {{foreach $contacts as $contact}} {{/foreach}} - {{/if}}
      - From fd62629285f1aaa88682af448da26e5a05b60932 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 5 Aug 2020 06:50:51 +0000 Subject: [PATCH 184/573] Probe for the date of the last item --- src/Model/Contact.php | 187 ++---------------------------------------- src/Network/Probe.php | 158 +++++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 178 deletions(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 08cde31eeb..39c955d74a 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -1235,6 +1235,9 @@ class Contact $data['gsid'] = GServer::getID($data['baseurl']); } + $data['last-item'] = Probe::getLastUpdate($data); + Logger::info('Fetched last item', ['url' => $data['url'], 'last-item' => $data['last-item']]); + if (!$contact_id && !empty($data['alias']) && ($data['alias'] != $data['url']) && !$in_loop) { $contact_id = self::getIdForURL($data["alias"], $uid, false, $default, true); } @@ -1264,6 +1267,7 @@ class Contact 'poco' => $data['poco'] ?? '', 'baseurl' => $data['baseurl'] ?? '', 'gsid' => $data['gsid'] ?? null, + 'last-item' => $data['last-item'], 'name-date' => DateTimeFormat::utcNow(), 'uri-date' => DateTimeFormat::utcNow(), 'avatar-date' => DateTimeFormat::utcNow(), @@ -1308,7 +1312,7 @@ class Contact self::updateFromProbe($contact_id, '', false); } } else { - $fields = ['url', 'nurl', 'addr', 'alias', 'name', 'nick', 'keywords', 'location', 'about', 'avatar-date', 'baseurl', 'gsid']; + $fields = ['url', 'nurl', 'addr', 'alias', 'name', 'nick', 'keywords', 'location', 'about', 'avatar-date', 'baseurl', 'gsid', 'last-item']; $contact = DBA::selectFirst('contact', $fields, ['id' => $contact_id]); // This condition should always be true @@ -1329,6 +1333,10 @@ class Contact $updated[$field] = ($data[$field] ?? '') ?: $contact[$field]; } + if ($contact['last-item'] < $data['last-item']) { + $updated['last-item'] = $data['last-item']; + } + if (($updated['addr'] != $contact['addr']) || (!empty($data['alias']) && ($data['alias'] != $contact['alias']))) { $updated['uri-date'] = DateTimeFormat::utcNow(); } @@ -2806,183 +2814,6 @@ class Contact return ['count' => $count, 'added' => $added, 'updated' => $updated]; } - /** - * Set the last date that the contact had posted something - * - * This functionality is currently unused - * - * @param string $data probing result - * @param bool $force force updating - */ - private static function setLastUpdate(array $data, bool $force = false) - { - $contact = self::getByURL($data['url'], false, []); - if (empty($contact)) { - return; - } - if (!$force && !GServer::updateNeeded($contact['created'], $contact['updated'], $contact['last_failure'], $contact['last_contact'])) { - Logger::info("Don't update profile", ['url' => $data['url'], 'updated' => $contact['updated']]); - return; - } - - if (self::updateFromNoScrape($data)) { - return; - } - - if (!empty($data['outbox'])) { - self::updateFromOutbox($data['outbox'], $data); - } elseif (!empty($data['poll']) && ($data['network'] == Protocol::ACTIVITYPUB)) { - self::updateFromOutbox($data['poll'], $data); - } elseif (!empty($data['poll'])) { - self::updateFromFeed($data); - } - } - - /** - * Update a global contact via the "noscrape" endpoint - * - * @param string $data Probing result - * - * @return bool 'true' if update was successful or the server was unreachable - */ - private static function updateFromNoScrape(array $data) - { - // Check the 'noscrape' endpoint when it is a Friendica server - $gserver = DBA::selectFirst('gserver', ['noscrape'], ["`nurl` = ? AND `noscrape` != ''", - Strings::normaliseLink($data['baseurl'])]); - if (!DBA::isResult($gserver)) { - return false; - } - - $curlResult = DI::httpRequest()->get($gserver['noscrape'] . '/' . $data['nick']); - - if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { - $noscrape = json_decode($curlResult->getBody(), true); - if (!empty($noscrape) && !empty($noscrape['updated'])) { - $noscrape['updated'] = DateTimeFormat::utc($noscrape['updated'], DateTimeFormat::MYSQL); - $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $noscrape['updated']]; - DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); - return true; - } - } elseif ($curlResult->isTimeout()) { - // On a timeout return the existing value, but mark the contact as failure - $fields = ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()]; - DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); - return true; - } - return false; - } - - /** - * Update a global contact via an ActivityPub Outbox - * - * @param string $feed - * @param array $data Probing result - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - private static function updateFromOutbox(string $feed, array $data) - { - $outbox = ActivityPub::fetchContent($feed); - if (empty($outbox)) { - return; - } - - if (!empty($outbox['orderedItems'])) { - $items = $outbox['orderedItems']; - } elseif (!empty($outbox['first']['orderedItems'])) { - $items = $outbox['first']['orderedItems']; - } elseif (!empty($outbox['first']['href']) && ($outbox['first']['href'] != $feed)) { - self::updateFromOutbox($outbox['first']['href'], $data); - return; - } elseif (!empty($outbox['first'])) { - if (is_string($outbox['first']) && ($outbox['first'] != $feed)) { - self::updateFromOutbox($outbox['first'], $data); - } else { - Logger::warning('Unexpected data', ['outbox' => $outbox]); - } - return; - } else { - $items = []; - } - - $last_updated = ''; - foreach ($items as $activity) { - if (!empty($activity['published'])) { - $published = DateTimeFormat::utc($activity['published']); - } elseif (!empty($activity['object']['published'])) { - $published = DateTimeFormat::utc($activity['object']['published']); - } else { - continue; - } - - if ($last_updated < $published) { - $last_updated = $published; - } - } - - if (empty($last_updated)) { - return; - } - - $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $last_updated]; - DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); - } - - /** - * Update a global contact via an XML feed - * - * @param string $data Probing result - */ - private static function updateFromFeed(array $data) - { - // Search for the newest entry in the feed - $curlResult = DI::httpRequest()->get($data['poll']); - if (!$curlResult->isSuccess()) { - $fields = ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()]; - DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); - - Logger::info("Profile wasn't reachable (no feed)", ['url' => $data['url']]); - return; - } - - $doc = new DOMDocument(); - @$doc->loadXML($curlResult->getBody()); - - $xpath = new DOMXPath($doc); - $xpath->registerNamespace('atom', 'http://www.w3.org/2005/Atom'); - - $entries = $xpath->query('/atom:feed/atom:entry'); - - $last_updated = ''; - - foreach ($entries as $entry) { - $published_item = $xpath->query('atom:published/text()', $entry)->item(0); - $updated_item = $xpath->query('atom:updated/text()' , $entry)->item(0); - $published = !empty($published_item->nodeValue) ? DateTimeFormat::utc($published_item->nodeValue) : null; - $updated = !empty($updated_item->nodeValue) ? DateTimeFormat::utc($updated_item->nodeValue) : null; - - if (empty($published) || empty($updated)) { - Logger::notice('Invalid entry for XPath.', ['entry' => $entry, 'url' => $data['url']]); - continue; - } - - if ($last_updated < $published) { - $last_updated = $published; - } - - if ($last_updated < $updated) { - $last_updated = $updated; - } - } - - if (empty($last_updated)) { - return; - } - - $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $last_updated]; - DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); - } - /** * Returns a random, global contact of the current node * diff --git a/src/Network/Probe.php b/src/Network/Probe.php index 3c43fb28ea..53418ecb61 100644 --- a/src/Network/Probe.php +++ b/src/Network/Probe.php @@ -38,6 +38,7 @@ use Friendica\Protocol\ActivityPub; use Friendica\Protocol\Email; use Friendica\Protocol\Feed; use Friendica\Util\Crypto; +use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; use Friendica\Util\Strings; use Friendica\Util\XML; @@ -2009,4 +2010,161 @@ class Probe return $fixed; } + + /** + * Fetch the last date that the contact had posted something (publically) + * + * @param string $data probing result + * @return string last activity + */ + public static function getLastUpdate(array $data) + { + if ($lastUpdate = self::updateFromNoScrape($data)) { + return $lastUpdate; + } + + if (!empty($data['outbox'])) { + return self::updateFromOutbox($data['outbox'], $data); + } elseif (!empty($data['poll']) && ($data['network'] == Protocol::ACTIVITYPUB)) { + return self::updateFromOutbox($data['poll'], $data); + } elseif (!empty($data['poll'])) { + return self::updateFromFeed($data); + } + + return ''; + } + + /** + * Fetch the last activity date from the "noscrape" endpoint + * + * @param array $data Probing result + * @return string last activity + * + * @return bool 'true' if update was successful or the server was unreachable + */ + private static function updateFromNoScrape(array $data) + { + // Check the 'noscrape' endpoint when it is a Friendica server + $gserver = DBA::selectFirst('gserver', ['noscrape'], ["`nurl` = ? AND `noscrape` != ''", + Strings::normaliseLink($data['baseurl'])]); + if (!DBA::isResult($gserver)) { + return ''; + } + + $curlResult = DI::httpRequest()->get($gserver['noscrape'] . '/' . $data['nick']); + + if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { + $noscrape = json_decode($curlResult->getBody(), true); + if (!empty($noscrape) && !empty($noscrape['updated'])) { + return DateTimeFormat::utc($noscrape['updated'], DateTimeFormat::MYSQL); + } + } + + return ''; + } + + /** + * Fetch the last activity date from an ActivityPub Outbox + * + * @param string $feed + * @param array $data Probing result + * @return string last activity + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + private static function updateFromOutbox(string $feed, array $data) + { + $outbox = ActivityPub::fetchContent($feed); + if (empty($outbox)) { + return ''; + } + + if (!empty($outbox['orderedItems'])) { + $items = $outbox['orderedItems']; + } elseif (!empty($outbox['first']['orderedItems'])) { + $items = $outbox['first']['orderedItems']; + } elseif (!empty($outbox['first']['href']) && ($outbox['first']['href'] != $feed)) { + return self::updateFromOutbox($outbox['first']['href'], $data); + } elseif (!empty($outbox['first'])) { + if (is_string($outbox['first']) && ($outbox['first'] != $feed)) { + return self::updateFromOutbox($outbox['first'], $data); + } else { + Logger::warning('Unexpected data', ['outbox' => $outbox]); + } + return ''; + } else { + $items = []; + } + + $last_updated = ''; + foreach ($items as $activity) { + if (!empty($activity['published'])) { + $published = DateTimeFormat::utc($activity['published']); + } elseif (!empty($activity['object']['published'])) { + $published = DateTimeFormat::utc($activity['object']['published']); + } else { + continue; + } + + if ($last_updated < $published) { + $last_updated = $published; + } + } + + if (!empty($last_updated)) { + return $last_updated; + } + + return ''; + } + + /** + * Fetch the last activity date from an XML feed + * + * @param array $data Probing result + * @return string last activity + */ + private static function updateFromFeed(array $data) + { + // Search for the newest entry in the feed + $curlResult = DI::httpRequest()->get($data['poll']); + if (!$curlResult->isSuccess()) { + return ''; + } + + $doc = new DOMDocument(); + @$doc->loadXML($curlResult->getBody()); + + $xpath = new DOMXPath($doc); + $xpath->registerNamespace('atom', 'http://www.w3.org/2005/Atom'); + + $entries = $xpath->query('/atom:feed/atom:entry'); + + $last_updated = ''; + + foreach ($entries as $entry) { + $published_item = $xpath->query('atom:published/text()', $entry)->item(0); + $updated_item = $xpath->query('atom:updated/text()' , $entry)->item(0); + $published = !empty($published_item->nodeValue) ? DateTimeFormat::utc($published_item->nodeValue) : null; + $updated = !empty($updated_item->nodeValue) ? DateTimeFormat::utc($updated_item->nodeValue) : null; + + if (empty($published) || empty($updated)) { + Logger::notice('Invalid entry for XPath.', ['entry' => $entry, 'url' => $data['url']]); + continue; + } + + if ($last_updated < $published) { + $last_updated = $published; + } + + if ($last_updated < $updated) { + $last_updated = $updated; + } + } + + if (!empty($last_updated)) { + return $last_updated; + } + + return ''; + } } From cd99b9706b75c8719e3df29f2dbbad4689ee2ed9 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 5 Aug 2020 07:51:15 +0000 Subject: [PATCH 185/573] Check for empty baseurl --- src/Network/Probe.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Network/Probe.php b/src/Network/Probe.php index 53418ecb61..c4f4e57f29 100644 --- a/src/Network/Probe.php +++ b/src/Network/Probe.php @@ -2044,9 +2044,13 @@ class Probe */ private static function updateFromNoScrape(array $data) { + if (empty($data['baseurl'])) { + return ''; + } + // Check the 'noscrape' endpoint when it is a Friendica server $gserver = DBA::selectFirst('gserver', ['noscrape'], ["`nurl` = ? AND `noscrape` != ''", - Strings::normaliseLink($data['baseurl'])]); + Strings::normaliseLink($data['baseurl'])]); if (!DBA::isResult($gserver)) { return ''; } From 3a4be3d5f468d5bedb56199becdaba87fd543f94 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 5 Aug 2020 08:06:47 +0000 Subject: [PATCH 186/573] Fill "last-item" with an empty date when bo date had been provided --- src/Model/Contact.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 39c955d74a..934c628d8e 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -1218,11 +1218,11 @@ class Contact } // Take the default values when probing failed - if (!empty($default) && !in_array($data["network"], array_merge(Protocol::NATIVE_SUPPORT, [Protocol::PUMPIO]))) { + if (!empty($default) && (empty($data['network']) || !in_array($data["network"], array_merge(Protocol::NATIVE_SUPPORT, [Protocol::PUMPIO])))) { $data = array_merge($data, $default); } - if (empty($data) || ($data['network'] == Protocol::PHANTOM)) { + if (empty($data['network']) || ($data['network'] == Protocol::PHANTOM)) { Logger::info('No valid network found', ['url' => $url, 'data' => $data, 'callstack' => System::callstack(20)]); return 0; } @@ -1267,7 +1267,7 @@ class Contact 'poco' => $data['poco'] ?? '', 'baseurl' => $data['baseurl'] ?? '', 'gsid' => $data['gsid'] ?? null, - 'last-item' => $data['last-item'], + 'last-item' => $data['last-item'] ?: DBA::NULL_DATETIME, 'name-date' => DateTimeFormat::utcNow(), 'uri-date' => DateTimeFormat::utcNow(), 'avatar-date' => DateTimeFormat::utcNow(), From 2280f5294522de9283b40235a407389a8593dc56 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 5 Aug 2020 08:24:01 +0000 Subject: [PATCH 187/573] Only query the last item on public contacts --- src/Model/Contact.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 934c628d8e..278632072d 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -1235,8 +1235,10 @@ class Contact $data['gsid'] = GServer::getID($data['baseurl']); } - $data['last-item'] = Probe::getLastUpdate($data); - Logger::info('Fetched last item', ['url' => $data['url'], 'last-item' => $data['last-item']]); + if ($uid == 0) { + $data['last-item'] = Probe::getLastUpdate($data); + Logger::info('Fetched last item', ['url' => $data['url'], 'last-item' => $data['last-item']]); + } if (!$contact_id && !empty($data['alias']) && ($data['alias'] != $data['url']) && !$in_loop) { $contact_id = self::getIdForURL($data["alias"], $uid, false, $default, true); @@ -1267,7 +1269,6 @@ class Contact 'poco' => $data['poco'] ?? '', 'baseurl' => $data['baseurl'] ?? '', 'gsid' => $data['gsid'] ?? null, - 'last-item' => $data['last-item'] ?: DBA::NULL_DATETIME, 'name-date' => DateTimeFormat::utcNow(), 'uri-date' => DateTimeFormat::utcNow(), 'avatar-date' => DateTimeFormat::utcNow(), @@ -1276,6 +1277,10 @@ class Contact 'readonly' => 0, 'pending' => 0]; + if (!empty($data['last-item'])) { + $fields['last-item'] = $data['last-item']; + } + $condition = ['nurl' => Strings::normaliseLink($data["url"]), 'uid' => $uid, 'deleted' => false]; // Before inserting we do check if the entry does exist now. @@ -1333,7 +1338,7 @@ class Contact $updated[$field] = ($data[$field] ?? '') ?: $contact[$field]; } - if ($contact['last-item'] < $data['last-item']) { + if (!empty($data['last-item']) && ($contact['last-item'] < $data['last-item'])) { $updated['last-item'] = $data['last-item']; } From 1f164f66f401ce8f026c3626a6ec1fa441448cb8 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 5 Aug 2020 12:35:38 +0000 Subject: [PATCH 188/573] Simplify contact search --- src/Protocol/Diaspora.php | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index feb1111443..5e4ed9ee17 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -1106,20 +1106,7 @@ class Diaspora */ private static function contactByHandle($uid, $handle) { - $cid = Contact::getIdForURL($handle, $uid); - if (!$cid) { - Logger::log("Haven't found a contact for user " . $uid . " and handle " . $handle, Logger::DEBUG); - return false; - } - - $contact = DBA::selectFirst('contact', [], ['id' => $cid]); - if (!DBA::isResult($contact)) { - // This here shouldn't happen at all - Logger::log("Haven't found a contact for user " . $uid . " and handle " . $handle, Logger::DEBUG); - return false; - } - - return $contact; + return Contact::getByURL($handle, null, [], $uid); } /** From 187dbc09acb26b4fc0119de5feb63702f7c0bfc4 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 5 Aug 2020 12:36:04 +0000 Subject: [PATCH 189/573] Avoid double probing --- src/Model/Contact.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 278632072d..18f0b020e2 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -1158,13 +1158,12 @@ class Contact * @param integer $uid The user id for the contact (0 = public contact) * @param boolean $update true = always update, false = never update, null = update when not found or outdated * @param array $default Default value for creating the contact when every else fails - * @param boolean $in_loop Internally used variable to prevent an endless loop * * @return integer Contact ID * @throws HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function getIdForURL($url, $uid = 0, $update = null, $default = [], $in_loop = false) + public static function getIdForURL($url, $uid = 0, $update = null, $default = []) { Logger::info('Get contact data', ['url' => $url, 'user' => $uid]); @@ -1235,13 +1234,16 @@ class Contact $data['gsid'] = GServer::getID($data['baseurl']); } - if ($uid == 0) { - $data['last-item'] = Probe::getLastUpdate($data); - Logger::info('Fetched last item', ['url' => $data['url'], 'last-item' => $data['last-item']]); + if (!$contact_id && !empty($data['alias']) && ($data['alias'] != $data['url'])) { + $contact = self::getByURL($data['alias'], false, ['id']); + if (!empty($contact['id'])) { + $contact_id = $contact['id']; + } } - if (!$contact_id && !empty($data['alias']) && ($data['alias'] != $data['url']) && !$in_loop) { - $contact_id = self::getIdForURL($data["alias"], $uid, false, $default, true); + if ($uid == 0) { + $data['last-item'] = Probe::getLastUpdate($data); + Logger::info('Fetched last item', ['url' => $url, 'probed_url' => $data['url'], 'last-item' => $data['last-item'], 'callstack' => System::callstack(20)]); } if (!$contact_id) { From 4e5a3ab0f11591d04a1afee406b09186818e68b5 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 5 Aug 2020 12:53:02 +0000 Subject: [PATCH 190/573] Added checked for URL change --- src/Model/Contact.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 18f0b020e2..b565e7e50b 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -1238,6 +1238,16 @@ class Contact $contact = self::getByURL($data['alias'], false, ['id']); if (!empty($contact['id'])) { $contact_id = $contact['id']; + Logger::info('Fetched id by alias', ['cid' => $contact_id, 'url' => $url, 'probed_url' => $data['url'], 'alias' => $data['alias']]); + } + } + + // Possibly there is a contact entry with the probed URL + if (!$contact_id && ($url != $data['url']) && ($url != $data['alias'])) { + $contact = self::getByURL($data['url'], false, ['id']); + if (!empty($contact['id'])) { + $contact_id = $contact['id']; + Logger::info('Fetched id by url', ['cid' => $contact_id, 'url' => $url, 'probed_url' => $data['url'], 'alias' => $data['alias']]); } } From 603b1f965d181ad20ecfcd7ca2993baa7f103778 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 5 Aug 2020 12:53:25 +0000 Subject: [PATCH 191/573] Fix wrong value for parameter --- src/Protocol/OStatus.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index d9e18fa613..74a055265d 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -2017,7 +2017,7 @@ class OStatus $mentioned = $newmentions; foreach ($mentioned as $mention) { - $contact = Contact::getByURL($mention, ['contact-type']); + $contact = Contact::getByURL($mention, false, ['contact-type']); if (!empty($contact) && ($contact['contact-type'] == Contact::TYPE_COMMUNITY)) { XML::addElement($doc, $entry, "link", "", [ From d56ac8d58f6e9ec0dd104ac19e74aeb0af86351a Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 5 Aug 2020 12:57:02 +0000 Subject: [PATCH 192/573] Fix wrong parameter order --- view/theme/frio/theme.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/theme/frio/theme.php b/view/theme/frio/theme.php index dcbeccc301..4b070e99af 100644 --- a/view/theme/frio/theme.php +++ b/view/theme/frio/theme.php @@ -201,7 +201,7 @@ function frio_remote_nav($a, &$nav) $remoteUser = Contact::getById(remote_user(), $fields); $nav['remote'] = DI::l10n()->t('Guest'); } elseif (Model\Profile::getMyURL()) { - $remoteUser = Contact::getByURL($homelink, $fields); + $remoteUser = Contact::getByURL($homelink, null, $fields); $nav['remote'] = DI::l10n()->t('Visitor'); } else { $remoteUser = null; From 4acf7cc38f9eba0018b1ed95f50861c4baa6a3e4 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 5 Aug 2020 14:57:49 +0000 Subject: [PATCH 193/573] Fix: Always search contacts with uid --- src/Model/Contact.php | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 35127ebfcc..3edb21f760 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -1172,17 +1172,12 @@ class Contact $data['gsid'] = GServer::getID($data['baseurl']); } - if (!$contact_id && !empty($data['alias']) && ($data['alias'] != $data['url'])) { - $contact = self::getByURL($data['alias'], false, ['id']); - if (!empty($contact['id'])) { - $contact_id = $contact['id']; - Logger::info('Fetched id by alias', ['cid' => $contact_id, 'url' => $url, 'probed_url' => $data['url'], 'alias' => $data['alias']]); + if (!$contact_id) { + $urls = [Strings::normaliseLink($url), Strings::normaliseLink($data['url'])]; + if (!empty($data['alias'])) { + $urls[] = Strings::normaliseLink($data['alias']); } - } - - // Possibly there is a contact entry with the probed URL - if (!$contact_id && ($url != $data['url']) && ($url != $data['alias'])) { - $contact = self::getByURL($data['url'], false, ['id']); + $contact = self::selectFirst(['id'], ['nurl' => $urls, 'uid' => $uid]); if (!empty($contact['id'])) { $contact_id = $contact['id']; Logger::info('Fetched id by url', ['cid' => $contact_id, 'url' => $url, 'probed_url' => $data['url'], 'alias' => $data['alias']]); From e295dc4f934651729aebf0e11ffce85dede96b78 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 6 Aug 2020 04:51:20 +0000 Subject: [PATCH 194/573] Avoid double probing and unneeded contact updates --- src/Model/Contact.php | 112 ++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 63 deletions(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 3edb21f760..684a30912a 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -220,7 +220,7 @@ class Contact // Add internal fields $removal = []; if (!empty($fields)) { - foreach (['id', 'updated', 'network'] as $internal) { + foreach (['id', 'avatar', 'updated', 'last-update', 'success_update', 'failure_update', 'network'] as $internal) { if (!in_array($internal, $fields)) { $fields[] = $internal; $removal[] = $internal; @@ -250,7 +250,8 @@ class Contact } // Update the contact in the background if needed - if ((($contact['updated'] < DateTimeFormat::utc('now -7 days')) || empty($contact['avatar'])) && + $updated = max($contact['success_update'], $contact['updated'], $contact['last-update'], $contact['failure_update']); + if ((($updated < DateTimeFormat::utc('now -7 days')) || empty($contact['avatar'])) && in_array($contact['network'], Protocol::FEDERATED)) { Worker::add(PRIORITY_LOW, "UpdateContact", $contact['id'], ($uid == 0 ? 'force' : '')); } @@ -1152,6 +1153,9 @@ class Contact if ((empty($data) && is_null($update)) || $update) { $data = Probe::uri($url, "", $uid); + $probed = !empty($data['network']) && ($data['network'] != Protocol::PHANTOM); + } else { + $probed = false; } // Take the default values when probing failed @@ -1180,15 +1184,10 @@ class Contact $contact = self::selectFirst(['id'], ['nurl' => $urls, 'uid' => $uid]); if (!empty($contact['id'])) { $contact_id = $contact['id']; - Logger::info('Fetched id by url', ['cid' => $contact_id, 'url' => $url, 'probed_url' => $data['url'], 'alias' => $data['alias']]); + Logger::info('Fetched id by url', ['cid' => $contact_id, 'uid' => $uid, 'url' => $url, 'probed_url' => $data['url'], 'alias' => $data['alias'], 'addr' => $data['addr']]); } } - if ($uid == 0) { - $data['last-item'] = Probe::getLastUpdate($data); - Logger::info('Fetched last item', ['url' => $url, 'probed_url' => $data['url'], 'last-item' => $data['last-item'], 'callstack' => System::callstack(20)]); - } - if (!$contact_id) { $fields = [ 'uid' => $uid, @@ -1222,8 +1221,9 @@ class Contact 'readonly' => 0, 'pending' => 0]; - if (!empty($data['last-item'])) { - $fields['last-item'] = $data['last-item']; + if (($uid == 0) && $probed) { + $fields['last-item'] = Probe::getLastUpdate($data); + Logger::info('Fetched last item', ['url' => $url, 'probed_url' => $data['url'], 'last-item' => $fields['last-item'], 'callstack' => System::callstack(20)]); } $condition = ['nurl' => Strings::normaliseLink($data["url"]), 'uid' => $uid, 'deleted' => false]; @@ -1249,53 +1249,13 @@ class Contact $contact_id = $contact["id"]; } - if (!empty($data['photo']) && ($data['network'] != Protocol::FEED)) { - self::updateAvatar($contact_id, $data['photo']); - } - - if (in_array($data["network"], array_merge(Protocol::NATIVE_SUPPORT, [Protocol::PUMPIO]))) { - if ($background_update) { - // Update in the background when we fetched the data solely from the database - Worker::add(PRIORITY_MEDIUM, "UpdateContact", $contact_id, ($uid == 0 ? 'force' : '')); - } else { - // Else do a direct update - self::updateFromProbe($contact_id, '', false); - } + if ($background_update && !$probed && in_array($data["network"], array_merge(Protocol::NATIVE_SUPPORT, [Protocol::PUMPIO]))) { + // Update in the background when we fetched the data solely from the database + Worker::add(PRIORITY_MEDIUM, "UpdateContact", $contact_id, ($uid == 0 ? 'force' : '')); + } elseif (!empty($data['network'])) { + self::updateFromProbeArray($contact_id, $data, false); } else { - $fields = ['url', 'nurl', 'addr', 'alias', 'name', 'nick', 'keywords', 'location', 'about', 'avatar-date', 'baseurl', 'gsid', 'last-item']; - $contact = DBA::selectFirst('contact', $fields, ['id' => $contact_id]); - - // This condition should always be true - if (!DBA::isResult($contact)) { - return $contact_id; - } - - $updated = [ - 'url' => $data['url'], - 'nurl' => Strings::normaliseLink($data['url']), - 'updated' => DateTimeFormat::utcNow(), - 'failed' => false - ]; - - $fields = ['addr', 'alias', 'name', 'nick', 'keywords', 'location', 'about', 'baseurl', 'gsid']; - - foreach ($fields as $field) { - $updated[$field] = ($data[$field] ?? '') ?: $contact[$field]; - } - - if (!empty($data['last-item']) && ($contact['last-item'] < $data['last-item'])) { - $updated['last-item'] = $data['last-item']; - } - - if (($updated['addr'] != $contact['addr']) || (!empty($data['alias']) && ($data['alias'] != $contact['alias']))) { - $updated['uri-date'] = DateTimeFormat::utcNow(); - } - - if (($data['name'] != $contact['name']) || ($data['nick'] != $contact['nick'])) { - $updated['name-date'] = DateTimeFormat::utcNow(); - } - - DBA::update('contact', $updated, ['id' => $contact_id], $contact); + Logger::info('Invalid data', ['url' => $url, 'data' => $data]); } return $contact_id; @@ -1862,6 +1822,25 @@ class Contact * @throws \ImagickException */ public static function updateFromProbe(int $id, string $network = '', bool $force = false) + { + $contact = DBA::selectFirst('contact', ['uid', 'url'], ['id' => $id]); + if (!DBA::isResult($contact)) { + return false; + } + + $ret = Probe::uri($contact['url'], $network, $contact['uid'], !$force); + return self::updateFromProbeArray($id, $ret, $force); + } + + /** + * @param integer $id contact id + * @param array $ret Probed data + * @param boolean $force Optional forcing of network probing (otherwise we use the cached data) + * @return boolean + * @throws HTTPException\InternalServerErrorException + * @throws \ImagickException + */ + private static function updateFromProbeArray(int $id, array $ret, bool $force = false) { /* Warning: Never ever fetch the public key via Probe::uri and write it into the contacts. @@ -1873,7 +1852,7 @@ class Contact $fields = ['uid', 'avatar', 'name', 'nick', 'location', 'keywords', 'about', 'subscribe', 'unsearchable', 'url', 'addr', 'batch', 'notify', 'poll', 'request', 'confirm', 'poco', - 'network', 'alias', 'baseurl', 'gsid', 'forum', 'prv', 'contact-type', 'pubkey']; + 'network', 'alias', 'baseurl', 'gsid', 'forum', 'prv', 'contact-type', 'pubkey', 'last-item']; $contact = DBA::selectFirst('contact', $fields, ['id' => $id]); if (!DBA::isResult($contact)) { return false; @@ -1888,8 +1867,6 @@ class Contact $contact['photo'] = $contact['avatar']; unset($contact['avatar']); - $ret = Probe::uri($contact['url'], $network, $uid, !$force); - $updated = DateTimeFormat::utcNow(); // We must not try to update relay contacts via probe. They are no real contacts. @@ -1930,7 +1907,12 @@ class Contact } } - $new_pubkey = $ret['pubkey']; + $new_pubkey = $ret['pubkey'] ?? ''; + + if ($uid == 0) { + $ret['last-item'] = Probe::getLastUpdate($ret); + Logger::info('Fetched last item', ['id' => $id, 'probed_url' => $ret['url'], 'last-item' => $ret['last-item'], 'callstack' => System::callstack(20)]); + } $update = false; @@ -1946,14 +1928,18 @@ class Contact } } + if (!empty($ret['last-item']) && ($contact['last-item'] < $ret['last-item'])) { + $update = true; + } else { + unset($ret['last-item']); + } + if (!empty($ret['photo']) && ($ret['network'] != Protocol::FEED)) { self::updateAvatar($id, $ret['photo'], $update || $force); } if (!$update) { - if ($force) { - self::updateContact($id, $uid, $ret['url'], ['failed' => false, 'last-update' => $updated, 'success_update' => $updated]); - } + self::updateContact($id, $uid, $ret['url'], ['failed' => false, 'last-update' => $updated, 'success_update' => $updated]); // Update the public contact if ($uid != 0) { From ecfbc7027e8a08f78250724568c9678d0dfb0f68 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 6 Aug 2020 05:52:22 +0000 Subject: [PATCH 195/573] Unused "use" removed --- src/Model/Contact.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 684a30912a..d8c479531d 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -21,8 +21,6 @@ namespace Friendica\Model; -use DOMDocument; -use DOMXPath; use Friendica\App\BaseURL; use Friendica\Content\Pager; use Friendica\Core\Hook; From d4f7bfa676bc9a8ffcb304912b48b360453d0f3c Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 6 Aug 2020 10:27:06 +0000 Subject: [PATCH 196/573] New "fcontact" model class --- src/Model/FContact.php | 133 +++++++++++++++++++++++++++++++++ src/Protocol/DFRN.php | 3 +- src/Protocol/Diaspora.php | 151 ++++---------------------------------- src/Worker/Delivery.php | 3 +- 4 files changed, 152 insertions(+), 138 deletions(-) create mode 100644 src/Model/FContact.php diff --git a/src/Model/FContact.php b/src/Model/FContact.php new file mode 100644 index 0000000000..07e8af4075 --- /dev/null +++ b/src/Model/FContact.php @@ -0,0 +1,133 @@ +. + * + */ + +namespace Friendica\Model; + +use Friendica\Core\Logger; +use Friendica\Core\Protocol; +use Friendica\Database\DBA; +use Friendica\Network\Probe; +use Friendica\Util\DateTimeFormat; +use Friendica\Util\Strings; + +class FContact +{ + /** + * Fetches data for a given handle + * + * @param string $handle The handle + * @param boolean $update true = always update, false = never update, null = update when not found or outdated + * + * @return array the queried data + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \ImagickException + */ + public static function getByUrl($handle, $update = null) + { + $person = DBA::selectFirst('fcontact', [], ['network' => Protocol::DIASPORA, 'addr' => $handle]); + if (!DBA::isResult($person)) { + $urls = [$handle, str_replace('http://', 'https://', $handle), Strings::normaliseLink($handle)]; + $person = DBA::selectFirst('fcontact', [], ['network' => Protocol::DIASPORA, 'url' => $urls]); + } + + if (DBA::isResult($person)) { + Logger::debug('In cache', ['person' => $person]); + + if (is_null($update)) { + // update record occasionally so it doesn't get stale + $d = strtotime($person["updated"]." +00:00"); + if ($d < strtotime("now - 14 days")) { + $update = true; + } + + if ($person["guid"] == "") { + $update = true; + } + } + } elseif (is_null($update)) { + $update = !DBA::isResult($person); + } else { + $person = []; + } + + if ($update) { + Logger::info('create or refresh', ['handle' => $handle]); + $r = Probe::uri($handle, Protocol::DIASPORA); + + // Note that Friendica contacts will return a "Diaspora person" + // if Diaspora connectivity is enabled on their server + if ($r && ($r["network"] === Protocol::DIASPORA)) { + self::updateFContact($r); + + $person = self::getByUrl($handle, false); + } + } + + return $person; + } + + /** + * Updates the fcontact table + * + * @param array $arr The fcontact data + * @throws \Exception + */ + private static function updateFContact($arr) + { + $fields = ['name' => $arr["name"], 'photo' => $arr["photo"], + 'request' => $arr["request"], 'nick' => $arr["nick"], + 'addr' => strtolower($arr["addr"]), 'guid' => $arr["guid"], + 'batch' => $arr["batch"], 'notify' => $arr["notify"], + 'poll' => $arr["poll"], 'confirm' => $arr["confirm"], + 'alias' => $arr["alias"], 'pubkey' => $arr["pubkey"], + 'updated' => DateTimeFormat::utcNow()]; + + $condition = ['url' => $arr["url"], 'network' => $arr["network"]]; + + DBA::update('fcontact', $fields, $condition, true); + } + + /** + * get a url (scheme://domain.tld/u/user) from a given Diaspora* + * fcontact guid + * + * @param mixed $fcontact_guid Hexadecimal string guid + * + * @return string the contact url or null + * @throws \Exception + */ + public static function getUrlByGuid($fcontact_guid) + { + Logger::info('fcontact', ['guid' => $fcontact_guid]); + + $r = q( + "SELECT `url` FROM `fcontact` WHERE `url` != '' AND `network` = '%s' AND `guid` = '%s'", + DBA::escape(Protocol::DIASPORA), + DBA::escape($fcontact_guid) + ); + + if (DBA::isResult($r)) { + return $r[0]['url']; + } + + return null; + } +} diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index 281220bc34..e10cc6915c 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -33,6 +33,7 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Conversation; use Friendica\Model\Event; +use Friendica\Model\FContact; use Friendica\Model\Item; use Friendica\Model\ItemURI; use Friendica\Model\Mail; @@ -1409,7 +1410,7 @@ class DFRN } } - $fcontact = Diaspora::personByHandle($contact['addr']); + $fcontact = FContact::getByUrl($contact['addr']); if (empty($fcontact)) { Logger::log('Unable to find contact details for ' . $contact['id'] . ' - ' . $contact['addr']); return -22; diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index 5e4ed9ee17..be32eb88b4 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -34,6 +34,7 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Conversation; +use Friendica\Model\FContact; use Friendica\Model\Item; use Friendica\Model\ItemURI; use Friendica\Model\Mail; @@ -936,7 +937,7 @@ class Diaspora Logger::log("Fetching diaspora key for: ".$handle); - $r = self::personByHandle($handle); + $r = FContact::getByUrl($handle); if ($r) { return $r["pubkey"]; } @@ -944,81 +945,6 @@ class Diaspora return ""; } - /** - * Fetches data for a given handle - * - * @param string $handle The handle - * @param boolean $update true = always update, false = never update, null = update when not found or outdated - * - * @return array the queried data - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function personByHandle($handle, $update = null) - { - $person = DBA::selectFirst('fcontact', [], ['network' => Protocol::DIASPORA, 'addr' => $handle]); - if (!DBA::isResult($person)) { - $urls = [$handle, str_replace('http://', 'https://', $handle), Strings::normaliseLink($handle)]; - $person = DBA::selectFirst('fcontact', [], ['network' => Protocol::DIASPORA, 'url' => $urls]); - } - - if (DBA::isResult($person)) { - Logger::debug('In cache', ['person' => $person]); - - if (is_null($update)) { - // update record occasionally so it doesn't get stale - $d = strtotime($person["updated"]." +00:00"); - if ($d < strtotime("now - 14 days")) { - $update = true; - } - - if ($person["guid"] == "") { - $update = true; - } - } - } elseif (is_null($update)) { - $update = !DBA::isResult($person); - } else { - $person = []; - } - - if ($update) { - Logger::log("create or refresh", Logger::DEBUG); - $r = Probe::uri($handle, Protocol::DIASPORA); - - // Note that Friendica contacts will return a "Diaspora person" - // if Diaspora connectivity is enabled on their server - if ($r && ($r["network"] === Protocol::DIASPORA)) { - self::updateFContact($r); - - $person = self::personByHandle($handle, false); - } - } - - return $person; - } - - /** - * Updates the fcontact table - * - * @param array $arr The fcontact data - * @throws \Exception - */ - private static function updateFContact($arr) - { - $fields = ['name' => $arr["name"], 'photo' => $arr["photo"], - 'request' => $arr["request"], 'nick' => $arr["nick"], - 'addr' => strtolower($arr["addr"]), 'guid' => $arr["guid"], - 'batch' => $arr["batch"], 'notify' => $arr["notify"], - 'poll' => $arr["poll"], 'confirm' => $arr["confirm"], - 'alias' => $arr["alias"], 'pubkey' => $arr["pubkey"], - 'updated' => DateTimeFormat::utcNow()]; - - $condition = ['url' => $arr["url"], 'network' => $arr["network"]]; - - DBA::update('fcontact', $fields, $condition, true); - } - /** * get a handle (user@domain.tld) from a given contact id * @@ -1066,32 +992,6 @@ class Diaspora return strtolower($handle); } - /** - * get a url (scheme://domain.tld/u/user) from a given Diaspora* - * fcontact guid - * - * @param mixed $fcontact_guid Hexadecimal string guid - * - * @return string the contact url or null - * @throws \Exception - */ - public static function urlFromContactGuid($fcontact_guid) - { - Logger::info('fcontact', ['guid' => $fcontact_guid]); - - $r = q( - "SELECT `url` FROM `fcontact` WHERE `url` != '' AND `network` = '%s' AND `guid` = '%s'", - DBA::escape(Protocol::DIASPORA), - DBA::escape($fcontact_guid) - ); - - if (DBA::isResult($r)) { - return $r[0]['url']; - } - - return null; - } - /** * Get a contact id for a given handle * @@ -1120,7 +1020,7 @@ class Diaspora */ public static function isSupportedByContactUrl($url, $update = null) { - return !empty(self::personByHandle($url, $update)); + return !empty(FContact::getByUrl($url, $update)); } /** @@ -1272,7 +1172,7 @@ class Diaspora // 0 => '[url=/people/0123456789abcdef]Foo Bar[/url]' // 1 => '0123456789abcdef' // 2 => 'Foo Bar' - $handle = self::urlFromContactGuid($match[1]); + $handle = FContact::getUrlByGuid($match[1]); if ($handle) { $return = '@[url='.$handle.']'.$match[2].'[/url]'; @@ -1479,7 +1379,7 @@ class Diaspora $item = Item::selectFirst($fields, $condition); if (!DBA::isResult($item)) { - $person = self::personByHandle($author); + $person = FContact::getByUrl($author); $result = self::storeByGuid($guid, $person["url"], $uid); // We don't have an url for items that arrived at the public dispatcher @@ -1702,7 +1602,7 @@ class Diaspora if (DBA::isResult($item)) { return $item["uri"]; } elseif (!$onlyfound) { - $person = self::personByHandle($author); + $person = FContact::getByUrl($author); $parts = parse_url($person['url']); unset($parts['path']); @@ -1733,27 +1633,6 @@ class Diaspora } } - /** - * Find the best importer for a comment, like, ... - * - * @param string $guid The guid of the item - * - * @return array|boolean the origin owner of that post - or false - * @throws \Exception - */ - private static function importerForGuid($guid) - { - $item = Item::selectFirst(['uid'], ['origin' => true, 'guid' => $guid]); - if (DBA::isResult($item)) { - Logger::log("Found user ".$item['uid']." as owner of item ".$guid, Logger::DEBUG); - $contact = DBA::selectFirst('contact', [], ['self' => true, 'uid' => $item['uid']]); - if (DBA::isResult($contact)) { - return $contact; - } - } - return false; - } - /** * Store the mentions in the tag table * @@ -1779,7 +1658,7 @@ class Diaspora continue; } - $person = self::personByHandle($match[3]); + $person = FContact::getByUrl($match[3]); if (empty($person)) { continue; } @@ -1835,7 +1714,7 @@ class Diaspora return false; } - $person = self::personByHandle($author); + $person = FContact::getByUrl($author); if (!is_array($person)) { Logger::log("unable to find author details"); return false; @@ -1950,7 +1829,7 @@ class Diaspora $body = Markdown::toBBCode($msg_text); $message_uri = $msg_author.":".$msg_guid; - $person = self::personByHandle($msg_author); + $person = FContact::getByUrl($msg_author); return Mail::insert([ 'uid' => $importer['uid'], @@ -2067,7 +1946,7 @@ class Diaspora return false; } - $person = self::personByHandle($author); + $person = FContact::getByUrl($author); if (!is_array($person)) { Logger::log("unable to find author details"); return false; @@ -2173,7 +2052,7 @@ class Diaspora $message_uri = $author.":".$guid; - $person = self::personByHandle($author); + $person = FContact::getByUrl($author); if (!$person) { Logger::log("unable to find author details"); return false; @@ -2239,7 +2118,7 @@ class Diaspora return false; } - $person = self::personByHandle($author); + $person = FContact::getByUrl($author); if (!is_array($person)) { Logger::log("Person not found: ".$author); return false; @@ -2513,7 +2392,7 @@ class Diaspora Logger::log("Author ".$author." wants to listen to us.", Logger::DEBUG); } - $ret = self::personByHandle($author); + $ret = FContact::getByUrl($author); if (!$ret || ($ret["network"] != Protocol::DIASPORA)) { Logger::log("Cannot resolve diaspora handle ".$author." for ".$recipient); @@ -2801,7 +2680,7 @@ class Diaspora $target_guid = Strings::escapeTags(XML::unescape($data->target_guid)); $target_type = Strings::escapeTags(XML::unescape($data->target_type)); - $person = self::personByHandle($author); + $person = FContact::getByUrl($author); if (!is_array($person)) { Logger::log("unable to find author detail for ".$author); return false; @@ -3204,7 +3083,7 @@ class Diaspora // We always try to use the data from the fcontact table. // This is important for transmitting data to Friendica servers. if (!empty($contact['addr'])) { - $fcontact = self::personByHandle($contact['addr']); + $fcontact = FContact::getByUrl($contact['addr']); if (!empty($fcontact)) { $dest_url = ($public_batch ? $fcontact["batch"] : $fcontact["notify"]); } diff --git a/src/Worker/Delivery.php b/src/Worker/Delivery.php index c69628398a..5278a7647f 100644 --- a/src/Worker/Delivery.php +++ b/src/Worker/Delivery.php @@ -33,6 +33,7 @@ use Friendica\Protocol\Activity; use Friendica\Util\Strings; use Friendica\Util\Network; use Friendica\Core\Worker; +use Friendica\Model\FContact; class Delivery { @@ -267,7 +268,7 @@ class Delivery private static function deliverDFRN($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup) { // Transmit Diaspora reshares via Diaspora if the Friendica contact support Diaspora - if (Diaspora::isReshare($target_item['body']) && !empty(Diaspora::personByHandle($contact['addr'], false))) { + if (Diaspora::isReshare($target_item['body']) && !empty(FContact::getByUrl($contact['addr'], false))) { Logger::info('Reshare will be transmitted via Diaspora', ['url' => $contact['url'], 'guid' => ($target_item['guid'] ?? '') ?: $target_item['id']]); self::deliverDiaspora($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup); return; From 2cbc9359446625c679352dfd759638d45339885b Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 6 Aug 2020 10:31:05 +0000 Subject: [PATCH 197/573] Renamed function --- src/Model/FContact.php | 4 ++-- src/Protocol/DFRN.php | 2 +- src/Protocol/Diaspora.php | 26 +++++++++++++------------- src/Worker/Delivery.php | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Model/FContact.php b/src/Model/FContact.php index 07e8af4075..c4d6251c3b 100644 --- a/src/Model/FContact.php +++ b/src/Model/FContact.php @@ -40,7 +40,7 @@ class FContact * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function getByUrl($handle, $update = null) + public static function getByURL($handle, $update = null) { $person = DBA::selectFirst('fcontact', [], ['network' => Protocol::DIASPORA, 'addr' => $handle]); if (!DBA::isResult($person)) { @@ -77,7 +77,7 @@ class FContact if ($r && ($r["network"] === Protocol::DIASPORA)) { self::updateFContact($r); - $person = self::getByUrl($handle, false); + $person = self::getByURL($handle, false); } } diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index e10cc6915c..96b1450aed 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -1410,7 +1410,7 @@ class DFRN } } - $fcontact = FContact::getByUrl($contact['addr']); + $fcontact = FContact::getByURL($contact['addr']); if (empty($fcontact)) { Logger::log('Unable to find contact details for ' . $contact['id'] . ' - ' . $contact['addr']); return -22; diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index be32eb88b4..c77e1610d0 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -937,7 +937,7 @@ class Diaspora Logger::log("Fetching diaspora key for: ".$handle); - $r = FContact::getByUrl($handle); + $r = FContact::getByURL($handle); if ($r) { return $r["pubkey"]; } @@ -1020,7 +1020,7 @@ class Diaspora */ public static function isSupportedByContactUrl($url, $update = null) { - return !empty(FContact::getByUrl($url, $update)); + return !empty(FContact::getByURL($url, $update)); } /** @@ -1379,7 +1379,7 @@ class Diaspora $item = Item::selectFirst($fields, $condition); if (!DBA::isResult($item)) { - $person = FContact::getByUrl($author); + $person = FContact::getByURL($author); $result = self::storeByGuid($guid, $person["url"], $uid); // We don't have an url for items that arrived at the public dispatcher @@ -1602,7 +1602,7 @@ class Diaspora if (DBA::isResult($item)) { return $item["uri"]; } elseif (!$onlyfound) { - $person = FContact::getByUrl($author); + $person = FContact::getByURL($author); $parts = parse_url($person['url']); unset($parts['path']); @@ -1658,7 +1658,7 @@ class Diaspora continue; } - $person = FContact::getByUrl($match[3]); + $person = FContact::getByURL($match[3]); if (empty($person)) { continue; } @@ -1714,7 +1714,7 @@ class Diaspora return false; } - $person = FContact::getByUrl($author); + $person = FContact::getByURL($author); if (!is_array($person)) { Logger::log("unable to find author details"); return false; @@ -1829,7 +1829,7 @@ class Diaspora $body = Markdown::toBBCode($msg_text); $message_uri = $msg_author.":".$msg_guid; - $person = FContact::getByUrl($msg_author); + $person = FContact::getByURL($msg_author); return Mail::insert([ 'uid' => $importer['uid'], @@ -1946,7 +1946,7 @@ class Diaspora return false; } - $person = FContact::getByUrl($author); + $person = FContact::getByURL($author); if (!is_array($person)) { Logger::log("unable to find author details"); return false; @@ -2052,7 +2052,7 @@ class Diaspora $message_uri = $author.":".$guid; - $person = FContact::getByUrl($author); + $person = FContact::getByURL($author); if (!$person) { Logger::log("unable to find author details"); return false; @@ -2118,7 +2118,7 @@ class Diaspora return false; } - $person = FContact::getByUrl($author); + $person = FContact::getByURL($author); if (!is_array($person)) { Logger::log("Person not found: ".$author); return false; @@ -2392,7 +2392,7 @@ class Diaspora Logger::log("Author ".$author." wants to listen to us.", Logger::DEBUG); } - $ret = FContact::getByUrl($author); + $ret = FContact::getByURL($author); if (!$ret || ($ret["network"] != Protocol::DIASPORA)) { Logger::log("Cannot resolve diaspora handle ".$author." for ".$recipient); @@ -2680,7 +2680,7 @@ class Diaspora $target_guid = Strings::escapeTags(XML::unescape($data->target_guid)); $target_type = Strings::escapeTags(XML::unescape($data->target_type)); - $person = FContact::getByUrl($author); + $person = FContact::getByURL($author); if (!is_array($person)) { Logger::log("unable to find author detail for ".$author); return false; @@ -3083,7 +3083,7 @@ class Diaspora // We always try to use the data from the fcontact table. // This is important for transmitting data to Friendica servers. if (!empty($contact['addr'])) { - $fcontact = FContact::getByUrl($contact['addr']); + $fcontact = FContact::getByURL($contact['addr']); if (!empty($fcontact)) { $dest_url = ($public_batch ? $fcontact["batch"] : $fcontact["notify"]); } diff --git a/src/Worker/Delivery.php b/src/Worker/Delivery.php index 5278a7647f..9027598c4e 100644 --- a/src/Worker/Delivery.php +++ b/src/Worker/Delivery.php @@ -268,7 +268,7 @@ class Delivery private static function deliverDFRN($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup) { // Transmit Diaspora reshares via Diaspora if the Friendica contact support Diaspora - if (Diaspora::isReshare($target_item['body']) && !empty(FContact::getByUrl($contact['addr'], false))) { + if (Diaspora::isReshare($target_item['body']) && !empty(FContact::getByURL($contact['addr'], false))) { Logger::info('Reshare will be transmitted via Diaspora', ['url' => $contact['url'], 'guid' => ($target_item['guid'] ?? '') ?: $target_item['id']]); self::deliverDiaspora($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup); return; From f09d9bc9cccd4d3875d8e66fe75cb7b948b5741f Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 6 Aug 2020 18:53:45 +0000 Subject: [PATCH 198/573] Remove url caching, locking cleanup --- src/Core/Worker.php | 22 +++++++------ src/Model/Contact.php | 45 +++++++++++++++----------- src/Module/Debug/Probe.php | 2 +- src/Network/Probe.php | 19 ++--------- src/Protocol/ActivityPub/Processor.php | 2 +- src/Protocol/ActivityPub/Receiver.php | 2 +- src/Protocol/DFRN.php | 5 ++- src/Protocol/OStatus.php | 16 +++------ src/Worker/Cron.php | 2 +- src/Worker/OnePoll.php | 4 +-- src/Worker/UpdateContact.php | 9 ++---- 11 files changed, 55 insertions(+), 73 deletions(-) diff --git a/src/Core/Worker.php b/src/Core/Worker.php index 937dd0a565..48984f6d82 100644 --- a/src/Core/Worker.php +++ b/src/Core/Worker.php @@ -39,6 +39,8 @@ class Worker const FAST_COMMANDS = ['APDelivery', 'Delivery', 'CreateShadowEntry']; + const LOCK_PROCESS = 'worker_process'; + const LOCK_WORKER = 'worker'; private static $up_start; private static $db_duration = 0; @@ -125,9 +127,9 @@ class Worker } // Trying to fetch new processes - but only once when successful - if (!$refetched && DI::lock()->acquire('worker_process', 0)) { + if (!$refetched && DI::lock()->acquire(self::LOCK_PROCESS, 0)) { self::findWorkerProcesses(); - DI::lock()->release('worker_process'); + DI::lock()->release(self::LOCK_PROCESS); self::$state = self::STATE_REFETCH; $refetched = true; } else { @@ -139,21 +141,21 @@ class Worker if (!self::getWaitingJobForPID()) { self::$state = self::STATE_LONG_LOOP; - if (DI::lock()->acquire('worker', 0)) { + if (DI::lock()->acquire(self::LOCK_WORKER, 0)) { // Count active workers and compare them with a maximum value that depends on the load if (self::tooMuchWorkers()) { Logger::info('Active worker limit reached, quitting.'); - DI::lock()->release('worker'); + DI::lock()->release(self::LOCK_WORKER); return; } // Check free memory if (DI::process()->isMinMemoryReached()) { Logger::info('Memory limit reached, quitting.'); - DI::lock()->release('worker'); + DI::lock()->release(self::LOCK_WORKER); return; } - DI::lock()->release('worker'); + DI::lock()->release(self::LOCK_WORKER); } } @@ -949,14 +951,14 @@ class Worker } $stamp = (float)microtime(true); - if (!DI::lock()->acquire('worker_process')) { + if (!DI::lock()->acquire(self::LOCK_PROCESS)) { return false; } self::$lock_duration += (microtime(true) - $stamp); $found = self::findWorkerProcesses(); - DI::lock()->release('worker_process'); + DI::lock()->release(self::LOCK_PROCESS); if ($found) { $stamp = (float)microtime(true); @@ -1194,13 +1196,13 @@ class Worker } // If there is a lock then we don't have to check for too much worker - if (!DI::lock()->acquire('worker', 0)) { + if (!DI::lock()->acquire(self::LOCK_WORKER, 0)) { return $added; } // If there are already enough workers running, don't fork another one $quit = self::tooMuchWorkers(); - DI::lock()->release('worker'); + DI::lock()->release(self::LOCK_WORKER); if ($quit) { return $added; diff --git a/src/Model/Contact.php b/src/Model/Contact.php index d8c479531d..c8d7152143 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -251,7 +251,7 @@ class Contact $updated = max($contact['success_update'], $contact['updated'], $contact['last-update'], $contact['failure_update']); if ((($updated < DateTimeFormat::utc('now -7 days')) || empty($contact['avatar'])) && in_array($contact['network'], Protocol::FEDERATED)) { - Worker::add(PRIORITY_LOW, "UpdateContact", $contact['id'], ($uid == 0 ? 'force' : '')); + Worker::add(PRIORITY_LOW, "UpdateContact", $contact['id']); } // Remove the internal fields @@ -409,7 +409,7 @@ class Contact } // Update the existing contact - self::updateFromProbe($contact['id'], '', true); + self::updateFromProbe($contact['id']); // And fetch the result $contact = DBA::selectFirst('contact', ['baseurl'], ['id' => $contact['id']]); @@ -1249,9 +1249,9 @@ class Contact if ($background_update && !$probed && in_array($data["network"], array_merge(Protocol::NATIVE_SUPPORT, [Protocol::PUMPIO]))) { // Update in the background when we fetched the data solely from the database - Worker::add(PRIORITY_MEDIUM, "UpdateContact", $contact_id, ($uid == 0 ? 'force' : '')); + Worker::add(PRIORITY_MEDIUM, "UpdateContact", $contact_id); } elseif (!empty($data['network'])) { - self::updateFromProbeArray($contact_id, $data, false); + self::updateFromProbeArray($contact_id, $data); } else { Logger::info('Invalid data', ['url' => $url, 'data' => $data]); } @@ -1814,31 +1814,29 @@ class Contact /** * @param integer $id contact id * @param string $network Optional network we are probing for - * @param boolean $force Optional forcing of network probing (otherwise we use the cached data) * @return boolean * @throws HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function updateFromProbe(int $id, string $network = '', bool $force = false) + public static function updateFromProbe(int $id, string $network = '') { $contact = DBA::selectFirst('contact', ['uid', 'url'], ['id' => $id]); if (!DBA::isResult($contact)) { return false; } - $ret = Probe::uri($contact['url'], $network, $contact['uid'], !$force); - return self::updateFromProbeArray($id, $ret, $force); + $ret = Probe::uri($contact['url'], $network, $contact['uid']); + return self::updateFromProbeArray($id, $ret); } /** * @param integer $id contact id * @param array $ret Probed data - * @param boolean $force Optional forcing of network probing (otherwise we use the cached data) * @return boolean * @throws HTTPException\InternalServerErrorException * @throws \ImagickException */ - private static function updateFromProbeArray(int $id, array $ret, bool $force = false) + private static function updateFromProbeArray(int $id, array $ret) { /* Warning: Never ever fetch the public key via Probe::uri and write it into the contacts. @@ -1878,7 +1876,7 @@ class Contact // If Probe::uri fails the network code will be different ("feed" or "unkn") if (in_array($ret['network'], [Protocol::FEED, Protocol::PHANTOM]) && ($ret['network'] != $contact['network'])) { - if ($force && ($uid == 0)) { + if ($uid == 0) { self::updateContact($id, $uid, $ret['url'], ['failed' => true, 'last-update' => $updated, 'failure_update' => $updated]); } return false; @@ -1933,7 +1931,7 @@ class Contact } if (!empty($ret['photo']) && ($ret['network'] != Protocol::FEED)) { - self::updateAvatar($id, $ret['photo'], $update || $force); + self::updateAvatar($id, $ret['photo'], $update); } if (!$update) { @@ -1941,7 +1939,10 @@ class Contact // Update the public contact if ($uid != 0) { - self::updateFromProbeByURL($ret['url']); + $contact = self::getByURL($ret['url'], false, ['id']); + if (!empty($contact['id'])) { + self::updateFromProbeArray($contact['id'], $ret); + } } return true; @@ -1963,7 +1964,7 @@ class Contact $ret['name-date'] = $updated; } - if ($force && ($uid == 0)) { + if ($uid == 0) { $ret['last-update'] = $updated; $ret['success_update'] = $updated; $ret['failed'] = false; @@ -1976,7 +1977,13 @@ class Contact return true; } - public static function updateFromProbeByURL($url, $force = false) + /** + * @param integer $url contact url + * @return integer Contact id + * @throws HTTPException\InternalServerErrorException + * @throws \ImagickException + */ + public static function updateFromProbeByURL($url) { $id = self::getIdForURL($url); @@ -1984,7 +1991,7 @@ class Contact return $id; } - self::updateFromProbe($id, '', $force); + self::updateFromProbe($id); return $id; } @@ -2080,7 +2087,7 @@ class Contact if (!empty($arr['contact']['name'])) { $ret = $arr['contact']; } else { - $ret = Probe::uri($url, $network, $user['uid'], false); + $ret = Probe::uri($url, $network, $user['uid']); } if (($network != '') && ($ret['network'] != $network)) { @@ -2371,7 +2378,7 @@ class Contact } // Ensure to always have the correct network type, independent from the connection request method - self::updateFromProbe($contact['id'], '', true); + self::updateFromProbe($contact['id']); return true; } else { @@ -2400,7 +2407,7 @@ class Contact $contact_id = DBA::lastInsertId(); // Ensure to always have the correct network type, independent from the connection request method - self::updateFromProbe($contact_id, '', true); + self::updateFromProbe($contact_id); self::updateAvatar($contact_id, $photo, true); diff --git a/src/Module/Debug/Probe.php b/src/Module/Debug/Probe.php index 0d1c3282b2..48838cc8bb 100644 --- a/src/Module/Debug/Probe.php +++ b/src/Module/Debug/Probe.php @@ -44,7 +44,7 @@ class Probe extends BaseModule $res = ''; if (!empty($addr)) { - $res = NetworkProbe::uri($addr, '', 0, false); + $res = NetworkProbe::uri($addr, '', 0); $res = print_r($res, true); } diff --git a/src/Network/Probe.php b/src/Network/Probe.php index c4f4e57f29..c841b9ce46 100644 --- a/src/Network/Probe.php +++ b/src/Network/Probe.php @@ -328,16 +328,8 @@ class Probe * @throws HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function uri($uri, $network = '', $uid = -1, $cache = true) + public static function uri($uri, $network = '', $uid = -1) { - $cachekey = 'Probe::uri:' . $network . ':' . $uri; - if ($cache) { - $result = DI::cache()->get($cachekey); - if (!is_null($result)) { - return $result; - } - } - if ($uid == -1) { $uid = local_user(); } @@ -408,14 +400,7 @@ class Probe $data['hide'] = self::getHideStatus($data['url']); } - $data = self::rearrangeData($data); - - // Only store into the cache if the value seems to be valid - if (!in_array($data['network'], [Protocol::PHANTOM, Protocol::MAIL])) { - DI::cache()->set($cachekey, $data, Duration::DAY); - } - - return $data; + return self::rearrangeData($data); } diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 7c40105e20..037c2e849a 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -822,7 +822,7 @@ class Processor } Logger::info('Updating profile', ['object' => $activity['object_id']]); - Contact::updateFromProbeByURL($activity['object_id'], true); + Contact::updateFromProbeByURL($activity['object_id']); } /** diff --git a/src/Protocol/ActivityPub/Receiver.php b/src/Protocol/ActivityPub/Receiver.php index 226ad60104..88c23f2be6 100644 --- a/src/Protocol/ActivityPub/Receiver.php +++ b/src/Protocol/ActivityPub/Receiver.php @@ -679,7 +679,7 @@ class Receiver return; } - if (Contact::updateFromProbe($cid, '', true)) { + if (Contact::updateFromProbe($cid)) { Logger::info('Update was successful', ['id' => $cid, 'uid' => $uid, 'url' => $url]); } diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index 96b1450aed..2956e7bfc8 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -2878,14 +2878,13 @@ class DFRN * Checks if the given contact url does support DFRN * * @param string $url profile url - * @param boolean $update Update the profile * @return boolean * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function isSupportedByContactUrl($url, $update = false) + public static function isSupportedByContactUrl($url) { - $probe = Probe::uri($url, Protocol::DFRN, 0, !$update); + $probe = Probe::uri($url, Protocol::DFRN); return $probe['network'] == Protocol::DFRN; } } diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 74a055265d..c00c029cd4 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -544,15 +544,8 @@ class OStatus } elseif ($item['contact-id'] < 0) { Logger::log("Item with uri ".$item["uri"]." is from a blocked contact.", Logger::DEBUG); } else { - // We are having duplicated entries. Hopefully this solves it. - if (DI::lock()->acquire('ostatus_process_item_insert')) { - $ret = Item::insert($item); - DI::lock()->release('ostatus_process_item_insert'); - Logger::log("Item with uri ".$item["uri"]." for user ".$importer["uid"].' stored. Return value: '.$ret); - } else { - $ret = Item::insert($item); - Logger::log("We couldn't lock - but tried to store the item anyway. Return value is ".$ret); - } + $ret = Item::insert($item); + Logger::log("Item with uri ".$item["uri"]." for user ".$importer["uid"].' stored. Return value: '.$ret); } } } @@ -2218,14 +2211,13 @@ class OStatus * Checks if the given contact url does support OStatus * * @param string $url profile url - * @param boolean $update Update the profile * @return boolean * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function isSupportedByContactUrl($url, $update = false) + public static function isSupportedByContactUrl($url) { - $probe = Probe::uri($url, Protocol::OSTATUS, 0, !$update); + $probe = Probe::uri($url, Protocol::OSTATUS); return $probe['network'] == Protocol::OSTATUS; } } diff --git a/src/Worker/Cron.php b/src/Worker/Cron.php index 37d4d4b807..2c264f0746 100644 --- a/src/Worker/Cron.php +++ b/src/Worker/Cron.php @@ -168,7 +168,7 @@ class Cron $oldest_id = $contact['id']; $oldest_date = $contact['last-update']; } - Worker::add(PRIORITY_LOW, "UpdateContact", $contact['id'], 'force'); + Worker::add(PRIORITY_LOW, "UpdateContact", $contact['id']); ++$count; } Logger::info('Initiated update for public contacts', ['interval' => $count, 'id' => $oldest_id, 'oldest' => $oldest_date]); diff --git a/src/Worker/OnePoll.php b/src/Worker/OnePoll.php index 40681175d4..00d5694db3 100644 --- a/src/Worker/OnePoll.php +++ b/src/Worker/OnePoll.php @@ -61,12 +61,12 @@ class OnePoll } if (($contact['network'] != Protocol::MAIL) || $force) { - Contact::updateFromProbe($contact_id, '', $force); + Contact::updateFromProbe($contact_id); } // Special treatment for wrongly detected local contacts if (!$force && ($contact['network'] != Protocol::DFRN) && Contact::isLocalById($contact_id)) { - Contact::updateFromProbe($contact_id, Protocol::DFRN, true); + Contact::updateFromProbe($contact_id, Protocol::DFRN); $contact = DBA::selectFirst('contact', [], ['id' => $contact_id]); } diff --git a/src/Worker/UpdateContact.php b/src/Worker/UpdateContact.php index 67bc45ef98..74fbe2c22a 100644 --- a/src/Worker/UpdateContact.php +++ b/src/Worker/UpdateContact.php @@ -29,14 +29,11 @@ class UpdateContact /** * Update contact data via probe * @param int $contact_id Contact ID - * @param string $command */ - public static function execute($contact_id, $command = '') + public static function execute($contact_id) { - $force = ($command == "force"); + $success = Contact::updateFromProbe($contact_id); - $success = Contact::updateFromProbe($contact_id, '', $force); - - Logger::info('Updated from probe', ['id' => $contact_id, 'force' => $force, 'success' => $success]); + Logger::info('Updated from probe', ['id' => $contact_id, 'success' => $success]); } } From c89b690156d7f36dcfe0999c1d3ea1b7637b99b4 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 6 Aug 2020 19:04:00 +0000 Subject: [PATCH 199/573] Removed unused parameter --- src/Module/Contact.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Module/Contact.php b/src/Module/Contact.php index 750a927e88..3cb57f8f20 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -185,7 +185,7 @@ class Contact extends BaseModule } // Update the entry in the contact table - Model\Contact::updateFromProbe($contact_id, '', true); + Model\Contact::updateFromProbe($contact_id); } /** From 077b57ecb38e7114539a54c0670a4930d434c276 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Thu, 6 Aug 2020 10:29:19 -0400 Subject: [PATCH 200/573] Actually destroy session on logout --- src/Core/Session/Native.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Core/Session/Native.php b/src/Core/Session/Native.php index 49550a27c0..83ed0f6e6e 100644 --- a/src/Core/Session/Native.php +++ b/src/Core/Session/Native.php @@ -53,4 +53,9 @@ class Native extends AbstractSession implements ISession session_start(); return $this; } + + public function clear() + { + session_destroy(); + } } From b45ba63dbf4ab9e5c2842907a0aa5123ab474771 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Thu, 6 Aug 2020 10:30:06 -0400 Subject: [PATCH 201/573] Add mutuals and all methods in Contact\Relation - Remove unused $fields parameters from list methods - Fix wrong SQL condition in listCommon --- src/Model/Contact/Relation.php | 103 +++++++++++++++++++++++++++++---- 1 file changed, 93 insertions(+), 10 deletions(-) diff --git a/src/Model/Contact/Relation.php b/src/Model/Contact/Relation.php index cc0570ec63..d1e51811ff 100644 --- a/src/Model/Contact/Relation.php +++ b/src/Model/Contact/Relation.php @@ -335,7 +335,6 @@ class Relation * Returns a paginated list of contacts that are followed the provided public contact. * * @param int $cid Public contact id - * @param array $field Field list * @param array $condition Additional condition on the contact table * @param int $count * @param int $offset @@ -343,14 +342,14 @@ class Relation * @return array * @throws Exception */ - public static function listFollows(int $cid, array $fields = [], array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false) + public static function listFollows(int $cid, array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false) { $condition = DBA::mergeConditions($condition, ['`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`)', $cid] ); - return DI::dba()->selectToArray('contact', $fields, $condition, + return DI::dba()->selectToArray('contact', [], $condition, ['limit' => [$offset, $count], 'order' => [$shuffle ? 'RAND()' : 'name']] ); } @@ -377,7 +376,6 @@ class Relation * Returns a paginated list of contacts that follow the provided public contact. * * @param int $cid Public contact id - * @param array $field Field list * @param array $condition Additional condition on the contact table * @param int $count * @param int $offset @@ -385,17 +383,104 @@ class Relation * @return array * @throws Exception */ - public static function listFollowers(int $cid, array $fields = [], array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false) + public static function listFollowers(int $cid, array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false) { $condition = DBA::mergeConditions($condition, ['`id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`)', $cid] ); - return DI::dba()->selectToArray('contact', $fields, $condition, + return DI::dba()->selectToArray('contact', [], $condition, ['limit' => [$offset, $count], 'order' => [$shuffle ? 'RAND()' : 'name']] ); } + /** + * Counts the number of contacts that are known mutuals with the provided public contact. + * + * @param int $cid Public contact id + * @param array $condition Additional condition array on the contact table + * @return int + * @throws Exception + */ + public static function countMutuals(int $cid, array $condition = []) + { + $condition = DBA::mergeConditions($condition, + ['`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`) + AND `id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`)', + $cid, $cid] + ); + + return DI::dba()->count('contact', $condition); + } + + /** + * Returns a paginated list of contacts that are known mutuals with the provided public contact. + * + * @param int $cid Public contact id + * @param array $condition Additional condition on the contact table + * @param int $count + * @param int $offset + * @param bool $shuffle + * @return array + * @throws Exception + */ + public static function listMutuals(int $cid, array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false) + { + $condition = DBA::mergeConditions($condition, + ['`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`) + AND `id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`)', + $cid, $cid] + ); + + return DI::dba()->selectToArray('contact', [], $condition, + ['limit' => [$offset, $count], 'order' => [$shuffle ? 'name' : 'RAND()']] + ); + } + + + /** + * Counts the number of contacts with any relationship with the provided public contact. + * + * @param int $cid Public contact id + * @param array $condition Additional condition array on the contact table + * @return int + * @throws Exception + */ + public static function countAll(int $cid, array $condition = []) + { + $condition = DBA::mergeConditions($condition, + ['`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`) + OR `id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`)', + $cid, $cid] + ); + + return DI::dba()->count('contact', $condition); + } + + /** + * Returns a paginated list of contacts with any relationship with the provided public contact. + * + * @param int $cid Public contact id + * @param array $condition Additional condition on the contact table + * @param int $count + * @param int $offset + * @param bool $shuffle + * @return array + * @throws Exception + */ + public static function listAll(int $cid, array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false) + { + $condition = DBA::mergeConditions($condition, + ['`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`) + OR `id` IN (SELECT `cid` FROM `contact-relation` WHERE `relation-cid` = ? AND `follows`)', + $cid, $cid] + ); + + return DI::dba()->selectToArray('contact', [], $condition, + ['limit' => [$offset, $count], 'order' => [$shuffle ? 'name' : 'RAND()']] + ); + } + /** * Counts the number of contacts that both provided public contacts have interacted with at least once. * Interactions include follows and likes and comments on public posts. @@ -423,7 +508,6 @@ class Relation * * @param int $sourceId Public contact id * @param int $targetId Public contact id - * @param array $field Field list * @param array $condition Additional condition on the contact table * @param int $count * @param int $offset @@ -434,8 +518,8 @@ class Relation public static function listCommon(int $sourceId, int $targetId, array $condition = [], int $count = 30, int $offset = 0, bool $shuffle = false) { $condition = DBA::mergeConditions($condition, - ["`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`) - AND `id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ? AND `follows`)", + ["`id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ?) + AND `id` IN (SELECT `relation-cid` FROM `contact-relation` WHERE `cid` = ?)", $sourceId, $targetId] ); @@ -444,7 +528,6 @@ class Relation ); } - /** * Counts the number of contacts that are followed by both provided public contacts. * From f5ea07c73118560ee485aa669e23768eb597a00a Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Thu, 6 Aug 2020 10:31:40 -0400 Subject: [PATCH 202/573] Remove unused App parameter from Module\Contact::getTabsHTML --- mod/common.php | 2 +- src/Module/AllFriends.php | 2 +- src/Module/Contact.php | 10 +++++----- src/Module/Contact/Advanced.php | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mod/common.php b/mod/common.php index d4ee97c4ff..fe63a653be 100644 --- a/mod/common.php +++ b/mod/common.php @@ -120,7 +120,7 @@ function common_content(App $a) $title = ''; $tab_str = ''; if ($cmd === 'loc' && $cid && local_user() == $uid) { - $tab_str = Module\Contact::getTabsHTML($a, $contact, 5); + $tab_str = Module\Contact::getTabsHTML($contact, 5); } else { $title = DI::l10n()->t('Common Friends'); } diff --git a/src/Module/AllFriends.php b/src/Module/AllFriends.php index f41b8b0d71..9f1152f26e 100644 --- a/src/Module/AllFriends.php +++ b/src/Module/AllFriends.php @@ -72,7 +72,7 @@ class AllFriends extends BaseModule return DI::l10n()->t('No friends to display.'); } - $tab_str = Contact::getTabsHTML($app, $contact, 4); + $tab_str = Contact::getTabsHTML($contact, 4); $entries = []; foreach ($friends as $friend) { diff --git a/src/Module/Contact.php b/src/Module/Contact.php index 750a927e88..3085bbd0b8 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -531,7 +531,7 @@ class Contact extends BaseModule $nettype = DI::l10n()->t('Network type: %s', ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol'])); // tabs - $tab_str = self::getTabsHTML($a, $contact, 3); + $tab_str = self::getTabsHTML($contact, 3); $lost_contact = (($contact['archive'] && $contact['term-date'] > DBA::NULL_DATETIME && $contact['term-date'] < DateTimeFormat::utcNow()) ? DI::l10n()->t('Communications lost with this contact!') : ''); @@ -855,14 +855,14 @@ class Contact extends BaseModule * * Available Pages are 'Status', 'Profile', 'Contacts' and 'Common Friends' * - * @param App $a * @param array $contact The contact array * @param int $active_tab 1 if tab should be marked as active * * @return string HTML string of the contact page tabs buttons. * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \ImagickException */ - public static function getTabsHTML($a, $contact, $active_tab) + public static function getTabsHTML(array $contact, int $active_tab) { $cid = $pcid = $contact['id']; $data = Model\Contact::getPublicAndUserContacID($contact['id'], local_user()); @@ -964,7 +964,7 @@ class Contact extends BaseModule $contact = DBA::selectFirst('contact', ['uid', 'url', 'id'], ['id' => $contact_id, 'deleted' => false]); if (!$update) { - $o .= self::getTabsHTML($a, $contact, 1); + $o .= self::getTabsHTML($contact, 1); } if (DBA::isResult($contact)) { @@ -988,7 +988,7 @@ class Contact extends BaseModule { $contact = DBA::selectFirst('contact', ['uid', 'url', 'id'], ['id' => $contact_id, 'deleted' => false]); - $o = self::getTabsHTML($a, $contact, 2); + $o = self::getTabsHTML($contact, 2); if (DBA::isResult($contact)) { DI::page()['aside'] = ''; diff --git a/src/Module/Contact/Advanced.php b/src/Module/Contact/Advanced.php index be1e874a57..2ba7fb185a 100644 --- a/src/Module/Contact/Advanced.php +++ b/src/Module/Contact/Advanced.php @@ -125,7 +125,7 @@ class Advanced extends BaseModule $remote_self_options = ['0' => DI::l10n()->t('No mirroring'), '2' => DI::l10n()->t('Mirror as my own posting')]; } - $tab_str = Contact::getTabsHTML(DI::app(), $contact, 6); + $tab_str = Contact::getTabsHTML($contact, 6); $tpl = Renderer::getMarkupTemplate('contact/advanced.tpl'); return Renderer::replaceMacros($tpl, [ From 383ddb10ed0184cdbd628b91ff73cc07b214b1c2 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Thu, 6 Aug 2020 10:34:11 -0400 Subject: [PATCH 203/573] Create new page_tabs template - Replace explicit tabs in profile contacts page with new template --- src/BaseModule.php | 36 +++++++++++++++++++ src/Module/Profile/Common.php | 15 +++----- src/Module/Profile/Contacts.php | 11 ++---- view/templates/page_tabs.tpl | 1 + view/templates/profile/contacts.tpl | 11 ++---- view/theme/frio/templates/common_tabs.tpl | 4 +-- view/theme/frio/templates/page_tabs.tpl | 7 ++++ .../theme/frio/templates/profile/contacts.tpl | 21 ++--------- 8 files changed, 57 insertions(+), 49 deletions(-) create mode 100644 view/templates/page_tabs.tpl create mode 100644 view/theme/frio/templates/page_tabs.tpl diff --git a/src/BaseModule.php b/src/BaseModule.php index 0e0fedb80c..ce7774bfd0 100644 --- a/src/BaseModule.php +++ b/src/BaseModule.php @@ -171,4 +171,40 @@ abstract class BaseModule throw new \Friendica\Network\HTTPException\ForbiddenException(); } } + + protected static function getContactFilterTabs(string $baseUrl, string $current, bool $displayCommonTab) + { + $tabs = [ + [ + 'label' => DI::l10n()->t('All contacts'), + 'url' => $baseUrl . '/contacts', + 'sel' => !$current || $current == 'all' ? 'active' : '', + ], + [ + 'label' => DI::l10n()->t('Followers'), + 'url' => $baseUrl . '/contacts/followers', + 'sel' => $current == 'followers' ? 'active' : '', + ], + [ + 'label' => DI::l10n()->t('Following'), + 'url' => $baseUrl . '/contacts/following', + 'sel' => $current == 'following' ? 'active' : '', + ], + [ + 'label' => DI::l10n()->t('Mutual friends'), + 'url' => $baseUrl . '/contacts/mutuals', + 'sel' => $current == 'mutuals' ? 'active' : '', + ], + ]; + + if ($displayCommonTab) { + $tabs[] = [ + 'label' => DI::l10n()->t('Common'), + 'url' => $baseUrl . '/contacts/common', + 'sel' => $current == 'common' ? 'active' : '', + ]; + } + + return $tabs; + } } diff --git a/src/Module/Profile/Common.php b/src/Module/Profile/Common.php index 0b08d327c9..a9d00fce5f 100644 --- a/src/Module/Profile/Common.php +++ b/src/Module/Profile/Common.php @@ -53,8 +53,6 @@ class Common extends BaseProfile throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.')); } - $o = self::getTabsHTML($a, 'contacts', false, $nickname); - if (!empty($a->profile['hide-friends'])) { throw new HTTPException\ForbiddenException(DI::l10n()->t('Permission denied.')); } @@ -65,6 +63,10 @@ class Common extends BaseProfile $a->redirect('profile/' . $nickname . '/contacts'); }; + $o = self::getTabsHTML($a, 'contacts', false, $nickname); + + $tabs = self::getContactFilterTabs('profile/' . $nickname, 'common', $displayCommonTab); + $sourceId = Contact::getIdForURL(Profile::getMyURL()); $targetId = Contact::getPublicIdByUserId($a->profile['uid']); @@ -92,15 +94,8 @@ class Common extends BaseProfile $o .= Renderer::replaceMacros($tpl, [ '$title' => $title, '$desc' => $desc, - '$nickname' => $nickname, - '$type' => 'common', - '$displayCommonTab' => $displayCommonTab, + '$tabs' => $tabs, - '$all_label' => DI::l10n()->t('All contacts'), - '$followers_label' => DI::l10n()->t('Followers'), - '$following_label' => DI::l10n()->t('Following'), - '$mutuals_label' => DI::l10n()->t('Mutual friends'), - '$common_label' => DI::l10n()->t('Common'), '$noresult_label' => DI::l10n()->t('No common contacts.'), '$contacts' => $contacts, diff --git a/src/Module/Profile/Contacts.php b/src/Module/Profile/Contacts.php index 938dbd3d05..6981b43a42 100644 --- a/src/Module/Profile/Contacts.php +++ b/src/Module/Profile/Contacts.php @@ -61,6 +61,8 @@ class Contacts extends Module\BaseProfile $o = self::getTabsHTML($a, 'contacts', $is_owner, $nickname); + $tabs = self::getContactFilterTabs('profile/' . $nickname, $type, Session::isAuthenticated() && $a->profile['uid'] != local_user()); + $condition = [ 'uid' => $a->profile['uid'], 'blocked' => false, @@ -113,15 +115,8 @@ class Contacts extends Module\BaseProfile $o .= Renderer::replaceMacros($tpl, [ '$title' => $title, '$desc' => $desc, - '$nickname' => $nickname, - '$type' => $type, - '$displayCommonTab' => Session::isAuthenticated() && $a->profile['uid'] != local_user(), + '$tabs' => $tabs, - '$all_label' => DI::l10n()->t('All contacts'), - '$followers_label' => DI::l10n()->t('Followers'), - '$following_label' => DI::l10n()->t('Following'), - '$mutuals_label' => DI::l10n()->t('Mutual friends'), - '$common_label' => DI::l10n()->t('Common'), '$noresult_label' => DI::l10n()->t('No contacts.'), '$contacts' => $contacts, diff --git a/view/templates/page_tabs.tpl b/view/templates/page_tabs.tpl new file mode 100644 index 0000000000..d5bf9c5761 --- /dev/null +++ b/view/templates/page_tabs.tpl @@ -0,0 +1 @@ +{{include file="common_tabs.tpl" tabs=$tabs}} \ No newline at end of file diff --git a/view/templates/profile/contacts.tpl b/view/templates/profile/contacts.tpl index 4a1379f17b..7e2ae9e83d 100644 --- a/view/templates/profile/contacts.tpl +++ b/view/templates/profile/contacts.tpl @@ -5,15 +5,8 @@

      {{$desc nofilter}}

      {{/if}} - + {{include file="page_tabs.tpl" tabs=$tabs}} + {{if $contacts}}
      {{foreach $contacts as $contact}} diff --git a/view/theme/frio/templates/common_tabs.tpl b/view/theme/frio/templates/common_tabs.tpl index 76c6db0398..27ae11686b 100644 --- a/view/theme/frio/templates/common_tabs.tpl +++ b/view/theme/frio/templates/common_tabs.tpl @@ -4,7 +4,7 @@