diff --git a/database.sql b/database.sql index ab8c300a8..891b830a4 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2022.12-dev (Giant Rhubarb) --- DB_UPDATE_VERSION 1486 +-- DB_UPDATE_VERSION 1487 -- ------------------------------------------ @@ -1577,6 +1577,7 @@ CREATE TABLE IF NOT EXISTS `profile` ( `education` text COMMENT 'Deprecated', `contact` text COMMENT 'Deprecated', `homepage` varchar(255) NOT NULL DEFAULT '' COMMENT '', + `homepage_verified` boolean NOT NULL DEFAULT '0' COMMENT 'was the homepage verified by a rel-me link back to the profile', `xmpp` varchar(255) NOT NULL DEFAULT '' COMMENT 'XMPP address', `matrix` varchar(255) NOT NULL DEFAULT '' COMMENT 'Matrix address', `photo` varbinary(383) NOT NULL DEFAULT '' COMMENT '', @@ -2674,6 +2675,7 @@ CREATE VIEW `owner-view` AS SELECT `profile`.`postal-code` AS `postal-code`, `profile`.`country-name` AS `country-name`, `profile`.`homepage` AS `homepage`, + `profile`.`homepage_verified` AS `homepage_verified`, `profile`.`dob` AS `dob` FROM `user` INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid` AND `contact`.`self` diff --git a/doc/database/db_profile.md b/doc/database/db_profile.md index c180332c5..ec47b94ec 100644 --- a/doc/database/db_profile.md +++ b/doc/database/db_profile.md @@ -6,51 +6,52 @@ user profiles data Fields ------ -| Field | Description | Type | Null | Key | Default | Extra | -| ------------ | --------------------------------------------- | ------------------ | ---- | --- | ---------- | -------------- | -| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment | -| uid | Owner User id | mediumint unsigned | NO | | 0 | | -| profile-name | Deprecated | varchar(255) | YES | | NULL | | -| is-default | Deprecated | boolean | YES | | NULL | | -| hide-friends | Hide friend list from viewers of this profile | boolean | NO | | 0 | | -| name | | varchar(255) | NO | | | | -| pdesc | Deprecated | varchar(255) | YES | | NULL | | -| dob | Day of birth | varchar(32) | NO | | 0000-00-00 | | -| address | | varchar(255) | NO | | | | -| locality | | varchar(255) | NO | | | | -| region | | varchar(255) | NO | | | | -| postal-code | | varchar(32) | NO | | | | -| country-name | | varchar(255) | NO | | | | -| hometown | Deprecated | varchar(255) | YES | | NULL | | -| gender | Deprecated | varchar(32) | YES | | NULL | | -| marital | Deprecated | varchar(255) | YES | | NULL | | -| with | Deprecated | text | YES | | NULL | | -| howlong | Deprecated | datetime | YES | | NULL | | -| sexual | Deprecated | varchar(255) | YES | | NULL | | -| politic | Deprecated | varchar(255) | YES | | NULL | | -| religion | Deprecated | varchar(255) | YES | | NULL | | -| pub_keywords | | text | YES | | NULL | | -| prv_keywords | | text | YES | | NULL | | -| likes | Deprecated | text | YES | | NULL | | -| dislikes | Deprecated | text | YES | | NULL | | -| about | Profile description | text | YES | | NULL | | -| summary | Deprecated | varchar(255) | YES | | NULL | | -| music | Deprecated | text | YES | | NULL | | -| book | Deprecated | text | YES | | NULL | | -| tv | Deprecated | text | YES | | NULL | | -| film | Deprecated | text | YES | | NULL | | -| interest | Deprecated | text | YES | | NULL | | -| romance | Deprecated | text | YES | | NULL | | -| work | Deprecated | text | YES | | NULL | | -| education | Deprecated | text | YES | | NULL | | -| contact | Deprecated | text | YES | | NULL | | -| homepage | | varchar(255) | NO | | | | -| xmpp | XMPP address | varchar(255) | NO | | | | -| matrix | Matrix address | varchar(255) | NO | | | | -| photo | | varbinary(383) | NO | | | | -| thumb | | varbinary(383) | NO | | | | -| publish | publish default profile in local directory | boolean | NO | | 0 | | -| net-publish | publish profile in global directory | boolean | NO | | 0 | | +| Field | Description | Type | Null | Key | Default | Extra | +| ----------------- | -------------------------------------------------------------- | ------------------ | ---- | --- | ---------- | -------------- | +| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment | +| uid | Owner User id | mediumint unsigned | NO | | 0 | | +| profile-name | Deprecated | varchar(255) | YES | | NULL | | +| is-default | Deprecated | boolean | YES | | NULL | | +| hide-friends | Hide friend list from viewers of this profile | boolean | NO | | 0 | | +| name | | varchar(255) | NO | | | | +| pdesc | Deprecated | varchar(255) | YES | | NULL | | +| dob | Day of birth | varchar(32) | NO | | 0000-00-00 | | +| address | | varchar(255) | NO | | | | +| locality | | varchar(255) | NO | | | | +| region | | varchar(255) | NO | | | | +| postal-code | | varchar(32) | NO | | | | +| country-name | | varchar(255) | NO | | | | +| hometown | Deprecated | varchar(255) | YES | | NULL | | +| gender | Deprecated | varchar(32) | YES | | NULL | | +| marital | Deprecated | varchar(255) | YES | | NULL | | +| with | Deprecated | text | YES | | NULL | | +| howlong | Deprecated | datetime | YES | | NULL | | +| sexual | Deprecated | varchar(255) | YES | | NULL | | +| politic | Deprecated | varchar(255) | YES | | NULL | | +| religion | Deprecated | varchar(255) | YES | | NULL | | +| pub_keywords | | text | YES | | NULL | | +| prv_keywords | | text | YES | | NULL | | +| likes | Deprecated | text | YES | | NULL | | +| dislikes | Deprecated | text | YES | | NULL | | +| about | Profile description | text | YES | | NULL | | +| summary | Deprecated | varchar(255) | YES | | NULL | | +| music | Deprecated | text | YES | | NULL | | +| book | Deprecated | text | YES | | NULL | | +| tv | Deprecated | text | YES | | NULL | | +| film | Deprecated | text | YES | | NULL | | +| interest | Deprecated | text | YES | | NULL | | +| romance | Deprecated | text | YES | | NULL | | +| work | Deprecated | text | YES | | NULL | | +| education | Deprecated | text | YES | | NULL | | +| contact | Deprecated | text | YES | | NULL | | +| homepage | | varchar(255) | NO | | | | +| homepage_verified | was the homepage verified by a rel-me link back to the profile | boolean | NO | | 0 | | +| xmpp | XMPP address | varchar(255) | NO | | | | +| matrix | Matrix address | varchar(255) | NO | | | | +| photo | | varbinary(383) | NO | | | | +| thumb | | varbinary(383) | NO | | | | +| publish | publish default profile in local directory | boolean | NO | | 0 | | +| net-publish | publish profile in global directory | boolean | NO | | 0 | | Indexes ------------ diff --git a/src/Module/Settings/Profile/Index.php b/src/Module/Settings/Profile/Index.php index 14fe613c1..52f61efd4 100644 --- a/src/Module/Settings/Profile/Index.php +++ b/src/Module/Settings/Profile/Index.php @@ -38,6 +38,7 @@ use Friendica\Module\Security\Login; use Friendica\Network\HTTPException; use Friendica\Util\DateTimeFormat; use Friendica\Util\Temporal; +use Friendica\Core\Worker; class Index extends BaseSettings { @@ -127,6 +128,8 @@ class Index extends BaseSettings DI::userSession()->getLocalUserId() ); + Worker::add(Worker::PRIORITY_MEDIUM, 'CheckRelMeProfileLink', DI::userSession()->getLocalUserId()); + if (!$result) { DI::sysmsg()->addNotice(DI::l10n()->t('Profile couldn\'t be updated.')); return; @@ -210,6 +213,12 @@ class Index extends BaseSettings $personal_account = ($profile['account-type'] != User::ACCOUNT_TYPE_COMMUNITY); + if ($profile['homepage_verified']) { + $homepage_help_text = DI::l10n()->t('The homepage is verified. A rel="me" link back to your Friendica profile page was found on the homepage.'); + } else { + $homepage_help_text = DI::l10n()->t('To verify your homepage, add a rel="me" link to it, pointing to your profile URL (%s).', $profile['url']); + } + $tpl = Renderer::getMarkupTemplate('settings/profile/index.tpl'); $o .= Renderer::replaceMacros($tpl, [ '$personal_account' => $personal_account, @@ -245,7 +254,7 @@ class Index extends BaseSettings '$age' => ((intval($profile['dob'])) ? '(' . DI::l10n()->t('Age: ') . DI::l10n()->tt('%d year old', '%d years old', Temporal::getAgeByTimezone($profile['dob'], $profile['timezone'])) . ')' : ''), '$xmpp' => ['xmpp', DI::l10n()->t('XMPP (Jabber) address:'), $profile['xmpp'], DI::l10n()->t('The XMPP address will be published so that people can follow you there.')], '$matrix' => ['matrix', DI::l10n()->t('Matrix (Element) address:'), $profile['matrix'], DI::l10n()->t('The Matrix address will be published so that people can follow you there.')], - '$homepage' => ['homepage', DI::l10n()->t('Homepage URL:'), $profile['homepage']], + '$homepage' => ['homepage', DI::l10n()->t('Homepage URL:'), $profile['homepage'], $homepage_help_text], '$pub_keywords' => ['pub_keywords', DI::l10n()->t('Public Keywords:'), $profile['pub_keywords'], DI::l10n()->t('(Used for suggesting potential friends, can be seen by others)')], '$prv_keywords' => ['prv_keywords', DI::l10n()->t('Private Keywords:'), $profile['prv_keywords'], DI::l10n()->t('(Used for searching profiles, never shown to others)')], '$custom_fields_description' => DI::l10n()->t("
Custom fields appear on your profile page.
diff --git a/src/Worker/CheckRelMeProfileLink.php b/src/Worker/CheckRelMeProfileLink.php new file mode 100644 index 000000000..e5727cc83 --- /dev/null +++ b/src/Worker/CheckRelMeProfileLink.php @@ -0,0 +1,98 @@ +. + * + */ + +namespace Friendica\Worker; + +use DOMDocument; +use Friendica\DI; +use Friendica\Core\Logger; +use Friendica\Model\Profile; +use Friendica\Model\User; +use Friendica\Network\HTTPClient\Client\HttpClientAccept; +use Friendica\Network\HTTPClient\Client\HttpClientOptions; +use Friendica\Util\Network; +use Friendica\Util\Strings; + +/* This class is used to verify the homepage link of a user profile. + * To do so, we look for rel="me" links in the given homepage, if one + * of them points to the Friendica profile of the user, a verification + * mark is added to the link. + * + * To reverse the process, if a homepage link is given, it is displayed + * with the rel="me" attribute as well, so that 3rd party tools can + * verify the connection between the two pages. + * + * This task will be performed by the worker on a daily basis _and_ every + * time the user changes their homepage link. In the first case the priority + * of the task is set to LOW, with the second case it is MEDIUM. + * + * rel-me microformat docs https://microformats.org/wiki/rel-me + */ +class CheckRelMeProfileLink +{ + /* Cheks the homepage of a profile for a rel-me link back to the user profile + * + * @param $uid (int) the UID of the user + */ + public static function execute(int $uid) + { + Logger::notice('Verifying the homepage', ['uid' => $uid]); + Profile::update(['homepage_verified' => false], $uid); + $homepageUrlVerified = false; + $owner = User::getOwnerDataById($uid); + if (!empty($owner['homepage'])) { + $xrd_timeout = DI::config()->get('system', 'xrd_timeout'); + $curlResult = DI::httpClient()->get($owner['homepage'], $accept_content = HttpClientAccept::HTML, [HttpClientOptions::TIMEOUT => $xrd_timeout]); + if ($curlResult->isSuccess()) { + $content = $curlResult->getBody(); + if (!$content) { + Logger::notice('Empty body of the fetched homepage link). Cannot verify the relation to profile of UID %s.', ['uid' => $uid, 'owner homepage' => $owner['homepage']]); + } else { + $doc = new DOMDocument(); + $doc->loadHTML($content); + if (!$doc) { + Logger::notice('Could not parse the content'); + } else { + foreach ($doc->getElementsByTagName('a') as $link) { + $rel = $link->getAttribute('rel'); + if ($rel == 'me') { + $href = $link->getAttribute('href'); + if (!$homepageUrlVerified && Network::isValidHttpUrl($href)) { + $homepageUrlVerified = Strings::compareLink($owner['url'], $href); + } + } + } + } + if ($homepageUrlVerified) { + Profile::update(['homepage_verified' => true], $uid); + Logger::notice('Homepage URL verified', ['uid' => $uid, 'owner homepage' => $owner['homepage']]); + } else { + Logger::notice('Homepage URL could not be verified', ['uid' => $uid, 'owner homepage' => $owner['homepage']]); + } + } + } else { + Logger::notice('Could not cURL the homepage URL', ['owner homepage' => $owner['homepage']]); + } + } else { + Logger::notice('The user has no homepage link.', ['uid' => $uid]); + } + } +} diff --git a/src/Worker/Cron.php b/src/Worker/Cron.php index 21d2f8a20..e0ef134a2 100644 --- a/src/Worker/Cron.php +++ b/src/Worker/Cron.php @@ -27,6 +27,7 @@ use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Tag; +use Friendica\Model\User; use Friendica\Protocol\ActivityPub\Queue; use Friendica\Protocol\Relay; @@ -85,6 +86,7 @@ class Cron // Hourly cron calls if (DI::config()->get('system', 'last_cron_hourly', 0) + 3600 < time()) { + // Update trending tags cache for the community page Tag::setLocalTrendingHashtags(24, 20); Tag::setGlobalTrendingHashtags(24, 20); @@ -132,6 +134,10 @@ class Cron Worker::add(Worker::PRIORITY_LOW, 'OptimizeTables'); } + foreach (User::getList(1, PHP_INT_MAX, 'active') as $user) { + Worker::add(Worker::PRIORITY_LOW, 'CheckRelMeProfileLink', $user['uid']); + } + // Resubscribe to relay servers Relay::reSubscribe(); diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 262d7d307..79b1539f8 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -55,7 +55,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1486); + define('DB_UPDATE_VERSION', 1487); } return [ @@ -1580,6 +1580,7 @@ return [ "education" => ["type" => "text", "comment" => "Deprecated"], "contact" => ["type" => "text", "comment" => "Deprecated"], "homepage" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], + "homepage_verified" => ["type" => "boolean", "not null" => 1, "default" => "0", "comment" => "was the homepage verified by a rel-me link back to the profile"], "xmpp" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "XMPP address"], "matrix" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Matrix address"], "photo" => ["type" => "varbinary(383)", "not null" => "1", "default" => "", "comment" => ""], diff --git a/static/dbview.config.php b/static/dbview.config.php index c12cccb4c..925f82462 100644 --- a/static/dbview.config.php +++ b/static/dbview.config.php @@ -919,6 +919,7 @@ "postal-code" => ["profile", "postal-code"], "country-name" => ["profile", "country-name"], "homepage" => ["profile", "homepage"], + "homepage_verified" => ["profile", "homepage_verified"], "dob" => ["profile", "dob"], ], "query" => "FROM `user` diff --git a/view/lang/C/messages.po b/view/lang/C/messages.po index 1ec18f46b..7fcf6e217 100644 --- a/view/lang/C/messages.po +++ b/view/lang/C/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 2022.12-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-11-05 19:31-0400\n" +"POT-Creation-Date: 2022-11-07 11:21+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAMECustom fields appear on your profile page.
\n" diff --git a/view/templates/profile/vcard.tpl b/view/templates/profile/vcard.tpl index fed6fb251..a304a9a1f 100644 --- a/view/templates/profile/vcard.tpl +++ b/view/templates/profile/vcard.tpl @@ -39,7 +39,7 @@ {{if $updated}} {{/if}} - {{if $homepage}}