]> git.mxchange.org Git - friendica.git/commitdiff
Merge pull request #11684 from MrPetovan/bug/11651-ap-fetch-queue
authorPhilipp <admin+Github@philipp.info>
Sun, 26 Jun 2022 11:36:19 +0000 (13:36 +0200)
committerGitHub <noreply@github.com>
Sun, 26 Jun 2022 11:36:19 +0000 (13:36 +0200)
Create ActivityPub\FetchQueue and ActivityPub\FetchQueueItem classes

25 files changed:
database.sql
doc/database/db_2fa_trusted_browser.md
src/Core/Cache/Type/MemcacheCache.php
src/Core/Cache/Type/MemcachedCache.php
src/Model/User/Cookie.php
src/Module/Security/Logout.php
src/Module/Security/TwoFactor/SignOut.php [new file with mode: 0644]
src/Module/Security/TwoFactor/Trust.php [new file with mode: 0644]
src/Module/Security/TwoFactor/Verify.php
src/Module/Settings/TwoFactor/Index.php
src/Module/Settings/TwoFactor/Trusted.php
src/Protocol/OStatus.php
src/Security/Authentication.php
src/Security/TwoFactor/Factory/TrustedBrowser.php
src/Security/TwoFactor/Model/TrustedBrowser.php
static/dbstructure.config.php
static/routes.config.php
tests/src/Core/Cache/CacheTest.php
tests/src/Security/TwoFactor/Factory/TrustedBrowserTest.php
tests/src/Security/TwoFactor/Model/TrustedBrowserTest.php
view/lang/C/messages.po
view/templates/settings/twofactor/trusted_browsers.tpl
view/templates/twofactor/signout.tpl [new file with mode: 0644]
view/templates/twofactor/trust.tpl [new file with mode: 0644]
view/templates/twofactor/verify.tpl

index 10b18d4cae9e993baea6c60319ef9f41585545f7..01bd84b00b28ed58bc76f467e1478fbc3fba5d58 100644 (file)
@@ -1,6 +1,6 @@
 -- ------------------------------------------
 -- Friendica 2022.09-dev (Giant Rhubarb)
--- DB_UPDATE_VERSION 1472
+-- DB_UPDATE_VERSION 1473
 -- ------------------------------------------
 
 
@@ -297,6 +297,7 @@ CREATE TABLE IF NOT EXISTS `2fa_trusted_browser` (
        `cookie_hash` varchar(80) NOT NULL COMMENT 'Trusted cookie hash',
        `uid` mediumint unsigned NOT NULL COMMENT 'User ID',
        `user_agent` text COMMENT 'User agent string',
+       `trusted` boolean NOT NULL DEFAULT '1' COMMENT 'Whenever this browser should be trusted or not',
        `created` datetime NOT NULL COMMENT 'Datetime the trusted browser was recorded',
        `last_used` datetime COMMENT 'Datetime the trusted browser was last used',
         PRIMARY KEY(`cookie_hash`),
index d12d9e1fc0bbe38dc033521b1a59bb0ff17b9185..18126b49fc9f07d1f806128a6bf3488a620e4b4f 100644 (file)
@@ -6,13 +6,14 @@ Two-factor authentication trusted browsers
 Fields
 ------
 
-| Field       | Description                                | Type               | Null | Key | Default | Extra |
-| ----------- | ------------------------------------------ | ------------------ | ---- | --- | ------- | ----- |
-| cookie_hash | Trusted cookie hash                        | varchar(80)        | NO   | PRI | NULL    |       |
-| uid         | User ID                                    | mediumint unsigned | NO   |     | NULL    |       |
-| user_agent  | User agent string                          | text               | YES  |     | NULL    |       |
-| created     | Datetime the trusted browser was recorded  | datetime           | NO   |     | NULL    |       |
-| last_used   | Datetime the trusted browser was last used | datetime           | YES  |     | NULL    |       |
+| Field       | Description                                    | Type               | Null | Key | Default | Extra |
+| ----------- | ---------------------------------------------- | ------------------ | ---- | --- | ------- | ----- |
+| cookie_hash | Trusted cookie hash                            | varchar(80)        | NO   | PRI | NULL    |       |
+| uid         | User ID                                        | mediumint unsigned | NO   |     | NULL    |       |
+| user_agent  | User agent string                              | text               | YES  |     | NULL    |       |
+| trusted     | Whenever this browser should be trusted or not | boolean            | NO   |     | 1       |       |
+| created     | Datetime the trusted browser was recorded      | datetime           | NO   |     | NULL    |       |
+| last_used   | Datetime the trusted browser was last used     | datetime           | YES  |     | NULL    |       |
 
 Indexes
 ------------
index 441c64c3a0bb01fce61299280d2abc30f6d59f84..225c338911715c06205f2eae3c4100f07652bcc4 100644 (file)
@@ -68,6 +68,17 @@ class MemcacheCache extends AbstractCache implements ICanCacheInMemory
                }
        }
 
+       /**
+        * Memcache doesn't allow spaces in keys
+        *
+        * @param string $key
+        * @return string
+        */
+       protected function getCacheKey(string $key): string
+       {
+               return str_replace(' ', '_', parent::getCacheKey($key));
+       }
+
        /**
         * (@inheritdoc)
         */
index d86906de7672fb0bcc2e05af60afe0dcb3c869b7..2d8b4e1c2f99d47a1540ddca7d8aecfe09e7fd1d 100644 (file)
@@ -93,6 +93,17 @@ class MemcachedCache extends AbstractCache implements ICanCacheInMemory
                }
        }
 
+       /**
+        * Memcached doesn't allow spaces in keys
+        *
+        * @param string $key
+        * @return string
+        */
+       protected function getCacheKey(string $key): string
+       {
+               return str_replace(' ', '_', parent::getCacheKey($key));
+       }
+
        /**
         * (@inheritdoc)
         */
index aabb0282094f35780ed39779a31d7a9797498c4a..4359d21071d73923185fc69ac9b9e2118e54c141 100644 (file)
@@ -124,6 +124,19 @@ class Cookie
                }
        }
 
+       /**
+        * Resets the cookie to a given data set
+        *
+        * @param array $data
+        *
+        * @return bool
+        */
+       public function reset(array $data): bool
+       {
+               return $this->clear() &&
+                          $this->setMultiple($data);
+       }
+
        /**
         * Clears the Friendica cookie
         */
@@ -131,7 +144,7 @@ class Cookie
        {
                $this->data = [];
                // make sure cookie is deleted on browser close, as a security measure
-               return $this->setCookie( '', -3600, $this->sslEnabled);
+               return $this->setCookie('', -3600, $this->sslEnabled);
        }
 
        /**
@@ -161,7 +174,7 @@ class Cookie
         *
         */
        protected function setCookie(string $value = null, int $expire = null,
-                                    bool $secure = null): bool
+                                                                bool $secure = null): bool
        {
                return setcookie(self::NAME, $value, $expire, self::PATH, self::DOMAIN, $secure, self::HTTPONLY);
        }
index 1ec076483440dc0658954148c9dff97dc1f6677e..004292cb5cce85d87812d9a0c879d8521aab6f1c 100644 (file)
@@ -31,7 +31,6 @@ use Friendica\Core\System;
 use Friendica\Model\Profile;
 use Friendica\Model\User\Cookie;
 use Friendica\Module\Response;
-use Friendica\Security\TwoFactor;
 use Friendica\Util\Profiler;
 use Psr\Log\LoggerInterface;
 
@@ -46,17 +45,14 @@ class Logout extends BaseModule
        protected $cookie;
        /** @var IHandleSessions */
        protected $session;
-       /** @var TwoFactor\Repository\TrustedBrowser */
-       protected $trustedBrowserRepo;
 
-       public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, TwoFactor\Repository\TrustedBrowser $trustedBrowserRepo, ICanCache $cache, Cookie $cookie, IHandleSessions $session, array $server, array $parameters = [])
+       public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, ICanCache $cache, Cookie $cookie, IHandleSessions $session, array $server, array $parameters = [])
        {
                parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
 
-               $this->cache              = $cache;
-               $this->cookie             = $cookie;
-               $this->session            = $session;
-               $this->trustedBrowserRepo = $trustedBrowserRepo;
+               $this->cache   = $cache;
+               $this->cookie  = $cookie;
+               $this->session = $session;
        }
 
 
@@ -73,9 +69,9 @@ class Logout extends BaseModule
 
                Hook::callAll("logging_out");
 
-               // Remove this trusted browser as it won't be able to be used ever again after the cookie is cleared
-               if ($this->cookie->get('trusted')) {
-                       $this->trustedBrowserRepo->removeForUser(local_user(), $this->cookie->get('trusted'));
+               // If this is a trusted browser, redirect to the 2fa signout page
+               if ($this->cookie->get('2fa_cookie_hash')) {
+                       $this->baseUrl->redirect('2fa/signout');
                }
 
                $this->cookie->clear();
diff --git a/src/Module/Security/TwoFactor/SignOut.php b/src/Module/Security/TwoFactor/SignOut.php
new file mode 100644 (file)
index 0000000..20b7f03
--- /dev/null
@@ -0,0 +1,129 @@
+<?php
+/**
+ * @copyright Copyright (C) 2010-2022, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\Security\TwoFactor;
+
+use Friendica\App;
+use Friendica\BaseModule;
+use Friendica\Core\L10n;
+use Friendica\Core\Renderer;
+use Friendica\Core\Session\Capability\IHandleSessions;
+use Friendica\Model\User\Cookie;
+use Friendica\Module\Response;
+use Friendica\Network\HTTPException\NotFoundException;
+use Friendica\Util\Profiler;
+use Friendica\Security\TwoFactor;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Page 4: Logout dialog for trusted browsers
+ *
+ * @package Friendica\Module\TwoFactor
+ */
+class SignOut extends BaseModule
+{
+       protected $errors = [];
+
+       /** @var IHandleSessions  */
+       protected $session;
+       /** @var Cookie  */
+       protected $cookie;
+       /** @var TwoFactor\Repository\TrustedBrowser  */
+       protected $trustedBrowserRepository;
+
+       public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger,  IHandleSessions $session, Cookie $cookie, TwoFactor\Repository\TrustedBrowser $trustedBrowserRepository, Profiler $profiler, Response $response, array $server, array $parameters = [])
+       {
+               parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
+
+               $this->session                  = $session;
+               $this->cookie                   = $cookie;
+               $this->trustedBrowserRepository = $trustedBrowserRepository;
+       }
+
+       protected function post(array $request = [])
+       {
+               if (!local_user() || !($this->cookie->get('2fa_cookie_hash'))) {
+                       return;
+               }
+
+               $action = $request['action'] ?? '';
+
+               if (!empty($action)) {
+                       self::checkFormSecurityTokenRedirectOnError('2fa', 'twofactor_signout');
+
+                       switch ($action) {
+                               case 'trust_and_sign_out':
+                                       $trusted = $this->cookie->get('2fa_cookie_hash');
+                                       $this->cookie->reset(['2fa_cookie_hash' => $trusted]);
+                                       $this->session->clear();
+
+                                       info($this->t('Logged out.'));
+                                       $this->baseUrl->redirect();
+                                       break;
+                               case 'sign_out':
+                                       $this->trustedBrowserRepository->removeForUser(local_user(), $this->cookie->get('2fa_cookie_hash'));
+                                       $this->cookie->clear();
+                                       $this->session->clear();
+
+                                       info($this->t('Logged out.'));
+                                       $this->baseUrl->redirect();
+                                       break;
+                               default:
+                                       $this->baseUrl->redirect();
+                       }
+               }
+       }
+
+       protected function content(array $request = []): string
+       {
+               if (!local_user() || !($this->cookie->get('2fa_cookie_hash'))) {
+                       $this->baseUrl->redirect();
+               }
+
+               try {
+                       $trustedBrowser = $this->trustedBrowserRepository->selectOneByHash($this->cookie->get('2fa_cookie_hash'));
+                       if (!$trustedBrowser->trusted) {
+                               $trusted = $this->cookie->get('2fa_cookie_hash');
+                               $this->cookie->reset(['2fa_cookie_hash' => $trusted]);
+                               $this->session->clear();
+
+                               info($this->t('Logged out.'));
+                               $this->baseUrl->redirect();
+                       }
+               } catch (NotFoundException $exception) {
+                       $this->cookie->clear();
+                       $this->session->clear();
+
+                       info($this->t('Logged out.'));
+                       $this->baseUrl->redirect();
+               }
+
+               return Renderer::replaceMacros(Renderer::getMarkupTemplate('twofactor/signout.tpl'), [
+                       '$form_security_token' => self::getFormSecurityToken('twofactor_signout'),
+
+                       '$title'                    => $this->t('Sign out of this browser?'),
+                       '$message'                  => $this->t('<p>If you trust this browser, you will not be asked for verification code the next time you sign in.</p>'),
+                       '$sign_out_label'           => $this->t('Sign out'),
+                       '$cancel_label'             => $this->t('Cancel'),
+                       '$trust_and_sign_out_label' => $this->t('Trust and sign out'),
+               ]);
+       }
+}
diff --git a/src/Module/Security/TwoFactor/Trust.php b/src/Module/Security/TwoFactor/Trust.php
new file mode 100644 (file)
index 0000000..a245e6b
--- /dev/null
@@ -0,0 +1,126 @@
+<?php
+/**
+ * @copyright Copyright (C) 2010-2022, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\Security\TwoFactor;
+
+use Friendica\App;
+use Friendica\BaseModule;
+use Friendica\Core\L10n;
+use Friendica\Core\Renderer;
+use Friendica\Core\Session\Capability\IHandleSessions;
+use Friendica\Model\User;
+use Friendica\Model\User\Cookie;
+use Friendica\Module\Response;
+use Friendica\Network\HTTPException\NotFoundException;
+use Friendica\Security\Authentication;
+use Friendica\Util\Profiler;
+use Friendica\Security\TwoFactor;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Page 2: Trust Browser dialog
+ *
+ * @package Friendica\Module\TwoFactor
+ */
+class Trust extends BaseModule
+{
+       /** @var App  */
+       protected $app;
+       /** @var Authentication  */
+       protected $auth;
+       /** @var IHandleSessions  */
+       protected $session;
+       /** @var Cookie  */
+       protected $cookie;
+       /** @var TwoFactor\Factory\TrustedBrowser  */
+       protected $trustedBrowserFactory;
+       /** @var TwoFactor\Repository\TrustedBrowser  */
+       protected $trustedBrowserRepositoy;
+
+       public function __construct(App $app, Authentication $auth, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, IHandleSessions $session, Cookie $cookie, TwoFactor\Factory\TrustedBrowser $trustedBrowserFactory, TwoFactor\Repository\TrustedBrowser $trustedBrowserRepositoy, Response $response, array $server, array $parameters = [])
+       {
+               parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
+
+               $this->app                     = $app;
+               $this->auth                    = $auth;
+               $this->session                 = $session;
+               $this->cookie                  = $cookie;
+               $this->trustedBrowserFactory   = $trustedBrowserFactory;
+               $this->trustedBrowserRepositoy = $trustedBrowserRepositoy;
+       }
+
+       protected function post(array $request = [])
+       {
+               if (!local_user() || !$this->session->get('2fa')) {
+                       return;
+               }
+
+               $action = $request['action'] ?? '';
+
+               if (!empty($action)) {
+                       self::checkFormSecurityTokenRedirectOnError('2fa', 'twofactor_trust');
+
+                       switch ($action) {
+                               case 'trust':
+                               case 'dont_trust':
+                                       $trustedBrowser = $this->trustedBrowserFactory->createForUserWithUserAgent(local_user(), $this->server['HTTP_USER_AGENT'], $action === 'trust');
+                                       $this->trustedBrowserRepositoy->save($trustedBrowser);
+
+                                       // The string is sent to the browser to be sent back with each request
+                                       if (!$this->cookie->set('2fa_cookie_hash', $trustedBrowser->cookie_hash)) {
+                                               notice($this->t('Couldn\'t save browser to Cookie.'));
+                                       };
+                                       break;
+                       }
+
+                       $this->auth->setForUser($this->app, User::getById($this->app->getLoggedInUserId()), true, true);
+               }
+       }
+
+       protected function content(array $request = []): string
+       {
+               if (!local_user() || !$this->session->get('2fa')) {
+                       $this->baseUrl->redirect();
+               }
+
+               if ($this->cookie->get('2fa_cookie_hash')) {
+                       try {
+                               $trustedBrowser = $this->trustedBrowserRepositoy->selectOneByHash($this->cookie->get('2fa_cookie_hash'));
+                               if (!$trustedBrowser->trusted) {
+                                       $this->auth->setForUser($this->app, User::getById($this->app->getLoggedInUserId()), true, true);
+                                       $this->baseUrl->redirect();
+                               }
+                       } catch (NotFoundException $exception) {
+                               $this->logger->notice('Trusted Browser of the cookie not found.', ['cookie_hash' => $this->cookie->get('trusted'), 'uid' => $this->app->getLoggedInUserId(), 'exception' => $exception]);
+                       }
+               }
+
+               return Renderer::replaceMacros(Renderer::getMarkupTemplate('twofactor/trust.tpl'), [
+                       '$form_security_token' => self::getFormSecurityToken('twofactor_trust'),
+
+                       '$title'            => $this->t('Trust this browser?'),
+                       '$message'          => $this->t('<p>If you choose to trust this browser, you will not be asked for a verification code the next time you sign in.</p>'),
+                       '$not_now_label'    => $this->t('Not now'),
+                       '$dont_trust_label' => $this->t('Don\'t trust'),
+                       '$trust_label'      => $this->t('Trust'),
+               ]);
+       }
+}
index c6d129432077c46bee513425e0b072c9d36edef7..d0850aedcbae63006d665b8002f51f5f5b16c48a 100644 (file)
 
 namespace Friendica\Module\Security\TwoFactor;
 
+use Friendica\App;
 use Friendica\BaseModule;
+use Friendica\Core\L10n;
+use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
 use Friendica\Core\Renderer;
-use Friendica\Core\Session;
-use Friendica\DI;
-use Friendica\Model\User;
+use Friendica\Core\Session\Capability\IHandleSessions;
+use Friendica\Module\Response;
+use Friendica\Util\Profiler;
 use PragmaRX\Google2FA\Google2FA;
 use Friendica\Security\TwoFactor;
+use Psr\Log\LoggerInterface;
 
 /**
  * Page 1: Authenticator code verification
@@ -36,7 +40,20 @@ use Friendica\Security\TwoFactor;
  */
 class Verify extends BaseModule
 {
-       private static $errors = [];
+       protected $errors = [];
+
+       /** @var IHandleSessions  */
+       protected $session;
+       /** @var IManagePersonalConfigValues  */
+       protected $pConfig;
+
+       public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IManagePersonalConfigValues $pConfig, IHandleSessions $session, array $server, array $parameters = [])
+       {
+               parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
+
+               $this->session = $session;
+               $this->pConfig = $pConfig;
+       }
 
        protected function post(array $request = [])
        {
@@ -44,36 +61,20 @@ class Verify extends BaseModule
                        return;
                }
 
-               if (($_POST['action'] ?? '') == 'verify') {
+               if (($request['action'] ?? '') === 'verify') {
                        self::checkFormSecurityTokenRedirectOnError('2fa', 'twofactor_verify');
 
-                       $a = DI::app();
+                       $code = $request['verify_code'] ?? '';
 
-                       $code = $_POST['verify_code'] ?? '';
-
-                       $valid = (new Google2FA())->verifyKey(DI::pConfig()->get(local_user(), '2fa', 'secret'), $code);
+                       $valid = (new Google2FA())->verifyKey($this->pConfig->get(local_user(), '2fa', 'secret'), $code);
 
                        // The same code can't be used twice even if it's valid
-                       if ($valid && Session::get('2fa') !== $code) {
-                               Session::set('2fa', $code);
-
-                               // Trust this browser feature
-                               if (!empty($_REQUEST['trust_browser'])) {
-                                       $trustedBrowserFactory = new TwoFactor\Factory\TrustedBrowser(DI::logger());
-                                       $trustedBrowserRepository = new TwoFactor\Repository\TrustedBrowser(DI::dba(), DI::logger(), $trustedBrowserFactory);
-
-                                       $trustedBrowser = $trustedBrowserFactory->createForUserWithUserAgent(local_user(), $_SERVER['HTTP_USER_AGENT']);
-
-                                       $trustedBrowserRepository->save($trustedBrowser);
-
-                                       // The string is sent to the browser to be sent back with each request
-                                       DI::cookie()->set('trusted', $trustedBrowser->cookie_hash);
-                               }
+                       if ($valid && $this->session->get('2fa') !== $code) {
+                               $this->session->set('2fa', $code);
 
-                               // Resume normal login workflow
-                               DI::auth()->setForUser($a, User::getById($a->getLoggedInUserId()), true, true);
+                               $this->baseUrl->redirect('2fa/trust');
                        } else {
-                               self::$errors[] = DI::l10n()->t('Invalid code, please retry.');
+                               $this->errors[] = $this->t('Invalid code, please retry.');
                        }
                }
        }
@@ -81,25 +82,24 @@ class Verify extends BaseModule
        protected function content(array $request = []): string
        {
                if (!local_user()) {
-                       DI::baseUrl()->redirect();
+                       $this->baseUrl->redirect();
                }
 
                // Already authenticated with 2FA token
-               if (Session::get('2fa')) {
-                       DI::baseUrl()->redirect();
+               if ($this->session->get('2fa')) {
+                       $this->baseUrl->redirect();
                }
 
                return Renderer::replaceMacros(Renderer::getMarkupTemplate('twofactor/verify.tpl'), [
                        '$form_security_token' => self::getFormSecurityToken('twofactor_verify'),
 
-                       '$title'            => DI::l10n()->t('Two-factor authentication'),
-                       '$message'          => DI::l10n()->t('<p>Open the two-factor authentication app on your device to get an authentication code and verify your identity.</p>'),
-                       '$errors_label'     => DI::l10n()->tt('Error', 'Errors', count(self::$errors)),
-                       '$errors'           => self::$errors,
-                       '$recovery_message' => DI::l10n()->t('Don’t have your phone? <a href="%s">Enter a two-factor recovery code</a>', '2fa/recovery'),
-                       '$verify_code'      => ['verify_code', DI::l10n()->t('Please enter a code from your authentication app'), '', '', DI::l10n()->t('Required'), 'autofocus autocomplete="off" placeholder="000000"', 'tel'],
-                       '$trust_browser'    => ['trust_browser', DI::l10n()->t('This is my two-factor authenticator app device'), !empty($_REQUEST['trust_browser'])],
-                       '$verify_label'     => DI::l10n()->t('Verify code and complete login'),
+                       '$title'            => $this->t('Two-factor authentication'),
+                       '$message'          => $this->t('<p>Open the two-factor authentication app on your device to get an authentication code and verify your identity.</p>'),
+                       '$errors_label'     => $this->tt('Error', 'Errors', count($this->errors)),
+                       '$errors'           => $this->errors,
+                       '$recovery_message' => $this->t('If you do not have access to your authentication code you can use a <a href="%s">two-factor recovery code</a>.', '2fa/recovery'),
+                       '$verify_code'      => ['verify_code', $this->t('Please enter a code from your authentication app'), '', '', $this->t('Required'), 'autofocus autocomplete="one-time-code" placeholder="000000" inputmode="numeric" pattern="[0-9]*"'],
+                       '$verify_label'     => $this->t('Verify code and complete login'),
                ]);
        }
 }
index 35c5d3cf9c19abd30ff9cecf18cbf6c54812c8dc..0da49f3177a5d1ff5820880d89f37df2a0c9df91 100644 (file)
@@ -24,6 +24,7 @@ namespace Friendica\Module\Settings\TwoFactor;
 use Friendica\Core\Renderer;
 use Friendica\Core\Session;
 use Friendica\DI;
+use Friendica\Network\HTTPException\FoundException;
 use Friendica\Security\TwoFactor\Model\AppSpecificPassword;
 use Friendica\Security\TwoFactor\Model\RecoveryCode;
 use Friendica\Model\User;
@@ -44,8 +45,8 @@ class Index extends BaseSettings
                try {
                        User::getIdFromPasswordAuthentication(local_user(), $_POST['password'] ?? '');
 
-                       $has_secret = (bool) DI::pConfig()->get(local_user(), '2fa', 'secret');
-                       $verified = DI::pConfig()->get(local_user(), '2fa', 'verified');
+                       $has_secret = (bool)DI::pConfig()->get(local_user(), '2fa', 'secret');
+                       $verified   = DI::pConfig()->get(local_user(), '2fa', 'verified');
 
                        switch ($_POST['action'] ?? '') {
                                case 'enable':
@@ -54,7 +55,8 @@ class Index extends BaseSettings
 
                                                DI::pConfig()->set(local_user(), '2fa', 'secret', $Google2FA->generateSecretKey(32));
 
-                                               DI::baseUrl()->redirect('settings/2fa/recovery?t=' . self::getFormSecurityToken('settings_2fa_password'));
+                                               DI::baseUrl()
+                                                 ->redirect('settings/2fa/recovery?t=' . self::getFormSecurityToken('settings_2fa_password'));
                                        }
                                        break;
                                case 'disable':
@@ -70,27 +72,33 @@ class Index extends BaseSettings
                                        break;
                                case 'recovery':
                                        if ($has_secret) {
-                                               DI::baseUrl()->redirect('settings/2fa/recovery?t=' . self::getFormSecurityToken('settings_2fa_password'));
+                                               DI::baseUrl()
+                                                 ->redirect('settings/2fa/recovery?t=' . self::getFormSecurityToken('settings_2fa_password'));
                                        }
                                        break;
                                case 'app_specific':
                                        if ($has_secret) {
-                                               DI::baseUrl()->redirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password'));
+                                               DI::baseUrl()
+                                                 ->redirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password'));
                                        }
                                        break;
                                case 'trusted':
                                        if ($has_secret) {
-                                               DI::baseUrl()->redirect('settings/2fa/trusted?t=' . self::getFormSecurityToken('settings_2fa_password'));
+                                               DI::baseUrl()
+                                                 ->redirect('settings/2fa/trusted?t=' . self::getFormSecurityToken('settings_2fa_password'));
                                        }
                                        break;
                                case 'configure':
                                        if (!$verified) {
-                                               DI::baseUrl()->redirect('settings/2fa/verify?t=' . self::getFormSecurityToken('settings_2fa_password'));
+                                               DI::baseUrl()
+                                                 ->redirect('settings/2fa/verify?t=' . self::getFormSecurityToken('settings_2fa_password'));
                                        }
                                        break;
                        }
+               } catch (FoundException $exception) {
+                       // Nothing to do here
                } catch (\Exception $e) {
-                       notice(DI::l10n()->t('Wrong Password'));
+                       notice(DI::l10n()->t($e->getMessage()));
                }
        }
 
index 12327a5918961af3875de186faa5d6b48acb23f7..d1c0476de5dca115c4abdf956b801f9349914988 100644 (file)
@@ -121,6 +121,7 @@ class Trusted extends BaseSettings
                                'os' => $result->os->family,
                                'device' => $result->device->family,
                                'browser' => $result->ua->family,
+                               'trusted_labeled' => $trustedBrowser->trusted ? $this->t('Yes') : $this->t('No'),
                        ];
 
                        return $trustedBrowser->toArray() + $dates + $uaData;
@@ -135,7 +136,8 @@ class Trusted extends BaseSettings
                        '$device_label'        => $this->t('Device'),
                        '$os_label'            => $this->t('OS'),
                        '$browser_label'       => $this->t('Browser'),
-                       '$created_label'       => $this->t('Trusted'),
+                       '$trusted_label'       => $this->t('Trusted'),
+                       '$created_label'       => $this->t('Created At'),
                        '$last_used_label'     => $this->t('Last Use'),
                        '$remove_label'        => $this->t('Remove'),
                        '$remove_all_label'    => $this->t('Remove All'),
index 90e52d673db1f2314487d5d220ce33adb837c353..36dbb06c8446db7eae69b6cb67173a3adc177177 100644 (file)
@@ -2017,7 +2017,7 @@ class OStatus
         * cache or it is empty
         *
         * @param string  $owner_nick  Nickname of the feed owner
-        * @param string  $last_update Date of the last update
+        * @param string  $last_update Date of the last update (in "Y-m-d H:i:s" format)
         * @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
index aca4f2c23e1132ef1fe3e5effd5428d2315cb85b..a23d0c95579c29359a9615729a573467817e3e67 100644 (file)
@@ -144,7 +144,7 @@ class Authentication
                                // Renew the cookie
                                $this->cookie->send();
 
-                               // Do the authentification if not done by now
+                               // Do the authentication if not done by now
                                if (!$this->session->get('authenticated')) {
                                        $this->setForUser($a, $user);
 
@@ -269,7 +269,11 @@ class Authentication
                }
 
                if (!$remember) {
+                       $trusted = $this->cookie->get('2fa_cookie_hash') ?? null;
                        $this->cookie->clear();
+                       if ($trusted) {
+                               $this->cookie->set('2fa_cookie_hash', $trusted);
+                       }
                }
 
                // if we haven't failed up this point, log them in.
@@ -407,11 +411,11 @@ class Authentication
                }
 
                // Case 1b: Check for trusted browser
-               if ($this->cookie->get('trusted')) {
+               if ($this->cookie->get('2fa_cookie_hash')) {
                        // Retrieve a trusted_browser model based on cookie hash
                        $trustedBrowserRepository = new TrustedBrowser($this->dba, $this->logger);
                        try {
-                               $trustedBrowser = $trustedBrowserRepository->selectOneByHash($this->cookie->get('trusted'));
+                               $trustedBrowser = $trustedBrowserRepository->selectOneByHash($this->cookie->get('2fa_cookie_hash'));
                                // Verify record ownership
                                if ($trustedBrowser->uid === $uid) {
                                        // Update last_used date
@@ -420,10 +424,13 @@ class Authentication
                                        // Save it to the database
                                        $trustedBrowserRepository->save($trustedBrowser);
 
-                                       // Set 2fa session key and return
-                                       $this->session->set('2fa', true);
+                                       // Only use this entry, if its really trusted, otherwise just update the record and proceed
+                                       if ($trustedBrowser->trusted) {
+                                               // Set 2fa session key and return
+                                               $this->session->set('2fa', true);
 
-                                       return;
+                                               return;
+                                       }
                                } else {
                                        // Invalid trusted cookie value, removing it
                                        $this->cookie->unset('trusted');
index 61ec154fcc23b9989085bd2c86c26751289a6c5e..21961f7ef368b54b5067d0a8182046bdcb3690c5 100644 (file)
@@ -27,7 +27,7 @@ use Friendica\Util\Strings;
 
 class TrustedBrowser extends BaseFactory
 {
-       public function createForUserWithUserAgent($uid, $userAgent): \Friendica\Security\TwoFactor\Model\TrustedBrowser
+       public function createForUserWithUserAgent(int $uid, string $userAgent, bool $trusted): \Friendica\Security\TwoFactor\Model\TrustedBrowser
        {
                $trustedHash = Strings::getRandomHex();
 
@@ -35,6 +35,7 @@ class TrustedBrowser extends BaseFactory
                        $trustedHash,
                        $uid,
                        $userAgent,
+                       $trusted,
                        DateTimeFormat::utcNow()
                );
        }
@@ -45,6 +46,7 @@ class TrustedBrowser extends BaseFactory
                        $row['cookie_hash'],
                        $row['uid'],
                        $row['user_agent'],
+                       $row['trusted'],
                        $row['created'],
                        $row['last_used']
                );
index d0a654d5f282045884654fa833636b855e1bdc44..cd9d2007e6f18fd02a3ac2f4b91b633d0a2e2ed8 100644 (file)
@@ -31,6 +31,7 @@ use Friendica\Util\DateTimeFormat;
  * @property-read $cookie_hash
  * @property-read $uid
  * @property-read $user_agent
+ * @property-read $trusted
  * @property-read $created
  * @property-read $last_used
  * @package Friendica\Model\TwoFactor
@@ -40,6 +41,7 @@ class TrustedBrowser extends BaseEntity
        protected $cookie_hash;
        protected $uid;
        protected $user_agent;
+       protected $trusted;
        protected $created;
        protected $last_used;
 
@@ -51,16 +53,18 @@ class TrustedBrowser extends BaseEntity
         * @param string      $cookie_hash
         * @param int         $uid
         * @param string      $user_agent
+        * @param bool        $trusted
         * @param string      $created
         * @param string|null $last_used
         */
-       public function __construct(string $cookie_hash, int $uid, string $user_agent, string $created, string $last_used = null)
+       public function __construct(string $cookie_hash, int $uid, string $user_agent, bool $trusted, string $created, string $last_used = null)
        {
                $this->cookie_hash = $cookie_hash;
-               $this->uid = $uid;
-               $this->user_agent = $user_agent;
-               $this->created = $created;
-               $this->last_used = $last_used;
+               $this->uid         = $uid;
+               $this->user_agent  = $user_agent;
+               $this->trusted     = $trusted;
+               $this->created     = $created;
+               $this->last_used   = $last_used;
        }
 
        public function recordUse()
index c48da6a09a12f39985e40d60bf29a09b3c3e5403..758c33d0dac1791b3b1e779b77c54db5739022b9 100644 (file)
@@ -55,7 +55,7 @@
 use Friendica\Database\DBA;
 
 if (!defined('DB_UPDATE_VERSION')) {
-       define('DB_UPDATE_VERSION', 1472);
+       define('DB_UPDATE_VERSION', 1473);
 }
 
 return [
@@ -358,6 +358,7 @@ return [
                        "cookie_hash" => ["type" => "varchar(80)", "not null" => "1", "primary" => "1", "comment" => "Trusted cookie hash"],
                        "uid" => ["type" => "mediumint unsigned", "not null" => "1", "foreign" => ["user" => "uid"], "comment" => "User ID"],
                        "user_agent" => ["type" => "text", "comment" => "User agent string"],
+                       "trusted" => ["type" => "boolean", "not null" => "1", "default" => "1", "comment" => "Whenever this browser should be trusted or not"],
                        "created" => ["type" => "datetime", "not null" => "1", "comment" => "Datetime the trusted browser was recorded"],
                        "last_used" => ["type" => "datetime", "comment" => "Datetime the trusted browser was last used"],
                ],
index 72964d23080ed74b2cb9da199c8e63bad45837e2..dac56a1255a36316a11db24b1900865ed62bbe1e 100644 (file)
@@ -165,6 +165,8 @@ return [
        '/2fa' => [
                '[/]'       => [Module\Security\TwoFactor\Verify::class,   [R::GET, R::POST]],
                '/recovery' => [Module\Security\TwoFactor\Recovery::class, [R::GET, R::POST]],
+               '/trust'    => [Module\Security\TwoFactor\Trust::class,    [R::GET, R::POST]],
+               '/signout'  => [Module\Security\TwoFactor\SignOut::class,  [R::GET, R::POST]],
        ],
 
        '/api' => [
index 88154c9d22cd899d7e4989e3a36de8641fedca6f..c249aefbfa11fd2710e334a8299dfd91d3b38185 100644 (file)
@@ -237,4 +237,13 @@ abstract class CacheTest extends MockedTest
                self::assertNotContains('value1', $list);
                self::assertNotContains('value2', $list);
        }
+
+       /**
+        * @small
+        */
+       public function testSpaceInKey()
+       {
+               self::assertTrue($this->instance->set('key space', 'value'));
+               self::assertEquals('value', $this->instance->get('key space'));
+       }
 }
index 84a118eb80b003b57690e5c324718a49168c0377..7bb07a13351cb561066461b56c01c05e7bf3e520 100644 (file)
@@ -38,6 +38,7 @@ class TrustedBrowserTest extends MockedTest
                        'uid' => 42,
                        'user_agent' => 'PHPUnit',
                        'created' => DateTimeFormat::utcNow(),
+                       'trusted' => true,
                        'last_used' => null,
                ];
 
@@ -57,6 +58,7 @@ class TrustedBrowserTest extends MockedTest
                        'uid' => null,
                        'user_agent' => null,
                        'created' => null,
+                       'trusted' => true,
                        'last_used' => null,
                ];
 
@@ -69,14 +71,15 @@ class TrustedBrowserTest extends MockedTest
        {
                $factory = new TrustedBrowser(new NullLogger());
 
-               $uid = 42;
+               $uid       = 42;
                $userAgent = 'PHPUnit';
 
-               $trustedBrowser = $factory->createForUserWithUserAgent($uid, $userAgent);
+               $trustedBrowser = $factory->createForUserWithUserAgent($uid, $userAgent, true);
 
                $this->assertNotEmpty($trustedBrowser->cookie_hash);
                $this->assertEquals($uid, $trustedBrowser->uid);
                $this->assertEquals($userAgent, $trustedBrowser->user_agent);
+               $this->assertTrue($trustedBrowser->trusted);
                $this->assertNotEmpty($trustedBrowser->created);
        }
 }
index 0c91ea5e5ea90a2707f87567b38ba26f72d94a87..cf5db0ffd8a6183c77d498ee2aa8de89b0d77dea 100644 (file)
@@ -36,12 +36,14 @@ class TrustedBrowserTest extends MockedTest
                        $hash,
                        42,
                        'PHPUnit',
+                       true,
                        DateTimeFormat::utcNow()
                );
 
                $this->assertEquals($hash, $trustedBrowser->cookie_hash);
                $this->assertEquals(42, $trustedBrowser->uid);
                $this->assertEquals('PHPUnit', $trustedBrowser->user_agent);
+               $this->assertTrue($trustedBrowser->trusted);
                $this->assertNotEmpty($trustedBrowser->created);
        }
 
@@ -54,6 +56,7 @@ class TrustedBrowserTest extends MockedTest
                        $hash,
                        42,
                        'PHPUnit',
+                       true,
                        $past,
                        $past
                );
index 333a0aaa66acd5264edd90a4635a74a96fabeeac..3a29f776845193a99a63b3816e5cfb4803d4b1bf 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: 2022.09-dev\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2022-06-13 05:45+0000\n"
+"POT-Creation-Date: 2022-06-25 22:37+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -28,7 +28,7 @@ msgid "Access denied."
 msgstr ""
 
 #: mod/cal.php:63 mod/cal.php:80 mod/photos.php:69 mod/photos.php:140
-#: mod/photos.php:798 src/Model/Profile.php:231 src/Module/Feed.php:72
+#: mod/photos.php:798 src/Model/Profile.php:232 src/Module/Feed.php:72
 #: src/Module/HCard.php:52 src/Module/Profile/Common.php:41
 #: src/Module/Profile/Common.php:52 src/Module/Profile/Contacts.php:40
 #: src/Module/Profile/Contacts.php:50 src/Module/Profile/Media.php:38
@@ -62,21 +62,21 @@ msgstr ""
 msgid "Next"
 msgstr ""
 
-#: mod/cal.php:249 mod/events.php:383 src/Model/Event.php:457
+#: mod/cal.php:249 mod/events.php:383 src/Model/Event.php:456
 msgid "today"
 msgstr ""
 
-#: mod/cal.php:250 mod/events.php:384 src/Model/Event.php:458
+#: mod/cal.php:250 mod/events.php:384 src/Model/Event.php:457
 #: src/Util/Temporal.php:334
 msgid "month"
 msgstr ""
 
-#: mod/cal.php:251 mod/events.php:385 src/Model/Event.php:459
+#: mod/cal.php:251 mod/events.php:385 src/Model/Event.php:458
 #: src/Util/Temporal.php:335
 msgid "week"
 msgstr ""
 
-#: mod/cal.php:252 mod/events.php:386 src/Model/Event.php:460
+#: mod/cal.php:252 mod/events.php:386 src/Model/Event.php:459
 #: src/Util/Temporal.php:336
 msgid "day"
 msgstr ""
@@ -106,7 +106,7 @@ msgstr ""
 
 #: mod/display.php:143 mod/photos.php:802
 #: src/Module/Conversation/Community.php:175 src/Module/Directory.php:49
-#: src/Module/Search/Index.php:50
+#: src/Module/Search/Index.php:65
 msgid "Public access denied."
 msgstr ""
 
@@ -159,13 +159,13 @@ msgstr ""
 msgid "Edit post"
 msgstr ""
 
-#: mod/editpost.php:91 mod/notes.php:56 src/Content/Text/HTML.php:875
+#: mod/editpost.php:91 mod/notes.php:56 src/Content/Text/HTML.php:882
 #: src/Module/Admin/Storage.php:142 src/Module/Filer/SaveTag.php:73
 msgid "Save"
 msgstr ""
 
 #: mod/editpost.php:92 mod/photos.php:1338 src/Content/Conversation.php:338
-#: src/Module/Contact/Poke.php:176 src/Object/Post.php:989
+#: src/Module/Contact/Poke.php:176 src/Object/Post.php:993
 msgid "Loading..."
 msgstr ""
 
@@ -231,7 +231,7 @@ msgstr ""
 #: mod/editpost.php:107 mod/message.php:200 mod/message.php:358
 #: mod/photos.php:1489 mod/wallmessage.php:142 src/Content/Conversation.php:368
 #: src/Content/Conversation.php:713 src/Module/Item/Compose.php:177
-#: src/Object/Post.php:528
+#: src/Object/Post.php:538
 msgid "Please wait"
 msgstr ""
 
@@ -263,7 +263,7 @@ msgstr ""
 
 #: mod/editpost.php:128 mod/events.php:513 mod/photos.php:1337
 #: mod/photos.php:1393 mod/photos.php:1467 src/Content/Conversation.php:383
-#: src/Module/Item/Compose.php:172 src/Object/Post.php:999
+#: src/Module/Item/Compose.php:172 src/Object/Post.php:1003
 msgid "Preview"
 msgstr ""
 
@@ -271,52 +271,53 @@ msgstr ""
 #: mod/follow.php:144 mod/photos.php:1004 mod/photos.php:1105 mod/tagrm.php:35
 #: mod/tagrm.php:127 mod/unfollow.php:97 src/Content/Conversation.php:386
 #: src/Module/Contact/Revoke.php:108 src/Module/RemoteFollow.php:127
+#: src/Module/Security/TwoFactor/Signout.php:125
 msgid "Cancel"
 msgstr ""
 
 #: mod/editpost.php:134 src/Content/Conversation.php:343
-#: src/Module/Item/Compose.php:163 src/Object/Post.php:990
+#: src/Module/Item/Compose.php:163 src/Object/Post.php:994
 msgid "Bold"
 msgstr ""
 
 #: mod/editpost.php:135 src/Content/Conversation.php:344
-#: src/Module/Item/Compose.php:164 src/Object/Post.php:991
+#: src/Module/Item/Compose.php:164 src/Object/Post.php:995
 msgid "Italic"
 msgstr ""
 
 #: mod/editpost.php:136 src/Content/Conversation.php:345
-#: src/Module/Item/Compose.php:165 src/Object/Post.php:992
+#: src/Module/Item/Compose.php:165 src/Object/Post.php:996
 msgid "Underline"
 msgstr ""
 
 #: mod/editpost.php:137 src/Content/Conversation.php:346
-#: src/Module/Item/Compose.php:166 src/Object/Post.php:993
+#: src/Module/Item/Compose.php:166 src/Object/Post.php:997
 msgid "Quote"
 msgstr ""
 
 #: mod/editpost.php:138 src/Content/Conversation.php:347
-#: src/Module/Item/Compose.php:167 src/Object/Post.php:994
+#: src/Module/Item/Compose.php:167 src/Object/Post.php:998
 msgid "Code"
 msgstr ""
 
 #: mod/editpost.php:139 src/Content/Conversation.php:349
-#: src/Module/Item/Compose.php:169 src/Object/Post.php:996
+#: src/Module/Item/Compose.php:169 src/Object/Post.php:1000
 msgid "Link"
 msgstr ""
 
 #: mod/editpost.php:140 src/Content/Conversation.php:350
-#: src/Module/Item/Compose.php:170 src/Object/Post.php:997
+#: src/Module/Item/Compose.php:170 src/Object/Post.php:1001
 msgid "Link or Media"
 msgstr ""
 
 #: mod/editpost.php:143 src/Content/Conversation.php:393
-#: src/Content/Widget/VCard.php:113 src/Model/Profile.php:462
+#: src/Content/Widget/VCard.php:113 src/Model/Profile.php:463
 #: src/Module/Admin/Logs/View.php:93
 msgid "Message"
 msgstr ""
 
 #: mod/editpost.php:144 src/Content/Conversation.php:394
-#: src/Module/Settings/TwoFactor/Trusted.php:137
+#: src/Module/Settings/TwoFactor/Trusted.php:138
 msgid "Browser"
 msgstr ""
 
@@ -366,8 +367,8 @@ msgstr ""
 #: src/Module/Install.php:286 src/Module/Install.php:291
 #: src/Module/Install.php:305 src/Module/Install.php:320
 #: src/Module/Install.php:347 src/Module/Register.php:148
-#: src/Module/Security/TwoFactor/Verify.php:100
-#: src/Module/Settings/TwoFactor/Index.php:133
+#: src/Module/Security/TwoFactor/Verify.php:101
+#: src/Module/Settings/TwoFactor/Index.php:136
 #: src/Module/Settings/TwoFactor/Verify.php:154
 msgid "Required"
 msgstr ""
@@ -386,8 +387,8 @@ msgid "Description:"
 msgstr ""
 
 #: mod/events.php:504 src/Content/Widget/VCard.php:104 src/Model/Event.php:80
-#: src/Model/Event.php:107 src/Model/Event.php:466 src/Model/Event.php:915
-#: src/Model/Profile.php:370 src/Module/Contact/Profile.php:369
+#: src/Model/Event.php:107 src/Model/Event.php:465 src/Model/Event.php:915
+#: src/Model/Profile.php:371 src/Module/Contact/Profile.php:369
 #: src/Module/Directory.php:148 src/Module/Notifications/Introductions.php:185
 #: src/Module/Profile/Profile.php:194
 msgid "Location:"
@@ -413,7 +414,7 @@ msgstr ""
 #: src/Module/Install.php:252 src/Module/Install.php:294
 #: src/Module/Install.php:331 src/Module/Invite.php:178
 #: src/Module/Item/Compose.php:162 src/Module/Profile/Profile.php:247
-#: src/Module/Settings/Profile/Index.php:222 src/Object/Post.php:988
+#: src/Module/Settings/Profile/Index.php:222 src/Object/Post.php:992
 #: view/theme/duepuntozero/config.php:69 view/theme/frio/config.php:160
 #: view/theme/quattro/config.php:71 view/theme/vier/config.php:119
 msgid "Submit"
@@ -466,8 +467,8 @@ msgstr ""
 msgid "OStatus support is disabled. Contact can't be added."
 msgstr ""
 
-#: mod/follow.php:138 src/Content/Item.php:443 src/Content/Widget.php:78
-#: src/Model/Contact.php:1102 src/Model/Contact.php:1114
+#: mod/follow.php:138 src/Content/Item.php:443 src/Content/Widget.php:80
+#: src/Model/Contact.php:1103 src/Model/Contact.php:1115
 #: view/theme/vier/theme.php:172
 msgid "Connect/Follow"
 msgstr ""
@@ -1068,16 +1069,16 @@ msgstr ""
 
 #: mod/photos.php:1333 mod/photos.php:1389 mod/photos.php:1463
 #: src/Module/Contact.php:544 src/Module/Item/Compose.php:160
-#: src/Object/Post.php:985
+#: src/Object/Post.php:989
 msgid "This is you"
 msgstr ""
 
 #: mod/photos.php:1335 mod/photos.php:1391 mod/photos.php:1465
-#: src/Object/Post.php:522 src/Object/Post.php:987
+#: src/Object/Post.php:532 src/Object/Post.php:991
 msgid "Comment"
 msgstr ""
 
-#: mod/photos.php:1424 src/Content/Conversation.php:629 src/Object/Post.php:247
+#: mod/photos.php:1424 src/Content/Conversation.php:629 src/Object/Post.php:256
 msgid "Select"
 msgstr ""
 
@@ -1087,19 +1088,19 @@ msgstr ""
 msgid "Delete"
 msgstr ""
 
-#: mod/photos.php:1486 src/Object/Post.php:369
+#: mod/photos.php:1486 src/Object/Post.php:379
 msgid "Like"
 msgstr ""
 
-#: mod/photos.php:1487 src/Object/Post.php:369
+#: mod/photos.php:1487 src/Object/Post.php:379
 msgid "I like this (toggle)"
 msgstr ""
 
-#: mod/photos.php:1488 src/Object/Post.php:370
+#: mod/photos.php:1488 src/Object/Post.php:380
 msgid "Dislike"
 msgstr ""
 
-#: mod/photos.php:1490 src/Object/Post.php:370
+#: mod/photos.php:1490 src/Object/Post.php:380
 msgid "I don't like this (toggle)"
 msgstr ""
 
@@ -1128,7 +1129,7 @@ msgstr ""
 msgid "Contact not found."
 msgstr ""
 
-#: mod/removeme.php:65 src/Navigation/Notifications/Repository/Notify.php:482
+#: mod/removeme.php:65 src/Navigation/Notifications/Repository/Notify.php:483
 msgid "[Friendica System Notify]"
 msgstr ""
 
@@ -1166,7 +1167,7 @@ msgid "Resubscribing to OStatus contacts"
 msgstr ""
 
 #: mod/repair_ostatus.php:46 src/Module/Debug/ActivityPubConversion.php:134
-#: src/Module/Debug/Babel.php:293 src/Module/Security/TwoFactor/Verify.php:97
+#: src/Module/Debug/Babel.php:293 src/Module/Security/TwoFactor/Verify.php:98
 msgid "Error"
 msgid_plural "Errors"
 msgstr[0] ""
@@ -1404,11 +1405,11 @@ msgid ""
 "hours."
 msgstr ""
 
-#: mod/suggest.php:55 src/Content/Widget.php:81 view/theme/vier/theme.php:175
+#: mod/suggest.php:55 src/Content/Widget.php:83 view/theme/vier/theme.php:175
 msgid "Friend Suggestions"
 msgstr ""
 
-#: mod/tagger.php:78 src/Content/Item.php:342 src/Model/Item.php:2699
+#: mod/tagger.php:78 src/Content/Item.php:342 src/Model/Item.php:2728
 msgid "photo"
 msgstr ""
 
@@ -1430,7 +1431,7 @@ msgid "Select a tag to remove: "
 msgstr ""
 
 #: mod/tagrm.php:126 src/Module/Settings/Delegation.php:179
-#: src/Module/Settings/TwoFactor/Trusted.php:140
+#: src/Module/Settings/TwoFactor/Trusted.php:142
 msgid "Remove"
 msgstr ""
 
@@ -1522,7 +1523,7 @@ msgstr ""
 msgid "File upload failed."
 msgstr ""
 
-#: mod/wall_upload.php:218 src/Model/Photo.php:1061
+#: mod/wall_upload.php:218 src/Model/Photo.php:1085
 msgid "Wall Photos"
 msgstr ""
 
@@ -1546,11 +1547,11 @@ msgid ""
 "your site allow private mail from unknown senders."
 msgstr ""
 
-#: src/App.php:463
+#: src/App.php:473
 msgid "No system theme config value set."
 msgstr ""
 
-#: src/App.php:584
+#: src/App.php:594
 msgid "Apologies but the website is unavailable at the moment."
 msgstr ""
 
@@ -1568,56 +1569,56 @@ msgstr ""
 msgid "toggle mobile"
 msgstr ""
 
-#: src/App/Router.php:275
+#: src/App/Router.php:282
 #, php-format
 msgid "Method not allowed for this module. Allowed method(s): %s"
 msgstr ""
 
-#: src/App/Router.php:277 src/Module/HTTPException/PageNotFound.php:34
+#: src/App/Router.php:284 src/Module/HTTPException/PageNotFound.php:49
 msgid "Page not found."
 msgstr ""
 
-#: src/App/Router.php:305
+#: src/App/Router.php:312
 msgid "You must be logged in to use addons. "
 msgstr ""
 
-#: src/BaseModule.php:377
+#: src/BaseModule.php:392
 msgid ""
 "The form security token was not correct. This probably happened because the "
 "form has been opened for too long (>3 hours) before submitting it."
 msgstr ""
 
-#: src/BaseModule.php:404
+#: src/BaseModule.php:419
 msgid "All contacts"
 msgstr ""
 
-#: src/BaseModule.php:409 src/Content/Widget.php:233 src/Core/ACL.php:194
+#: src/BaseModule.php:424 src/Content/Widget.php:235 src/Core/ACL.php:194
 #: src/Module/Contact.php:367 src/Module/PermissionTooltip.php:122
 #: src/Module/PermissionTooltip.php:144
 msgid "Followers"
 msgstr ""
 
-#: src/BaseModule.php:414 src/Content/Widget.php:234 src/Module/Contact.php:368
+#: src/BaseModule.php:429 src/Content/Widget.php:236 src/Module/Contact.php:368
 msgid "Following"
 msgstr ""
 
-#: src/BaseModule.php:419 src/Content/Widget.php:235 src/Module/Contact.php:369
+#: src/BaseModule.php:434 src/Content/Widget.php:237 src/Module/Contact.php:369
 msgid "Mutual friends"
 msgstr ""
 
-#: src/BaseModule.php:427
+#: src/BaseModule.php:442
 msgid "Common"
 msgstr ""
 
-#: src/Console/Addon.php:177 src/Console/Addon.php:202
+#: src/Console/Addon.php:175 src/Console/Addon.php:199
 msgid "Addon not found"
 msgstr ""
 
-#: src/Console/Addon.php:181
+#: src/Console/Addon.php:179
 msgid "Addon already enabled"
 msgstr ""
 
-#: src/Console/Addon.php:206
+#: src/Console/Addon.php:203
 msgid "Addon already disabled"
 msgstr ""
 
@@ -1840,78 +1841,78 @@ msgstr ""
 msgid "Monthly"
 msgstr ""
 
-#: src/Content/ContactSelector.php:123
+#: src/Content/ContactSelector.php:126
 msgid "DFRN"
 msgstr ""
 
-#: src/Content/ContactSelector.php:124
+#: src/Content/ContactSelector.php:127
 msgid "OStatus"
 msgstr ""
 
-#: src/Content/ContactSelector.php:125
+#: src/Content/ContactSelector.php:128
 msgid "RSS/Atom"
 msgstr ""
 
-#: src/Content/ContactSelector.php:126 src/Module/Admin/Users/Active.php:129
+#: src/Content/ContactSelector.php:129 src/Module/Admin/Users/Active.php:129
 #: src/Module/Admin/Users/Blocked.php:130 src/Module/Admin/Users/Create.php:73
 #: src/Module/Admin/Users/Deleted.php:88 src/Module/Admin/Users/Index.php:142
 #: src/Module/Admin/Users/Index.php:162 src/Module/Admin/Users/Pending.php:104
 msgid "Email"
 msgstr ""
 
-#: src/Content/ContactSelector.php:127 src/Module/Debug/Babel.php:307
+#: src/Content/ContactSelector.php:130 src/Module/Debug/Babel.php:307
 msgid "Diaspora"
 msgstr ""
 
-#: src/Content/ContactSelector.php:128
+#: src/Content/ContactSelector.php:131
 msgid "Zot!"
 msgstr ""
 
-#: src/Content/ContactSelector.php:129
+#: src/Content/ContactSelector.php:132
 msgid "LinkedIn"
 msgstr ""
 
-#: src/Content/ContactSelector.php:130
+#: src/Content/ContactSelector.php:133
 msgid "XMPP/IM"
 msgstr ""
 
-#: src/Content/ContactSelector.php:131
+#: src/Content/ContactSelector.php:134
 msgid "MySpace"
 msgstr ""
 
-#: src/Content/ContactSelector.php:132
+#: src/Content/ContactSelector.php:135
 msgid "Google+"
 msgstr ""
 
-#: src/Content/ContactSelector.php:133
+#: src/Content/ContactSelector.php:136
 msgid "pump.io"
 msgstr ""
 
-#: src/Content/ContactSelector.php:134
+#: src/Content/ContactSelector.php:137
 msgid "Twitter"
 msgstr ""
 
-#: src/Content/ContactSelector.php:135
+#: src/Content/ContactSelector.php:138
 msgid "Discourse"
 msgstr ""
 
-#: src/Content/ContactSelector.php:136
+#: src/Content/ContactSelector.php:139
 msgid "Diaspora Connector"
 msgstr ""
 
-#: src/Content/ContactSelector.php:137
+#: src/Content/ContactSelector.php:140
 msgid "GNU Social Connector"
 msgstr ""
 
-#: src/Content/ContactSelector.php:138
+#: src/Content/ContactSelector.php:141
 msgid "ActivityPub"
 msgstr ""
 
-#: src/Content/ContactSelector.php:139
+#: src/Content/ContactSelector.php:142
 msgid "pnut"
 msgstr ""
 
-#: src/Content/ContactSelector.php:175
+#: src/Content/ContactSelector.php:178
 #, php-format
 msgid "%s (via %s)"
 msgstr ""
@@ -2016,7 +2017,7 @@ msgid "Visible to <strong>everybody</strong>"
 msgstr ""
 
 #: src/Content/Conversation.php:308 src/Module/Item/Compose.php:171
-#: src/Object/Post.php:998
+#: src/Object/Post.php:1002
 msgid "Please enter a image/video/audio/webpage URL:"
 msgstr ""
 
@@ -2049,7 +2050,7 @@ msgid "Share"
 msgstr ""
 
 #: src/Content/Conversation.php:348 src/Module/Item/Compose.php:168
-#: src/Object/Post.php:995
+#: src/Object/Post.php:999
 msgid "Image"
 msgstr ""
 
@@ -2061,25 +2062,25 @@ msgstr ""
 msgid "Scheduled at"
 msgstr ""
 
-#: src/Content/Conversation.php:657 src/Object/Post.php:235
+#: src/Content/Conversation.php:657 src/Object/Post.php:244
 msgid "Pinned item"
 msgstr ""
 
-#: src/Content/Conversation.php:673 src/Object/Post.php:476
-#: src/Object/Post.php:477
+#: src/Content/Conversation.php:673 src/Object/Post.php:486
+#: src/Object/Post.php:487
 #, php-format
 msgid "View %s's profile @ %s"
 msgstr ""
 
-#: src/Content/Conversation.php:686 src/Object/Post.php:464
+#: src/Content/Conversation.php:686 src/Object/Post.php:474
 msgid "Categories:"
 msgstr ""
 
-#: src/Content/Conversation.php:687 src/Object/Post.php:465
+#: src/Content/Conversation.php:687 src/Object/Post.php:475
 msgid "Filed under:"
 msgstr ""
 
-#: src/Content/Conversation.php:695 src/Object/Post.php:490
+#: src/Content/Conversation.php:695 src/Object/Post.php:500
 #, php-format
 msgid "%s from %s"
 msgstr ""
@@ -2250,7 +2251,7 @@ msgid "Display membership date in profile"
 msgstr ""
 
 #: src/Content/ForumManager.php:151 src/Content/Nav.php:239
-#: src/Content/Text/HTML.php:896 src/Content/Widget.php:522
+#: src/Content/Text/HTML.php:903 src/Content/Widget.php:524
 msgid "Forums"
 msgstr ""
 
@@ -2258,12 +2259,12 @@ msgstr ""
 msgid "External link to forum"
 msgstr ""
 
-#: src/Content/ForumManager.php:156 src/Content/Widget.php:501
+#: src/Content/ForumManager.php:156 src/Content/Widget.php:503
 msgid "show less"
 msgstr ""
 
-#: src/Content/ForumManager.php:157 src/Content/Widget.php:403
-#: src/Content/Widget.php:502
+#: src/Content/ForumManager.php:157 src/Content/Widget.php:405
+#: src/Content/Widget.php:504
 msgid "show more"
 msgstr ""
 
@@ -2272,7 +2273,7 @@ msgstr ""
 msgid "%1$s poked %2$s"
 msgstr ""
 
-#: src/Content/Item.php:334 src/Model/Item.php:2697
+#: src/Content/Item.php:334 src/Model/Item.php:2726
 msgid "event"
 msgstr ""
 
@@ -2280,31 +2281,31 @@ msgstr ""
 msgid "Follow Thread"
 msgstr ""
 
-#: src/Content/Item.php:423 src/Model/Contact.php:1107
+#: src/Content/Item.php:423 src/Model/Contact.php:1108
 msgid "View Status"
 msgstr ""
 
-#: src/Content/Item.php:424 src/Content/Item.php:446 src/Model/Contact.php:1041
-#: src/Model/Contact.php:1099 src/Model/Contact.php:1108
+#: src/Content/Item.php:424 src/Content/Item.php:446 src/Model/Contact.php:1042
+#: src/Model/Contact.php:1100 src/Model/Contact.php:1109
 #: src/Module/Directory.php:158 src/Module/Settings/Profile/Index.php:225
 msgid "View Profile"
 msgstr ""
 
-#: src/Content/Item.php:425 src/Model/Contact.php:1109
+#: src/Content/Item.php:425 src/Model/Contact.php:1110
 msgid "View Photos"
 msgstr ""
 
-#: src/Content/Item.php:426 src/Model/Contact.php:1100
-#: src/Model/Contact.php:1110
+#: src/Content/Item.php:426 src/Model/Contact.php:1101
+#: src/Model/Contact.php:1111
 msgid "Network Posts"
 msgstr ""
 
-#: src/Content/Item.php:427 src/Model/Contact.php:1101
-#: src/Model/Contact.php:1111
+#: src/Content/Item.php:427 src/Model/Contact.php:1102
+#: src/Model/Contact.php:1112
 msgid "View Contact"
 msgstr ""
 
-#: src/Content/Item.php:428 src/Model/Contact.php:1112
+#: src/Content/Item.php:428 src/Model/Contact.php:1113
 msgid "Send PM"
 msgstr ""
 
@@ -2323,11 +2324,11 @@ msgstr ""
 msgid "Ignore"
 msgstr ""
 
-#: src/Content/Item.php:434 src/Object/Post.php:445
+#: src/Content/Item.php:434 src/Object/Post.php:455
 msgid "Languages"
 msgstr ""
 
-#: src/Content/Item.php:438 src/Model/Contact.php:1113
+#: src/Content/Item.php:438 src/Model/Contact.php:1114
 msgid "Poke"
 msgstr ""
 
@@ -2343,7 +2344,7 @@ msgstr ""
 msgid "Clear notifications"
 msgstr ""
 
-#: src/Content/Nav.php:96 src/Content/Text/HTML.php:883
+#: src/Content/Nav.php:96 src/Content/Text/HTML.php:890
 msgid "@name, !forum, #tags, content"
 msgstr ""
 
@@ -2366,7 +2367,7 @@ msgstr ""
 
 #: src/Content/Nav.php:190 src/Module/BaseProfile.php:56
 #: src/Module/Contact.php:433 src/Module/Contact/Profile.php:380
-#: src/Module/Settings/TwoFactor/Index.php:112 view/theme/frio/theme.php:225
+#: src/Module/Settings/TwoFactor/Index.php:115 view/theme/frio/theme.php:225
 msgid "Status"
 msgstr ""
 
@@ -2427,7 +2428,7 @@ msgstr ""
 
 #: src/Content/Nav.php:222 src/Module/Help.php:67
 #: src/Module/Settings/TwoFactor/AppSpecific.php:127
-#: src/Module/Settings/TwoFactor/Index.php:111
+#: src/Module/Settings/TwoFactor/Index.php:114
 #: src/Module/Settings/TwoFactor/Recovery.php:105
 #: src/Module/Settings/TwoFactor/Verify.php:145 view/theme/vier/theme.php:217
 msgid "Help"
@@ -2445,8 +2446,8 @@ msgstr ""
 msgid "Addon applications, utilities, games"
 msgstr ""
 
-#: src/Content/Nav.php:230 src/Content/Text/HTML.php:881
-#: src/Module/Admin/Logs/View.php:87 src/Module/Search/Index.php:97
+#: src/Content/Nav.php:230 src/Content/Text/HTML.php:888
+#: src/Module/Admin/Logs/View.php:87 src/Module/Search/Index.php:112
 msgid "Search"
 msgstr ""
 
@@ -2454,17 +2455,17 @@ msgstr ""
 msgid "Search site content"
 msgstr ""
 
-#: src/Content/Nav.php:233 src/Content/Text/HTML.php:890
+#: src/Content/Nav.php:233 src/Content/Text/HTML.php:897
 msgid "Full Text"
 msgstr ""
 
-#: src/Content/Nav.php:234 src/Content/Text/HTML.php:891
+#: src/Content/Nav.php:234 src/Content/Text/HTML.php:898
 #: src/Content/Widget/TagCloud.php:68
 msgid "Tags"
 msgstr ""
 
 #: src/Content/Nav.php:235 src/Content/Nav.php:294
-#: src/Content/Text/HTML.php:892 src/Module/BaseProfile.php:125
+#: src/Content/Text/HTML.php:899 src/Module/BaseProfile.php:125
 #: src/Module/BaseProfile.php:128 src/Module/Contact.php:370
 #: src/Module/Contact.php:464 view/theme/frio/theme.php:236
 msgid "Contacts"
@@ -2491,7 +2492,7 @@ msgstr ""
 msgid "People directory"
 msgstr ""
 
-#: src/Content/Nav.php:263 src/Module/BaseAdmin.php:85
+#: src/Content/Nav.php:263 src/Module/BaseAdmin.php:88
 msgid "Information"
 msgstr ""
 
@@ -2500,7 +2501,7 @@ msgid "Information about this friendica instance"
 msgstr ""
 
 #: src/Content/Nav.php:266 src/Module/Admin/Tos.php:76
-#: src/Module/BaseAdmin.php:96 src/Module/Register.php:176
+#: src/Module/BaseAdmin.php:99 src/Module/Register.php:176
 #: src/Module/Tos.php:87
 msgid "Terms of Service"
 msgstr ""
@@ -2572,7 +2573,7 @@ msgstr ""
 msgid "Manage/edit friends and contacts"
 msgstr ""
 
-#: src/Content/Nav.php:299 src/Module/BaseAdmin.php:126
+#: src/Content/Nav.php:299 src/Module/BaseAdmin.php:129
 msgid "Admin"
 msgstr ""
 
@@ -2612,8 +2613,8 @@ msgstr ""
 msgid "last"
 msgstr ""
 
-#: src/Content/Text/BBCode.php:990 src/Content/Text/BBCode.php:1784
-#: src/Content/Text/BBCode.php:1785
+#: src/Content/Text/BBCode.php:990 src/Content/Text/BBCode.php:1808
+#: src/Content/Text/BBCode.php:1809
 msgid "Image/photo"
 msgstr ""
 
@@ -2623,41 +2624,41 @@ msgid ""
 "<a href=\"%1$s\" target=\"_blank\" rel=\"noopener noreferrer\">%2$s</a> %3$s"
 msgstr ""
 
-#: src/Content/Text/BBCode.php:1188 src/Model/Item.php:3271
-#: src/Model/Item.php:3277 src/Model/Item.php:3278
+#: src/Content/Text/BBCode.php:1188 src/Model/Item.php:3301
+#: src/Model/Item.php:3307 src/Model/Item.php:3308
 msgid "Link to source"
 msgstr ""
 
-#: src/Content/Text/BBCode.php:1702 src/Content/Text/HTML.php:933
+#: src/Content/Text/BBCode.php:1726 src/Content/Text/HTML.php:940
 msgid "Click to open/close"
 msgstr ""
 
-#: src/Content/Text/BBCode.php:1733
+#: src/Content/Text/BBCode.php:1757
 msgid "$1 wrote:"
 msgstr ""
 
-#: src/Content/Text/BBCode.php:1789 src/Content/Text/BBCode.php:1790
+#: src/Content/Text/BBCode.php:1813 src/Content/Text/BBCode.php:1814
 msgid "Encrypted content"
 msgstr ""
 
-#: src/Content/Text/BBCode.php:2008
+#: src/Content/Text/BBCode.php:2032
 msgid "Invalid source protocol"
 msgstr ""
 
-#: src/Content/Text/BBCode.php:2023
+#: src/Content/Text/BBCode.php:2047
 msgid "Invalid link protocol"
 msgstr ""
 
-#: src/Content/Text/HTML.php:797
+#: src/Content/Text/HTML.php:805
 msgid "Loading more entries..."
 msgstr ""
 
-#: src/Content/Text/HTML.php:798
+#: src/Content/Text/HTML.php:806
 msgid "The end"
 msgstr ""
 
-#: src/Content/Text/HTML.php:875 src/Content/Widget/VCard.php:109
-#: src/Model/Profile.php:456
+#: src/Content/Text/HTML.php:882 src/Content/Widget/VCard.php:109
+#: src/Model/Profile.php:457
 msgid "Follow"
 msgstr ""
 
@@ -2677,117 +2678,117 @@ msgstr ""
 msgid "Connect"
 msgstr ""
 
-#: src/Content/Widget.php:70
+#: src/Content/Widget.php:72
 #, php-format
 msgid "%d invitation available"
 msgid_plural "%d invitations available"
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/Content/Widget.php:76 view/theme/vier/theme.php:170
+#: src/Content/Widget.php:78 view/theme/vier/theme.php:170
 msgid "Find People"
 msgstr ""
 
-#: src/Content/Widget.php:77 view/theme/vier/theme.php:171
+#: src/Content/Widget.php:79 view/theme/vier/theme.php:171
 msgid "Enter name or interest"
 msgstr ""
 
-#: src/Content/Widget.php:79 view/theme/vier/theme.php:173
+#: src/Content/Widget.php:81 view/theme/vier/theme.php:173
 msgid "Examples: Robert Morgenstein, Fishing"
 msgstr ""
 
-#: src/Content/Widget.php:80 src/Module/Contact.php:391
+#: src/Content/Widget.php:82 src/Module/Contact.php:391
 #: src/Module/Directory.php:97 view/theme/vier/theme.php:174
 msgid "Find"
 msgstr ""
 
-#: src/Content/Widget.php:82 view/theme/vier/theme.php:176
+#: src/Content/Widget.php:84 view/theme/vier/theme.php:176
 msgid "Similar Interests"
 msgstr ""
 
-#: src/Content/Widget.php:83 view/theme/vier/theme.php:177
+#: src/Content/Widget.php:85 view/theme/vier/theme.php:177
 msgid "Random Profile"
 msgstr ""
 
-#: src/Content/Widget.php:84 view/theme/vier/theme.php:178
+#: src/Content/Widget.php:86 view/theme/vier/theme.php:178
 msgid "Invite Friends"
 msgstr ""
 
-#: src/Content/Widget.php:85 src/Module/Directory.php:89
+#: src/Content/Widget.php:87 src/Module/Directory.php:89
 #: view/theme/vier/theme.php:179
 msgid "Global Directory"
 msgstr ""
 
-#: src/Content/Widget.php:87 view/theme/vier/theme.php:181
+#: src/Content/Widget.php:89 view/theme/vier/theme.php:181
 msgid "Local Directory"
 msgstr ""
 
-#: src/Content/Widget.php:209 src/Model/Group.php:570
+#: src/Content/Widget.php:211 src/Model/Group.php:583
 #: src/Module/Contact.php:354 src/Module/Welcome.php:76
 msgid "Groups"
 msgstr ""
 
-#: src/Content/Widget.php:211
+#: src/Content/Widget.php:213
 msgid "Everyone"
 msgstr ""
 
-#: src/Content/Widget.php:240
+#: src/Content/Widget.php:242
 msgid "Relationships"
 msgstr ""
 
-#: src/Content/Widget.php:242 src/Module/Contact.php:306
+#: src/Content/Widget.php:244 src/Module/Contact.php:306
 #: src/Module/Group.php:293
 msgid "All Contacts"
 msgstr ""
 
-#: src/Content/Widget.php:281
+#: src/Content/Widget.php:283
 msgid "Protocols"
 msgstr ""
 
-#: src/Content/Widget.php:283
+#: src/Content/Widget.php:285
 msgid "All Protocols"
 msgstr ""
 
-#: src/Content/Widget.php:311
+#: src/Content/Widget.php:313
 msgid "Saved Folders"
 msgstr ""
 
-#: src/Content/Widget.php:313 src/Content/Widget.php:344
+#: src/Content/Widget.php:315 src/Content/Widget.php:346
 msgid "Everything"
 msgstr ""
 
-#: src/Content/Widget.php:342
+#: src/Content/Widget.php:344
 msgid "Categories"
 msgstr ""
 
-#: src/Content/Widget.php:399
+#: src/Content/Widget.php:401
 #, php-format
 msgid "%d contact in common"
 msgid_plural "%d contacts in common"
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/Content/Widget.php:495
+#: src/Content/Widget.php:497
 msgid "Archives"
 msgstr ""
 
-#: src/Content/Widget.php:519
+#: src/Content/Widget.php:521
 msgid "Persons"
 msgstr ""
 
-#: src/Content/Widget.php:520
+#: src/Content/Widget.php:522
 msgid "Organisations"
 msgstr ""
 
-#: src/Content/Widget.php:521 src/Model/Contact.php:1537
+#: src/Content/Widget.php:523 src/Model/Contact.php:1538
 msgid "News"
 msgstr ""
 
-#: src/Content/Widget.php:525 src/Module/Settings/Account.php:455
+#: src/Content/Widget.php:527 src/Module/Settings/Account.php:455
 msgid "Account Types"
 msgstr ""
 
-#: src/Content/Widget.php:526 src/Module/Admin/BaseUsers.php:51
+#: src/Content/Widget.php:528 src/Module/Admin/BaseUsers.php:51
 msgid "All"
 msgstr ""
 
@@ -2837,22 +2838,22 @@ msgstr[1] ""
 msgid "More Trending Tags"
 msgstr ""
 
-#: src/Content/Widget/VCard.php:102 src/Model/Profile.php:375
+#: src/Content/Widget/VCard.php:102 src/Model/Profile.php:376
 #: src/Module/Contact/Profile.php:371 src/Module/Profile/Profile.php:176
 msgid "XMPP:"
 msgstr ""
 
-#: src/Content/Widget/VCard.php:103 src/Model/Profile.php:376
+#: src/Content/Widget/VCard.php:103 src/Model/Profile.php:377
 #: src/Module/Contact/Profile.php:373 src/Module/Profile/Profile.php:180
 msgid "Matrix:"
 msgstr ""
 
-#: src/Content/Widget/VCard.php:107 src/Model/Profile.php:468
+#: src/Content/Widget/VCard.php:107 src/Model/Profile.php:469
 #: src/Module/Notifications/Introductions.php:199
 msgid "Network:"
 msgstr ""
 
-#: src/Content/Widget/VCard.php:111 src/Model/Profile.php:458
+#: src/Content/Widget/VCard.php:111 src/Model/Profile.php:459
 msgid "Unfollow"
 msgstr ""
 
@@ -3213,206 +3214,206 @@ msgstr ""
 msgid "Could not connect to database."
 msgstr ""
 
-#: src/Core/L10n.php:400 src/Model/Event.php:425
+#: src/Core/L10n.php:399 src/Model/Event.php:424
 #: src/Module/Settings/Display.php:182
 msgid "Monday"
 msgstr ""
 
-#: src/Core/L10n.php:400 src/Model/Event.php:426
+#: src/Core/L10n.php:399 src/Model/Event.php:425
 msgid "Tuesday"
 msgstr ""
 
-#: src/Core/L10n.php:400 src/Model/Event.php:427
+#: src/Core/L10n.php:399 src/Model/Event.php:426
 msgid "Wednesday"
 msgstr ""
 
-#: src/Core/L10n.php:400 src/Model/Event.php:428
+#: src/Core/L10n.php:399 src/Model/Event.php:427
 msgid "Thursday"
 msgstr ""
 
-#: src/Core/L10n.php:400 src/Model/Event.php:429
+#: src/Core/L10n.php:399 src/Model/Event.php:428
 msgid "Friday"
 msgstr ""
 
-#: src/Core/L10n.php:400 src/Model/Event.php:430
+#: src/Core/L10n.php:399 src/Model/Event.php:429
 msgid "Saturday"
 msgstr ""
 
-#: src/Core/L10n.php:400 src/Model/Event.php:424
+#: src/Core/L10n.php:399 src/Model/Event.php:423
 #: src/Module/Settings/Display.php:182
 msgid "Sunday"
 msgstr ""
 
-#: src/Core/L10n.php:404 src/Model/Event.php:445
+#: src/Core/L10n.php:403 src/Model/Event.php:444
 msgid "January"
 msgstr ""
 
-#: src/Core/L10n.php:404 src/Model/Event.php:446
+#: src/Core/L10n.php:403 src/Model/Event.php:445
 msgid "February"
 msgstr ""
 
-#: src/Core/L10n.php:404 src/Model/Event.php:447
+#: src/Core/L10n.php:403 src/Model/Event.php:446
 msgid "March"
 msgstr ""
 
-#: src/Core/L10n.php:404 src/Model/Event.php:448
+#: src/Core/L10n.php:403 src/Model/Event.php:447
 msgid "April"
 msgstr ""
 
-#: src/Core/L10n.php:404 src/Core/L10n.php:424 src/Model/Event.php:436
+#: src/Core/L10n.php:403 src/Core/L10n.php:422 src/Model/Event.php:435
 msgid "May"
 msgstr ""
 
-#: src/Core/L10n.php:404 src/Model/Event.php:449
+#: src/Core/L10n.php:403 src/Model/Event.php:448
 msgid "June"
 msgstr ""
 
-#: src/Core/L10n.php:404 src/Model/Event.php:450
+#: src/Core/L10n.php:403 src/Model/Event.php:449
 msgid "July"
 msgstr ""
 
-#: src/Core/L10n.php:404 src/Model/Event.php:451
+#: src/Core/L10n.php:403 src/Model/Event.php:450
 msgid "August"
 msgstr ""
 
-#: src/Core/L10n.php:404 src/Model/Event.php:452
+#: src/Core/L10n.php:403 src/Model/Event.php:451
 msgid "September"
 msgstr ""
 
-#: src/Core/L10n.php:404 src/Model/Event.php:453
+#: src/Core/L10n.php:403 src/Model/Event.php:452
 msgid "October"
 msgstr ""
 
-#: src/Core/L10n.php:404 src/Model/Event.php:454
+#: src/Core/L10n.php:403 src/Model/Event.php:453
 msgid "November"
 msgstr ""
 
-#: src/Core/L10n.php:404 src/Model/Event.php:455
+#: src/Core/L10n.php:403 src/Model/Event.php:454
 msgid "December"
 msgstr ""
 
-#: src/Core/L10n.php:420 src/Model/Event.php:417
+#: src/Core/L10n.php:418 src/Model/Event.php:416
 msgid "Mon"
 msgstr ""
 
-#: src/Core/L10n.php:420 src/Model/Event.php:418
+#: src/Core/L10n.php:418 src/Model/Event.php:417
 msgid "Tue"
 msgstr ""
 
-#: src/Core/L10n.php:420 src/Model/Event.php:419
+#: src/Core/L10n.php:418 src/Model/Event.php:418
 msgid "Wed"
 msgstr ""
 
-#: src/Core/L10n.php:420 src/Model/Event.php:420
+#: src/Core/L10n.php:418 src/Model/Event.php:419
 msgid "Thu"
 msgstr ""
 
-#: src/Core/L10n.php:420 src/Model/Event.php:421
+#: src/Core/L10n.php:418 src/Model/Event.php:420
 msgid "Fri"
 msgstr ""
 
-#: src/Core/L10n.php:420 src/Model/Event.php:422
+#: src/Core/L10n.php:418 src/Model/Event.php:421
 msgid "Sat"
 msgstr ""
 
-#: src/Core/L10n.php:420 src/Model/Event.php:416
+#: src/Core/L10n.php:418 src/Model/Event.php:415
 msgid "Sun"
 msgstr ""
 
-#: src/Core/L10n.php:424 src/Model/Event.php:432
+#: src/Core/L10n.php:422 src/Model/Event.php:431
 msgid "Jan"
 msgstr ""
 
-#: src/Core/L10n.php:424 src/Model/Event.php:433
+#: src/Core/L10n.php:422 src/Model/Event.php:432
 msgid "Feb"
 msgstr ""
 
-#: src/Core/L10n.php:424 src/Model/Event.php:434
+#: src/Core/L10n.php:422 src/Model/Event.php:433
 msgid "Mar"
 msgstr ""
 
-#: src/Core/L10n.php:424 src/Model/Event.php:435
+#: src/Core/L10n.php:422 src/Model/Event.php:434
 msgid "Apr"
 msgstr ""
 
-#: src/Core/L10n.php:424 src/Model/Event.php:437
+#: src/Core/L10n.php:422 src/Model/Event.php:436
 msgid "Jun"
 msgstr ""
 
-#: src/Core/L10n.php:424 src/Model/Event.php:438
+#: src/Core/L10n.php:422 src/Model/Event.php:437
 msgid "Jul"
 msgstr ""
 
-#: src/Core/L10n.php:424 src/Model/Event.php:439
+#: src/Core/L10n.php:422 src/Model/Event.php:438
 msgid "Aug"
 msgstr ""
 
-#: src/Core/L10n.php:424
+#: src/Core/L10n.php:422
 msgid "Sep"
 msgstr ""
 
-#: src/Core/L10n.php:424 src/Model/Event.php:441
+#: src/Core/L10n.php:422 src/Model/Event.php:440
 msgid "Oct"
 msgstr ""
 
-#: src/Core/L10n.php:424 src/Model/Event.php:442
+#: src/Core/L10n.php:422 src/Model/Event.php:441
 msgid "Nov"
 msgstr ""
 
-#: src/Core/L10n.php:424 src/Model/Event.php:443
+#: src/Core/L10n.php:422 src/Model/Event.php:442
 msgid "Dec"
 msgstr ""
 
-#: src/Core/L10n.php:443
+#: src/Core/L10n.php:441
 msgid "poke"
 msgstr ""
 
-#: src/Core/L10n.php:443
+#: src/Core/L10n.php:441
 msgid "poked"
 msgstr ""
 
-#: src/Core/L10n.php:444
+#: src/Core/L10n.php:442
 msgid "ping"
 msgstr ""
 
-#: src/Core/L10n.php:444
+#: src/Core/L10n.php:442
 msgid "pinged"
 msgstr ""
 
-#: src/Core/L10n.php:445
+#: src/Core/L10n.php:443
 msgid "prod"
 msgstr ""
 
-#: src/Core/L10n.php:445
+#: src/Core/L10n.php:443
 msgid "prodded"
 msgstr ""
 
-#: src/Core/L10n.php:446
+#: src/Core/L10n.php:444
 msgid "slap"
 msgstr ""
 
-#: src/Core/L10n.php:446
+#: src/Core/L10n.php:444
 msgid "slapped"
 msgstr ""
 
-#: src/Core/L10n.php:447
+#: src/Core/L10n.php:445
 msgid "finger"
 msgstr ""
 
-#: src/Core/L10n.php:447
+#: src/Core/L10n.php:445
 msgid "fingered"
 msgstr ""
 
-#: src/Core/L10n.php:448
+#: src/Core/L10n.php:446
 msgid "rebuff"
 msgstr ""
 
-#: src/Core/L10n.php:448
+#: src/Core/L10n.php:446
 msgid "rebuffed"
 msgstr ""
 
 #: src/Core/Renderer.php:89 src/Core/Renderer.php:118 src/Core/Renderer.php:145
-#: src/Core/Renderer.php:179 src/Render/FriendicaSmartyEngine.php:56
+#: src/Core/Renderer.php:179 src/Render/FriendicaSmartyEngine.php:69
 msgid ""
 "Friendica can't display this page at the moment, please contact the "
 "administrator."
@@ -3497,35 +3498,35 @@ msgid ""
 "\t\t\t\t\tThe friendica database was successfully updated from %s to %s."
 msgstr ""
 
-#: src/Core/UserImport.php:125
+#: src/Core/UserImport.php:126
 msgid "Error decoding account file"
 msgstr ""
 
-#: src/Core/UserImport.php:131
+#: src/Core/UserImport.php:132
 msgid "Error! No version data in file! This is not a Friendica account file?"
 msgstr ""
 
-#: src/Core/UserImport.php:139
+#: src/Core/UserImport.php:140
 #, php-format
 msgid "User '%s' already exists on this server!"
 msgstr ""
 
-#: src/Core/UserImport.php:175
+#: src/Core/UserImport.php:176
 msgid "User creation error"
 msgstr ""
 
-#: src/Core/UserImport.php:220
+#: src/Core/UserImport.php:221
 #, php-format
 msgid "%d contact not imported"
 msgid_plural "%d contacts not imported"
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/Core/UserImport.php:273
+#: src/Core/UserImport.php:274
 msgid "User profile creation error"
 msgstr ""
 
-#: src/Core/UserImport.php:326
+#: src/Core/UserImport.php:327
 msgid "Done. You can now login with your username and password"
 msgstr ""
 
@@ -3555,7 +3556,7 @@ msgstr ""
 msgid "There are no tables on MyISAM or InnoDB with the Antelope file format."
 msgstr ""
 
-#: src/Database/DBStructure.php:158
+#: src/Database/DBStructure.php:157
 #, php-format
 msgid ""
 "\n"
@@ -3563,20 +3564,20 @@ msgid ""
 "%s\n"
 msgstr ""
 
-#: src/Database/DBStructure.php:161
+#: src/Database/DBStructure.php:160
 msgid "Errors encountered performing database changes: "
 msgstr ""
 
-#: src/Database/DBStructure.php:549
+#: src/Database/DBStructure.php:563
 msgid "Another database update is currently running."
 msgstr ""
 
-#: src/Database/DBStructure.php:553
+#: src/Database/DBStructure.php:567
 #, php-format
 msgid "%s: Database update"
 msgstr ""
 
-#: src/Database/DBStructure.php:803
+#: src/Database/DBStructure.php:817
 #, php-format
 msgid "%s: updating %s table."
 msgstr ""
@@ -3607,81 +3608,81 @@ msgstr ""
 msgid "Legacy module file not found: %s"
 msgstr ""
 
-#: src/Model/Contact.php:1103 src/Model/Contact.php:1115
+#: src/Model/Contact.php:1104 src/Model/Contact.php:1116
 msgid "UnFollow"
 msgstr ""
 
-#: src/Model/Contact.php:1121 src/Module/Admin/Users/Pending.php:107
+#: src/Model/Contact.php:1122 src/Module/Admin/Users/Pending.php:107
 #: src/Module/Notifications/Introductions.php:130
 #: src/Module/Notifications/Introductions.php:202
 msgid "Approve"
 msgstr ""
 
-#: src/Model/Contact.php:1533
+#: src/Model/Contact.php:1534
 msgid "Organisation"
 msgstr ""
 
-#: src/Model/Contact.php:1541
+#: src/Model/Contact.php:1542
 msgid "Forum"
 msgstr ""
 
-#: src/Model/Contact.php:2550
+#: src/Model/Contact.php:2625
 msgid "Disallowed profile URL."
 msgstr ""
 
-#: src/Model/Contact.php:2555 src/Module/Friendica.php:81
+#: src/Model/Contact.php:2630 src/Module/Friendica.php:81
 msgid "Blocked domain"
 msgstr ""
 
-#: src/Model/Contact.php:2560
+#: src/Model/Contact.php:2635
 msgid "Connect URL missing."
 msgstr ""
 
-#: src/Model/Contact.php:2569
+#: src/Model/Contact.php:2644
 msgid ""
 "The contact could not be added. Please check the relevant network "
 "credentials in your Settings -> Social Networks page."
 msgstr ""
 
-#: src/Model/Contact.php:2611
+#: src/Model/Contact.php:2686
 msgid "The profile address specified does not provide adequate information."
 msgstr ""
 
-#: src/Model/Contact.php:2613
+#: src/Model/Contact.php:2688
 msgid "No compatible communication protocols or feeds were discovered."
 msgstr ""
 
-#: src/Model/Contact.php:2616
+#: src/Model/Contact.php:2691
 msgid "An author or name was not found."
 msgstr ""
 
-#: src/Model/Contact.php:2619
+#: src/Model/Contact.php:2694
 msgid "No browser URL could be matched to this address."
 msgstr ""
 
-#: src/Model/Contact.php:2622
+#: src/Model/Contact.php:2697
 msgid ""
 "Unable to match @-style Identity Address with a known protocol or email "
 "contact."
 msgstr ""
 
-#: src/Model/Contact.php:2623
+#: src/Model/Contact.php:2698
 msgid "Use mailto: in front of address to force email check."
 msgstr ""
 
-#: src/Model/Contact.php:2629
+#: src/Model/Contact.php:2704
 msgid ""
 "The profile address specified belongs to a network which has been disabled "
 "on this site."
 msgstr ""
 
-#: src/Model/Contact.php:2634
+#: src/Model/Contact.php:2709
 msgid ""
 "Limited profile. This person will be unable to receive direct/personal "
 "notifications from you."
 msgstr ""
 
-#: src/Model/Contact.php:2693
+#: src/Model/Contact.php:2768
 msgid "Unable to retrieve contact information."
 msgstr ""
 
@@ -3689,41 +3690,41 @@ msgstr ""
 msgid "l F d, Y \\@ g:i A \\G\\M\\TP (e)"
 msgstr ""
 
-#: src/Model/Event.php:73 src/Model/Event.php:90 src/Model/Event.php:464
+#: src/Model/Event.php:73 src/Model/Event.php:90 src/Model/Event.php:463
 #: src/Model/Event.php:897
 msgid "Starts:"
 msgstr ""
 
-#: src/Model/Event.php:76 src/Model/Event.php:96 src/Model/Event.php:465
+#: src/Model/Event.php:76 src/Model/Event.php:96 src/Model/Event.php:464
 #: src/Model/Event.php:901
 msgid "Finishes:"
 msgstr ""
 
-#: src/Model/Event.php:414
+#: src/Model/Event.php:413
 msgid "all-day"
 msgstr ""
 
-#: src/Model/Event.php:440
+#: src/Model/Event.php:439
 msgid "Sept"
 msgstr ""
 
-#: src/Model/Event.php:462
+#: src/Model/Event.php:461
 msgid "No events to display"
 msgstr ""
 
-#: src/Model/Event.php:578
+#: src/Model/Event.php:577
 msgid "l, F j"
 msgstr ""
 
-#: src/Model/Event.php:609
+#: src/Model/Event.php:608
 msgid "Edit event"
 msgstr ""
 
-#: src/Model/Event.php:610
+#: src/Model/Event.php:609
 msgid "Duplicate event"
 msgstr ""
 
-#: src/Model/Event.php:611
+#: src/Model/Event.php:610
 msgid "Delete event"
 msgstr ""
 
@@ -3747,112 +3748,112 @@ msgstr ""
 msgid "Hide map"
 msgstr ""
 
-#: src/Model/Event.php:1009
+#: src/Model/Event.php:1010
 #, php-format
 msgid "%s's birthday"
 msgstr ""
 
-#: src/Model/Event.php:1010
+#: src/Model/Event.php:1011
 #, php-format
 msgid "Happy Birthday %s"
 msgstr ""
 
-#: src/Model/Group.php:95
+#: src/Model/Group.php:105
 msgid ""
 "A deleted group with this name was revived. Existing item permissions "
 "<strong>may</strong> apply to this group and any future members. If this is "
 "not what you intended, please create another group with a different name."
 msgstr ""
 
-#: src/Model/Group.php:486
+#: src/Model/Group.php:499
 msgid "Default privacy group for new contacts"
 msgstr ""
 
-#: src/Model/Group.php:518
+#: src/Model/Group.php:531
 msgid "Everybody"
 msgstr ""
 
-#: src/Model/Group.php:537
+#: src/Model/Group.php:550
 msgid "edit"
 msgstr ""
 
-#: src/Model/Group.php:569
+#: src/Model/Group.php:582
 msgid "add"
 msgstr ""
 
-#: src/Model/Group.php:574
+#: src/Model/Group.php:587
 msgid "Edit group"
 msgstr ""
 
-#: src/Model/Group.php:575 src/Module/Group.php:194
+#: src/Model/Group.php:588 src/Module/Group.php:194
 msgid "Contacts not in any group"
 msgstr ""
 
-#: src/Model/Group.php:577
+#: src/Model/Group.php:590
 msgid "Create a new group"
 msgstr ""
 
-#: src/Model/Group.php:578 src/Module/Group.php:179 src/Module/Group.php:202
+#: src/Model/Group.php:591 src/Module/Group.php:179 src/Module/Group.php:202
 #: src/Module/Group.php:277
 msgid "Group Name: "
 msgstr ""
 
-#: src/Model/Group.php:579
+#: src/Model/Group.php:592
 msgid "Edit groups"
 msgstr ""
 
-#: src/Model/Item.php:1795
+#: src/Model/Item.php:1823
 #, php-format
 msgid "Detected languages in this post:\\n%s"
 msgstr ""
 
-#: src/Model/Item.php:2701
+#: src/Model/Item.php:2730
 msgid "activity"
 msgstr ""
 
-#: src/Model/Item.php:2703
+#: src/Model/Item.php:2732
 msgid "comment"
 msgstr ""
 
-#: src/Model/Item.php:2706
+#: src/Model/Item.php:2735
 msgid "post"
 msgstr ""
 
-#: src/Model/Item.php:2821
+#: src/Model/Item.php:2851
 #, php-format
 msgid "Content warning: %s"
 msgstr ""
 
-#: src/Model/Item.php:3180
+#: src/Model/Item.php:3210
 msgid "bytes"
 msgstr ""
 
-#: src/Model/Item.php:3214
+#: src/Model/Item.php:3244
 #, php-format
 msgid "%s (%d%s, %d votes)"
 msgstr ""
 
-#: src/Model/Item.php:3216
+#: src/Model/Item.php:3246
 #, php-format
 msgid "%s (%d votes)"
 msgstr ""
 
-#: src/Model/Item.php:3221
+#: src/Model/Item.php:3251
 #, php-format
 msgid "%d voters. Poll end: %s"
 msgstr ""
 
-#: src/Model/Item.php:3223
+#: src/Model/Item.php:3253
 #, php-format
 msgid "%d voters."
 msgstr ""
 
-#: src/Model/Item.php:3225
+#: src/Model/Item.php:3255
 #, php-format
 msgid "Poll end: %s"
 msgstr ""
 
-#: src/Model/Item.php:3259 src/Model/Item.php:3260
+#: src/Model/Item.php:3289 src/Model/Item.php:3290
 msgid "View on separate page"
 msgstr ""
 
@@ -3860,147 +3861,147 @@ msgstr ""
 msgid "[no subject]"
 msgstr ""
 
-#: src/Model/Profile.php:358 src/Module/Profile/Profile.php:256
+#: src/Model/Profile.php:359 src/Module/Profile/Profile.php:256
 #: src/Module/Profile/Profile.php:258
 msgid "Edit profile"
 msgstr ""
 
-#: src/Model/Profile.php:360
+#: src/Model/Profile.php:361
 msgid "Change profile photo"
 msgstr ""
 
-#: src/Model/Profile.php:373 src/Module/Directory.php:153
+#: src/Model/Profile.php:374 src/Module/Directory.php:153
 #: src/Module/Profile/Profile.php:184
 msgid "Homepage:"
 msgstr ""
 
-#: src/Model/Profile.php:374 src/Module/Contact/Profile.php:375
+#: src/Model/Profile.php:375 src/Module/Contact/Profile.php:375
 #: src/Module/Notifications/Introductions.php:187
 msgid "About:"
 msgstr ""
 
-#: src/Model/Profile.php:460
+#: src/Model/Profile.php:461
 msgid "Atom feed"
 msgstr ""
 
-#: src/Model/Profile.php:504
+#: src/Model/Profile.php:505
 msgid "F d"
 msgstr ""
 
-#: src/Model/Profile.php:568 src/Model/Profile.php:652
+#: src/Model/Profile.php:569 src/Model/Profile.php:653
 msgid "[today]"
 msgstr ""
 
-#: src/Model/Profile.php:577
+#: src/Model/Profile.php:578
 msgid "Birthday Reminders"
 msgstr ""
 
-#: src/Model/Profile.php:578
+#: src/Model/Profile.php:579
 msgid "Birthdays this week:"
 msgstr ""
 
-#: src/Model/Profile.php:601
+#: src/Model/Profile.php:602
 msgid "g A l F d"
 msgstr ""
 
-#: src/Model/Profile.php:639
+#: src/Model/Profile.php:640
 msgid "[No description]"
 msgstr ""
 
-#: src/Model/Profile.php:665
+#: src/Model/Profile.php:666
 msgid "Event Reminders"
 msgstr ""
 
-#: src/Model/Profile.php:666
+#: src/Model/Profile.php:667
 msgid "Upcoming events the next 7 days:"
 msgstr ""
 
-#: src/Model/Profile.php:854
+#: src/Model/Profile.php:855
 #, php-format
 msgid "OpenWebAuth: %1$s welcomes %2$s"
 msgstr ""
 
-#: src/Model/Profile.php:980
+#: src/Model/Profile.php:981
 msgid "Hometown:"
 msgstr ""
 
-#: src/Model/Profile.php:981
+#: src/Model/Profile.php:982
 msgid "Marital Status:"
 msgstr ""
 
-#: src/Model/Profile.php:982
+#: src/Model/Profile.php:983
 msgid "With:"
 msgstr ""
 
-#: src/Model/Profile.php:983
+#: src/Model/Profile.php:984
 msgid "Since:"
 msgstr ""
 
-#: src/Model/Profile.php:984
+#: src/Model/Profile.php:985
 msgid "Sexual Preference:"
 msgstr ""
 
-#: src/Model/Profile.php:985
+#: src/Model/Profile.php:986
 msgid "Political Views:"
 msgstr ""
 
-#: src/Model/Profile.php:986
+#: src/Model/Profile.php:987
 msgid "Religious Views:"
 msgstr ""
 
-#: src/Model/Profile.php:987
+#: src/Model/Profile.php:988
 msgid "Likes:"
 msgstr ""
 
-#: src/Model/Profile.php:988
+#: src/Model/Profile.php:989
 msgid "Dislikes:"
 msgstr ""
 
-#: src/Model/Profile.php:989
+#: src/Model/Profile.php:990
 msgid "Title/Description:"
 msgstr ""
 
-#: src/Model/Profile.php:990 src/Module/Admin/Summary.php:234
+#: src/Model/Profile.php:991 src/Module/Admin/Summary.php:234
 msgid "Summary"
 msgstr ""
 
-#: src/Model/Profile.php:991
+#: src/Model/Profile.php:992
 msgid "Musical interests"
 msgstr ""
 
-#: src/Model/Profile.php:992
+#: src/Model/Profile.php:993
 msgid "Books, literature"
 msgstr ""
 
-#: src/Model/Profile.php:993
+#: src/Model/Profile.php:994
 msgid "Television"
 msgstr ""
 
-#: src/Model/Profile.php:994
+#: src/Model/Profile.php:995
 msgid "Film/dance/culture/entertainment"
 msgstr ""
 
-#: src/Model/Profile.php:995
+#: src/Model/Profile.php:996
 msgid "Hobbies/Interests"
 msgstr ""
 
-#: src/Model/Profile.php:996
+#: src/Model/Profile.php:997
 msgid "Love/romance"
 msgstr ""
 
-#: src/Model/Profile.php:997
+#: src/Model/Profile.php:998
 msgid "Work/employment"
 msgstr ""
 
-#: src/Model/Profile.php:998
+#: src/Model/Profile.php:999
 msgid "School/education"
 msgstr ""
 
-#: src/Model/Profile.php:999
+#: src/Model/Profile.php:1000
 msgid "Contact information and Social Networks"
 msgstr ""
 
-#: src/Model/User.php:210 src/Model/User.php:1058
+#: src/Model/User.php:212 src/Model/User.php:1058
 msgid "SERIOUS ERROR: Generation of security keys failed."
 msgstr ""
 
@@ -4047,13 +4048,13 @@ msgstr ""
 msgid "Invalid OpenID url"
 msgstr ""
 
-#: src/Model/User.php:970 src/Security/Authentication.php:235
+#: src/Model/User.php:970 src/Security/Authentication.php:240
 msgid ""
 "We encountered a problem while logging in with the OpenID you provided. "
 "Please check the correct spelling of the ID."
 msgstr ""
 
-#: src/Model/User.php:970 src/Security/Authentication.php:235
+#: src/Model/User.php:970 src/Security/Authentication.php:240
 msgid "The error message was:"
 msgstr ""
 
@@ -4135,7 +4136,7 @@ msgstr ""
 msgid "Profile Photos"
 msgstr ""
 
-#: src/Model/User.php:1368
+#: src/Model/User.php:1367
 #, php-format
 msgid ""
 "\n"
@@ -4143,7 +4144,7 @@ msgid ""
 "\t\t\tthe administrator of %2$s has set up an account for you."
 msgstr ""
 
-#: src/Model/User.php:1371
+#: src/Model/User.php:1370
 #, php-format
 msgid ""
 "\n"
@@ -4180,12 +4181,12 @@ msgid ""
 "\t\tThank you and welcome to %4$s."
 msgstr ""
 
-#: src/Model/User.php:1404 src/Model/User.php:1511
+#: src/Model/User.php:1403 src/Model/User.php:1510
 #, php-format
 msgid "Registration details for %s"
 msgstr ""
 
-#: src/Model/User.php:1424
+#: src/Model/User.php:1423
 #, php-format
 msgid ""
 "\n"
@@ -4201,12 +4202,12 @@ msgid ""
 "\t\t"
 msgstr ""
 
-#: src/Model/User.php:1443
+#: src/Model/User.php:1442
 #, php-format
 msgid "Registration at %s"
 msgstr ""
 
-#: src/Model/User.php:1467
+#: src/Model/User.php:1466
 #, php-format
 msgid ""
 "\n"
@@ -4215,7 +4216,7 @@ msgid ""
 "\t\t\t"
 msgstr ""
 
-#: src/Model/User.php:1475
+#: src/Model/User.php:1474
 #, php-format
 msgid ""
 "\n"
@@ -4295,7 +4296,7 @@ msgid "Administration"
 msgstr ""
 
 #: src/Module/Admin/Addons/Details.php:112 src/Module/Admin/Addons/Index.php:68
-#: src/Module/BaseAdmin.php:93 src/Module/BaseSettings.php:85
+#: src/Module/BaseAdmin.php:96 src/Module/BaseSettings.php:85
 msgid "Addons"
 msgstr ""
 
@@ -4774,7 +4775,7 @@ msgid ""
 "only reflect the part of the network your node is aware of."
 msgstr ""
 
-#: src/Module/Admin/Federation.php:197 src/Module/BaseAdmin.php:87
+#: src/Module/Admin/Federation.php:197 src/Module/BaseAdmin.php:90
 msgid "Federation Statistics"
 msgstr ""
 
@@ -4790,7 +4791,7 @@ msgstr ""
 msgid "Item marked for deletion."
 msgstr ""
 
-#: src/Module/Admin/Item/Delete.php:65 src/Module/BaseAdmin.php:106
+#: src/Module/Admin/Item/Delete.php:65 src/Module/BaseAdmin.php:109
 msgid "Delete Item"
 msgstr ""
 
@@ -4819,7 +4820,7 @@ msgstr ""
 msgid "The GUID of the item you want to delete."
 msgstr ""
 
-#: src/Module/Admin/Item/Source.php:57 src/Module/BaseAdmin.php:116
+#: src/Module/Admin/Item/Source.php:57 src/Module/BaseAdmin.php:119
 msgid "Item Source"
 msgstr ""
 
@@ -4882,8 +4883,8 @@ msgstr ""
 msgid "PHP log currently disabled."
 msgstr ""
 
-#: src/Module/Admin/Logs/Settings.php:80 src/Module/BaseAdmin.php:108
-#: src/Module/BaseAdmin.php:109
+#: src/Module/Admin/Logs/Settings.php:80 src/Module/BaseAdmin.php:111
+#: src/Module/BaseAdmin.php:112
 msgid "Logs"
 msgstr ""
 
@@ -4936,7 +4937,7 @@ msgid ""
 "is readable."
 msgstr ""
 
-#: src/Module/Admin/Logs/View.php:85 src/Module/BaseAdmin.php:110
+#: src/Module/Admin/Logs/View.php:85 src/Module/BaseAdmin.php:113
 msgid "View Logs"
 msgstr ""
 
@@ -5118,7 +5119,7 @@ msgstr ""
 msgid "Interactors"
 msgstr ""
 
-#: src/Module/Admin/Site.php:432 src/Module/BaseAdmin.php:90
+#: src/Module/Admin/Site.php:432 src/Module/BaseAdmin.php:93
 msgid "Site"
 msgstr ""
 
@@ -5907,7 +5908,7 @@ msgid ""
 msgstr ""
 
 #: src/Module/Admin/Site.php:542 src/Module/Contact/Profile.php:273
-#: src/Module/Settings/TwoFactor/Index.php:118
+#: src/Module/Settings/TwoFactor/Index.php:121
 msgid "Disabled"
 msgstr ""
 
@@ -5971,7 +5972,7 @@ msgstr ""
 msgid "Storage Configuration"
 msgstr ""
 
-#: src/Module/Admin/Storage.php:141 src/Module/BaseAdmin.php:91
+#: src/Module/Admin/Storage.php:141 src/Module/BaseAdmin.php:94
 msgid "Storage"
 msgstr ""
 
@@ -6187,7 +6188,7 @@ msgid "Screenshot"
 msgstr ""
 
 #: src/Module/Admin/Themes/Details.php:91 src/Module/Admin/Themes/Index.php:112
-#: src/Module/BaseAdmin.php:94
+#: src/Module/BaseAdmin.php:97
 msgid "Themes"
 msgstr ""
 
@@ -6388,7 +6389,7 @@ msgid "Permanent deletion"
 msgstr ""
 
 #: src/Module/Admin/Users/Index.php:150 src/Module/Admin/Users/Index.php:160
-#: src/Module/BaseAdmin.php:92
+#: src/Module/BaseAdmin.php:95
 msgid "Users"
 msgstr ""
 
@@ -6494,89 +6495,89 @@ msgstr ""
 msgid "Item was not found."
 msgstr ""
 
-#: src/Module/BaseAdmin.php:54 src/Module/BaseAdmin.php:58
+#: src/Module/BaseAdmin.php:57 src/Module/BaseAdmin.php:61
 msgid "Please login to continue."
 msgstr ""
 
-#: src/Module/BaseAdmin.php:63
+#: src/Module/BaseAdmin.php:66
 msgid "You don't have access to administration pages."
 msgstr ""
 
-#: src/Module/BaseAdmin.php:67
+#: src/Module/BaseAdmin.php:70
 msgid ""
 "Submanaged account can't access the administration pages. Please log back in "
 "as the main account."
 msgstr ""
 
-#: src/Module/BaseAdmin.php:86
+#: src/Module/BaseAdmin.php:89
 msgid "Overview"
 msgstr ""
 
-#: src/Module/BaseAdmin.php:89
+#: src/Module/BaseAdmin.php:92
 msgid "Configuration"
 msgstr ""
 
-#: src/Module/BaseAdmin.php:95 src/Module/BaseSettings.php:63
+#: src/Module/BaseAdmin.php:98 src/Module/BaseSettings.php:63
 msgid "Additional features"
 msgstr ""
 
-#: src/Module/BaseAdmin.php:98
+#: src/Module/BaseAdmin.php:101
 msgid "Database"
 msgstr ""
 
-#: src/Module/BaseAdmin.php:99
+#: src/Module/BaseAdmin.php:102
 msgid "DB updates"
 msgstr ""
 
-#: src/Module/BaseAdmin.php:100
+#: src/Module/BaseAdmin.php:103
 msgid "Inspect Deferred Workers"
 msgstr ""
 
-#: src/Module/BaseAdmin.php:101
+#: src/Module/BaseAdmin.php:104
 msgid "Inspect worker Queue"
 msgstr ""
 
-#: src/Module/BaseAdmin.php:103
+#: src/Module/BaseAdmin.php:106
 msgid "Tools"
 msgstr ""
 
-#: src/Module/BaseAdmin.php:104
+#: src/Module/BaseAdmin.php:107
 msgid "Contact Blocklist"
 msgstr ""
 
-#: src/Module/BaseAdmin.php:105
+#: src/Module/BaseAdmin.php:108
 msgid "Server Blocklist"
 msgstr ""
 
-#: src/Module/BaseAdmin.php:112
+#: src/Module/BaseAdmin.php:115
 msgid "Diagnostics"
 msgstr ""
 
-#: src/Module/BaseAdmin.php:113
+#: src/Module/BaseAdmin.php:116
 msgid "PHP Info"
 msgstr ""
 
-#: src/Module/BaseAdmin.php:114
+#: src/Module/BaseAdmin.php:117
 msgid "probe address"
 msgstr ""
 
-#: src/Module/BaseAdmin.php:115
+#: src/Module/BaseAdmin.php:118
 msgid "check webfinger"
 msgstr ""
 
-#: src/Module/BaseAdmin.php:117
+#: src/Module/BaseAdmin.php:120
 msgid "Babel"
 msgstr ""
 
-#: src/Module/BaseAdmin.php:118 src/Module/Debug/ActivityPubConversion.php:142
+#: src/Module/BaseAdmin.php:121 src/Module/Debug/ActivityPubConversion.php:142
 msgid "ActivityPub Conversion"
 msgstr ""
 
-#: src/Module/BaseAdmin.php:127
+#: src/Module/BaseAdmin.php:130
 msgid "Addon Features"
 msgstr ""
 
-#: src/Module/BaseAdmin.php:128
+#: src/Module/BaseAdmin.php:131
 msgid "User registrations waiting for confirmation"
 msgstr ""
 
@@ -6638,8 +6639,8 @@ msgstr ""
 msgid "Account"
 msgstr ""
 
-#: src/Module/BaseSettings.php:48 src/Module/Security/TwoFactor/Verify.php:95
-#: src/Module/Settings/TwoFactor/Index.php:110
+#: src/Module/BaseSettings.php:48 src/Module/Security/TwoFactor/Verify.php:96
+#: src/Module/Settings/TwoFactor/Index.php:113
 msgid "Two-factor authentication"
 msgstr ""
 
@@ -6691,7 +6692,7 @@ msgid "Only show blocked contacts"
 msgstr ""
 
 #: src/Module/Contact.php:330 src/Module/Contact.php:377
-#: src/Object/Post.php:329
+#: src/Object/Post.php:339
 msgid "Ignored"
 msgstr ""
 
@@ -6723,7 +6724,7 @@ msgstr ""
 msgid "Search your contacts"
 msgstr ""
 
-#: src/Module/Contact.php:390 src/Module/Search/Index.php:192
+#: src/Module/Contact.php:390 src/Module/Search/Index.php:207
 #, php-format
 msgid "Results for: %s"
 msgstr ""
@@ -7088,7 +7089,7 @@ msgid ""
 msgstr ""
 
 #: src/Module/Contact/Profile.php:378
-#: src/Module/Settings/TwoFactor/Index.php:132
+#: src/Module/Settings/TwoFactor/Index.php:135
 msgid "Actions"
 msgstr ""
 
@@ -7147,6 +7148,7 @@ msgstr ""
 #: src/Module/Contact/Revoke.php:107
 #: src/Module/Notifications/Introductions.php:142
 #: src/Module/OAuth/Acknowledge.php:53 src/Module/Register.php:130
+#: src/Module/Settings/TwoFactor/Trusted.php:124
 msgid "Yes"
 msgstr ""
 
@@ -7178,8 +7180,8 @@ msgstr ""
 msgid "Hide"
 msgstr ""
 
-#: src/Module/Conversation/Community.php:137 src/Module/Search/Index.php:137
-#: src/Module/Search/Index.php:179
+#: src/Module/Conversation/Community.php:137 src/Module/Search/Index.php:152
+#: src/Module/Search/Index.php:194
 msgid "No results."
 msgstr ""
 
@@ -7239,7 +7241,7 @@ msgstr ""
 msgid "Posts that mention or involve you"
 msgstr ""
 
-#: src/Module/Conversation/Network.php:287 src/Object/Post.php:341
+#: src/Module/Conversation/Network.php:287 src/Object/Post.php:351
 msgid "Starred"
 msgstr ""
 
@@ -8060,6 +8062,7 @@ msgstr ""
 
 #: src/Module/Notifications/Introductions.php:142
 #: src/Module/OAuth/Acknowledge.php:54 src/Module/Register.php:131
+#: src/Module/Settings/TwoFactor/Trusted.php:124
 msgid "No"
 msgstr ""
 
@@ -8293,19 +8296,19 @@ msgstr ""
 
 #: src/Module/Profile/Profile.php:326 src/Module/Profile/Profile.php:329
 #: src/Module/Profile/Status.php:66 src/Module/Profile/Status.php:69
-#: src/Protocol/Feed.php:1017 src/Protocol/OStatus.php:1245
+#: src/Protocol/Feed.php:1018 src/Protocol/OStatus.php:1276
 #, php-format
 msgid "%s's timeline"
 msgstr ""
 
 #: src/Module/Profile/Profile.php:327 src/Module/Profile/Status.php:67
-#: src/Protocol/Feed.php:1021 src/Protocol/OStatus.php:1249
+#: src/Protocol/Feed.php:1022 src/Protocol/OStatus.php:1281
 #, php-format
 msgid "%s's posts"
 msgstr ""
 
 #: src/Module/Profile/Profile.php:328 src/Module/Profile/Status.php:68
-#: src/Protocol/Feed.php:1024 src/Protocol/OStatus.php:1252
+#: src/Protocol/Feed.php:1025 src/Protocol/OStatus.php:1285
 #, php-format
 msgid "%s's comments"
 msgstr ""
@@ -8505,15 +8508,15 @@ msgstr ""
 msgid "Your Webfinger address or profile URL:"
 msgstr ""
 
-#: src/Module/Search/Index.php:54
+#: src/Module/Search/Index.php:69
 msgid "Only logged in users are permitted to perform a search."
 msgstr ""
 
-#: src/Module/Search/Index.php:74
+#: src/Module/Search/Index.php:89
 msgid "Only one search per minute is permitted for not logged in users."
 msgstr ""
 
-#: src/Module/Search/Index.php:190
+#: src/Module/Search/Index.php:205
 #, php-format
 msgid "Items tagged with: %s"
 msgstr ""
@@ -8576,7 +8579,11 @@ msgstr ""
 msgid "privacy policy"
 msgstr ""
 
-#: src/Module/Security/Logout.php:87
+#: src/Module/Security/Logout.php:83
+#: src/Module/Security/TwoFactor/Signout.php:78
+#: src/Module/Security/TwoFactor/Signout.php:86
+#: src/Module/Security/TwoFactor/Signout.php:108
+#: src/Module/Security/TwoFactor/Signout.php:115
 msgid "Logged out."
 msgstr ""
 
@@ -8602,7 +8609,7 @@ msgid "Remaining recovery codes: %d"
 msgstr ""
 
 #: src/Module/Security/TwoFactor/Recovery.php:77
-#: src/Module/Security/TwoFactor/Verify.php:76
+#: src/Module/Security/TwoFactor/Verify.php:77
 #: src/Module/Settings/TwoFactor/Verify.php:94
 msgid "Invalid code, please retry."
 msgstr ""
@@ -8618,7 +8625,6 @@ msgid ""
 msgstr ""
 
 #: src/Module/Security/TwoFactor/Recovery.php:98
-#: src/Module/Security/TwoFactor/Verify.php:99
 #, php-format
 msgid ""
 "Don’t have your phone? <a href=\"%s\">Enter a two-factor recovery code</a>"
@@ -8632,19 +8638,66 @@ msgstr ""
 msgid "Submit recovery code and complete login"
 msgstr ""
 
-#: src/Module/Security/TwoFactor/Verify.php:96
+#: src/Module/Security/TwoFactor/Signout.php:122
+msgid "Sign out of this browser?"
+msgstr ""
+
+#: src/Module/Security/TwoFactor/Signout.php:123
+msgid ""
+"<p>If you trust this browser, you will not be asked for verification code "
+"the next time you sign in.</p>"
+msgstr ""
+
+#: src/Module/Security/TwoFactor/Signout.php:124
+msgid "Sign out"
+msgstr ""
+
+#: src/Module/Security/TwoFactor/Signout.php:126
+msgid "Trust and sign out"
+msgstr ""
+
+#: src/Module/Security/TwoFactor/Trust.php:89
+msgid "Couldn't save browser to Cookie."
+msgstr ""
+
+#: src/Module/Security/TwoFactor/Trust.php:119
+msgid "Trust this browser?"
+msgstr ""
+
+#: src/Module/Security/TwoFactor/Trust.php:120
+msgid ""
+"<p>If you choose to trust this browser, you will not be asked for a "
+"verification code the next time you sign in.</p>"
+msgstr ""
+
+#: src/Module/Security/TwoFactor/Trust.php:121
+msgid "Not now"
+msgstr ""
+
+#: src/Module/Security/TwoFactor/Trust.php:122
+msgid "Don't trust"
+msgstr ""
+
+#: src/Module/Security/TwoFactor/Trust.php:123
+msgid "Trust"
+msgstr ""
+
+#: src/Module/Security/TwoFactor/Verify.php:97
 msgid ""
 "<p>Open the two-factor authentication app on your device to get an "
 "authentication code and verify your identity.</p>"
 msgstr ""
 
 #: src/Module/Security/TwoFactor/Verify.php:100
-#: src/Module/Settings/TwoFactor/Verify.php:154
-msgid "Please enter a code from your authentication app"
+#, php-format
+msgid ""
+"If you do not have access to your authentication code you can use a <a href="
+"\"%s\">two-factor recovery code</a>."
 msgstr ""
 
 #: src/Module/Security/TwoFactor/Verify.php:101
-msgid "This is my two-factor authenticator app device"
+#: src/Module/Settings/TwoFactor/Verify.php:154
+msgid "Please enter a code from your authentication app"
 msgstr ""
 
 #: src/Module/Security/TwoFactor/Verify.php:102
@@ -9651,99 +9704,95 @@ msgstr ""
 msgid "Generate"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:67
+#: src/Module/Settings/TwoFactor/Index.php:68
 msgid "Two-factor authentication successfully disabled."
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:93
-msgid "Wrong Password"
-msgstr ""
-
-#: src/Module/Settings/TwoFactor/Index.php:113
+#: src/Module/Settings/TwoFactor/Index.php:116
 msgid ""
 "<p>Use an application on a mobile device to get two-factor authentication "
 "codes when prompted on login.</p>"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:117
+#: src/Module/Settings/TwoFactor/Index.php:120
 msgid "Authenticator app"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:118
+#: src/Module/Settings/TwoFactor/Index.php:121
 msgid "Configured"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:118
+#: src/Module/Settings/TwoFactor/Index.php:121
 msgid "Not Configured"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:119
+#: src/Module/Settings/TwoFactor/Index.php:122
 msgid "<p>You haven't finished configuring your authenticator app.</p>"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:120
+#: src/Module/Settings/TwoFactor/Index.php:123
 msgid "<p>Your authenticator app is correctly configured.</p>"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:122
+#: src/Module/Settings/TwoFactor/Index.php:125
 msgid "Recovery codes"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:123
+#: src/Module/Settings/TwoFactor/Index.php:126
 msgid "Remaining valid codes"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:125
+#: src/Module/Settings/TwoFactor/Index.php:128
 msgid ""
 "<p>These one-use codes can replace an authenticator app code in case you "
 "have lost access to it.</p>"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:127
+#: src/Module/Settings/TwoFactor/Index.php:130
 msgid "App-specific passwords"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:128
+#: src/Module/Settings/TwoFactor/Index.php:131
 msgid "Generated app-specific passwords"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:130
+#: src/Module/Settings/TwoFactor/Index.php:133
 msgid ""
 "<p>These randomly generated passwords allow you to authenticate on apps not "
 "supporting two-factor authentication.</p>"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:133
+#: src/Module/Settings/TwoFactor/Index.php:136
 msgid "Current password:"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:133
+#: src/Module/Settings/TwoFactor/Index.php:136
 msgid ""
 "You need to provide your current password to change two-factor "
 "authentication settings."
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:134
+#: src/Module/Settings/TwoFactor/Index.php:137
 msgid "Enable two-factor authentication"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:135
+#: src/Module/Settings/TwoFactor/Index.php:138
 msgid "Disable two-factor authentication"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:136
+#: src/Module/Settings/TwoFactor/Index.php:139
 msgid "Show recovery codes"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:137
+#: src/Module/Settings/TwoFactor/Index.php:140
 msgid "Manage app-specific passwords"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:138
+#: src/Module/Settings/TwoFactor/Index.php:141
 msgid "Manage trusted browsers"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Index.php:139
+#: src/Module/Settings/TwoFactor/Index.php:142
 msgid "Finish app configuration"
 msgstr ""
 
@@ -9785,34 +9834,38 @@ msgstr ""
 msgid "Trusted browser successfully removed."
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Trusted.php:133
+#: src/Module/Settings/TwoFactor/Trusted.php:134
 msgid "Two-factor Trusted Browsers"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Trusted.php:134
+#: src/Module/Settings/TwoFactor/Trusted.php:135
 msgid ""
 "Trusted browsers are individual browsers you chose to skip two-factor "
 "authentication to access Friendica. Please use this feature sparingly, as it "
 "can negate the benefit of two-factor authentication."
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Trusted.php:135
+#: src/Module/Settings/TwoFactor/Trusted.php:136
 msgid "Device"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Trusted.php:136
+#: src/Module/Settings/TwoFactor/Trusted.php:137
 msgid "OS"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Trusted.php:138
+#: src/Module/Settings/TwoFactor/Trusted.php:139
 msgid "Trusted"
 msgstr ""
 
-#: src/Module/Settings/TwoFactor/Trusted.php:139
-msgid "Last Use"
+#: src/Module/Settings/TwoFactor/Trusted.php:140
+msgid "Created At"
 msgstr ""
 
 #: src/Module/Settings/TwoFactor/Trusted.php:141
+msgid "Last Use"
+msgstr ""
+
+#: src/Module/Settings/TwoFactor/Trusted.php:143
 msgid "Remove All"
 msgstr ""
 
@@ -10285,205 +10338,205 @@ msgstr ""
 msgid "%1$s commented on your thread %2$s"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:221
-#: src/Navigation/Notifications/Repository/Notify.php:735
+#: src/Navigation/Notifications/Repository/Notify.php:222
+#: src/Navigation/Notifications/Repository/Notify.php:736
 msgid "[Friendica:Notify]"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:285
+#: src/Navigation/Notifications/Repository/Notify.php:286
 #, php-format
 msgid "%s New mail received at %s"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:287
+#: src/Navigation/Notifications/Repository/Notify.php:288
 #, php-format
 msgid "%1$s sent you a new private message at %2$s."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:288
+#: src/Navigation/Notifications/Repository/Notify.php:289
 msgid "a private message"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:288
+#: src/Navigation/Notifications/Repository/Notify.php:289
 #, php-format
 msgid "%1$s sent you %2$s."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:290
+#: src/Navigation/Notifications/Repository/Notify.php:291
 #, php-format
 msgid "Please visit %s to view and/or reply to your private messages."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:320
+#: src/Navigation/Notifications/Repository/Notify.php:321
 #, php-format
 msgid "%1$s commented on %2$s's %3$s %4$s"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:325
+#: src/Navigation/Notifications/Repository/Notify.php:326
 #, php-format
 msgid "%1$s commented on your %2$s %3$s"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:329
+#: src/Navigation/Notifications/Repository/Notify.php:330
 #, php-format
 msgid "%1$s commented on their %2$s %3$s"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:333
-#: src/Navigation/Notifications/Repository/Notify.php:769
+#: src/Navigation/Notifications/Repository/Notify.php:334
+#: src/Navigation/Notifications/Repository/Notify.php:770
 #, php-format
 msgid "%1$s Comment to conversation #%2$d by %3$s"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:335
+#: src/Navigation/Notifications/Repository/Notify.php:336
 #, php-format
 msgid "%s commented on an item/conversation you have been following."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:339
-#: src/Navigation/Notifications/Repository/Notify.php:354
-#: src/Navigation/Notifications/Repository/Notify.php:373
-#: src/Navigation/Notifications/Repository/Notify.php:784
+#: src/Navigation/Notifications/Repository/Notify.php:340
+#: src/Navigation/Notifications/Repository/Notify.php:355
+#: src/Navigation/Notifications/Repository/Notify.php:374
+#: src/Navigation/Notifications/Repository/Notify.php:785
 #, php-format
 msgid "Please visit %s to view and/or reply to the conversation."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:346
+#: src/Navigation/Notifications/Repository/Notify.php:347
 #, php-format
 msgid "%s %s posted to your profile wall"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:348
+#: src/Navigation/Notifications/Repository/Notify.php:349
 #, php-format
 msgid "%1$s posted to your profile wall at %2$s"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:349
+#: src/Navigation/Notifications/Repository/Notify.php:350
 #, php-format
 msgid "%1$s posted to [url=%2$s]your wall[/url]"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:361
+#: src/Navigation/Notifications/Repository/Notify.php:362
 #, php-format
 msgid "%1$s %2$s poked you"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:363
+#: src/Navigation/Notifications/Repository/Notify.php:364
 #, php-format
 msgid "%1$s poked you at %2$s"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:364
+#: src/Navigation/Notifications/Repository/Notify.php:365
 #, php-format
 msgid "%1$s [url=%2$s]poked you[/url]."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:381
+#: src/Navigation/Notifications/Repository/Notify.php:382
 #, php-format
 msgid "%s Introduction received"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:383
+#: src/Navigation/Notifications/Repository/Notify.php:384
 #, php-format
 msgid "You've received an introduction from '%1$s' at %2$s"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:384
+#: src/Navigation/Notifications/Repository/Notify.php:385
 #, php-format
 msgid "You've received [url=%1$s]an introduction[/url] from %2$s."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:389
-#: src/Navigation/Notifications/Repository/Notify.php:435
+#: src/Navigation/Notifications/Repository/Notify.php:390
+#: src/Navigation/Notifications/Repository/Notify.php:436
 #, php-format
 msgid "You may visit their profile at %s"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:391
+#: src/Navigation/Notifications/Repository/Notify.php:392
 #, php-format
 msgid "Please visit %s to approve or reject the introduction."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:398
+#: src/Navigation/Notifications/Repository/Notify.php:399
 #, php-format
 msgid "%s A new person is sharing with you"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:400
 #: src/Navigation/Notifications/Repository/Notify.php:401
+#: src/Navigation/Notifications/Repository/Notify.php:402
 #, php-format
 msgid "%1$s is sharing with you at %2$s"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:408
+#: src/Navigation/Notifications/Repository/Notify.php:409
 #, php-format
 msgid "%s You have a new follower"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:410
 #: src/Navigation/Notifications/Repository/Notify.php:411
+#: src/Navigation/Notifications/Repository/Notify.php:412
 #, php-format
 msgid "You have a new follower at %2$s : %1$s"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:424
+#: src/Navigation/Notifications/Repository/Notify.php:425
 #, php-format
 msgid "%s Friend suggestion received"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:426
+#: src/Navigation/Notifications/Repository/Notify.php:427
 #, php-format
 msgid "You've received a friend suggestion from '%1$s' at %2$s"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:427
+#: src/Navigation/Notifications/Repository/Notify.php:428
 #, php-format
 msgid "You've received [url=%1$s]a friend suggestion[/url] for %2$s from %3$s."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:433
+#: src/Navigation/Notifications/Repository/Notify.php:434
 msgid "Name:"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:434
+#: src/Navigation/Notifications/Repository/Notify.php:435
 msgid "Photo:"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:437
+#: src/Navigation/Notifications/Repository/Notify.php:438
 #, php-format
 msgid "Please visit %s to approve or reject the suggestion."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:445
-#: src/Navigation/Notifications/Repository/Notify.php:460
+#: src/Navigation/Notifications/Repository/Notify.php:446
+#: src/Navigation/Notifications/Repository/Notify.php:461
 #, php-format
 msgid "%s Connection accepted"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:447
-#: src/Navigation/Notifications/Repository/Notify.php:462
+#: src/Navigation/Notifications/Repository/Notify.php:448
+#: src/Navigation/Notifications/Repository/Notify.php:463
 #, php-format
 msgid "'%1$s' has accepted your connection request at %2$s"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:448
-#: src/Navigation/Notifications/Repository/Notify.php:463
+#: src/Navigation/Notifications/Repository/Notify.php:449
+#: src/Navigation/Notifications/Repository/Notify.php:464
 #, php-format
 msgid "%2$s has accepted your [url=%1$s]connection request[/url]."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:453
+#: src/Navigation/Notifications/Repository/Notify.php:454
 msgid ""
 "You are now mutual friends and may exchange status updates, photos, and "
 "email without restriction."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:455
+#: src/Navigation/Notifications/Repository/Notify.php:456
 #, php-format
 msgid "Please visit %s if you wish to make any changes to this relationship."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:468
+#: src/Navigation/Notifications/Repository/Notify.php:469
 #, php-format
 msgid ""
 "'%1$s' has chosen to accept you a fan, which restricts some forms of "
@@ -10492,33 +10545,33 @@ msgid ""
 "automatically."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:470
+#: src/Navigation/Notifications/Repository/Notify.php:471
 #, php-format
 msgid ""
 "'%1$s' may choose to extend this into a two-way or more permissive "
 "relationship in the future."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:472
+#: src/Navigation/Notifications/Repository/Notify.php:473
 #, php-format
 msgid "Please visit %s  if you wish to make any changes to this relationship."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:482
+#: src/Navigation/Notifications/Repository/Notify.php:483
 msgid "registration request"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:484
+#: src/Navigation/Notifications/Repository/Notify.php:485
 #, php-format
 msgid "You've received a registration request from '%1$s' at %2$s"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:485
+#: src/Navigation/Notifications/Repository/Notify.php:486
 #, php-format
 msgid "You've received a [url=%1$s]registration request[/url] from %2$s."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:490
+#: src/Navigation/Notifications/Repository/Notify.php:491
 #, php-format
 msgid ""
 "Full Name:\t%s\n"
@@ -10526,17 +10579,17 @@ msgid ""
 "Login Name:\t%s (%s)"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:496
+#: src/Navigation/Notifications/Repository/Notify.php:497
 #, php-format
 msgid "Please visit %s to approve or reject the request."
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:763
+#: src/Navigation/Notifications/Repository/Notify.php:764
 #, php-format
 msgid "%s %s tagged you"
 msgstr ""
 
-#: src/Navigation/Notifications/Repository/Notify.php:766
+#: src/Navigation/Notifications/Repository/Notify.php:767
 #, php-format
 msgid "%s %s shared a new post"
 msgstr ""
@@ -10563,244 +10616,244 @@ msgstr ""
 msgid "%s posted an update."
 msgstr ""
 
-#: src/Object/Post.php:134
+#: src/Object/Post.php:136
 msgid "Private Message"
 msgstr ""
 
-#: src/Object/Post.php:137
+#: src/Object/Post.php:140
 msgid "Public Message"
 msgstr ""
 
-#: src/Object/Post.php:140
+#: src/Object/Post.php:144
 msgid "Unlisted Message"
 msgstr ""
 
-#: src/Object/Post.php:170
+#: src/Object/Post.php:179
 msgid "This entry was edited"
 msgstr ""
 
-#: src/Object/Post.php:198
+#: src/Object/Post.php:207
 msgid "Connector Message"
 msgstr ""
 
-#: src/Object/Post.php:213 src/Object/Post.php:215
+#: src/Object/Post.php:222 src/Object/Post.php:224
 msgid "Edit"
 msgstr ""
 
-#: src/Object/Post.php:239
+#: src/Object/Post.php:248
 msgid "Delete globally"
 msgstr ""
 
-#: src/Object/Post.php:239
+#: src/Object/Post.php:248
 msgid "Remove locally"
 msgstr ""
 
-#: src/Object/Post.php:255
+#: src/Object/Post.php:264
 #, php-format
 msgid "Block %s"
 msgstr ""
 
-#: src/Object/Post.php:260
+#: src/Object/Post.php:269
 msgid "Save to folder"
 msgstr ""
 
-#: src/Object/Post.php:294
+#: src/Object/Post.php:304
 msgid "I will attend"
 msgstr ""
 
-#: src/Object/Post.php:294
+#: src/Object/Post.php:304
 msgid "I will not attend"
 msgstr ""
 
-#: src/Object/Post.php:294
+#: src/Object/Post.php:304
 msgid "I might attend"
 msgstr ""
 
-#: src/Object/Post.php:324
+#: src/Object/Post.php:334
 msgid "Ignore thread"
 msgstr ""
 
-#: src/Object/Post.php:325
+#: src/Object/Post.php:335
 msgid "Unignore thread"
 msgstr ""
 
-#: src/Object/Post.php:326
+#: src/Object/Post.php:336
 msgid "Toggle ignore status"
 msgstr ""
 
-#: src/Object/Post.php:336
+#: src/Object/Post.php:346
 msgid "Add star"
 msgstr ""
 
-#: src/Object/Post.php:337
+#: src/Object/Post.php:347
 msgid "Remove star"
 msgstr ""
 
-#: src/Object/Post.php:338
+#: src/Object/Post.php:348
 msgid "Toggle star status"
 msgstr ""
 
-#: src/Object/Post.php:349
+#: src/Object/Post.php:359
 msgid "Pin"
 msgstr ""
 
-#: src/Object/Post.php:350
+#: src/Object/Post.php:360
 msgid "Unpin"
 msgstr ""
 
-#: src/Object/Post.php:351
+#: src/Object/Post.php:361
 msgid "Toggle pin status"
 msgstr ""
 
-#: src/Object/Post.php:354
+#: src/Object/Post.php:364
 msgid "Pinned"
 msgstr ""
 
-#: src/Object/Post.php:359
+#: src/Object/Post.php:369
 msgid "Add tag"
 msgstr ""
 
-#: src/Object/Post.php:372
+#: src/Object/Post.php:382
 msgid "Quote share this"
 msgstr ""
 
-#: src/Object/Post.php:372
+#: src/Object/Post.php:382
 msgid "Quote Share"
 msgstr ""
 
-#: src/Object/Post.php:375
+#: src/Object/Post.php:385
 msgid "Reshare this"
 msgstr ""
 
-#: src/Object/Post.php:375
+#: src/Object/Post.php:385
 msgid "Reshare"
 msgstr ""
 
-#: src/Object/Post.php:376
+#: src/Object/Post.php:386
 msgid "Cancel your Reshare"
 msgstr ""
 
-#: src/Object/Post.php:376
+#: src/Object/Post.php:386
 msgid "Unshare"
 msgstr ""
 
-#: src/Object/Post.php:423
+#: src/Object/Post.php:433
 #, php-format
 msgid "%s (Received %s)"
 msgstr ""
 
-#: src/Object/Post.php:428
+#: src/Object/Post.php:438
 msgid "Comment this item on your system"
 msgstr ""
 
-#: src/Object/Post.php:428
+#: src/Object/Post.php:438
 msgid "Remote comment"
 msgstr ""
 
-#: src/Object/Post.php:449
+#: src/Object/Post.php:459
 msgid "Share via ..."
 msgstr ""
 
-#: src/Object/Post.php:449
+#: src/Object/Post.php:459
 msgid "Share via external services"
 msgstr ""
 
-#: src/Object/Post.php:478
+#: src/Object/Post.php:488
 msgid "to"
 msgstr ""
 
-#: src/Object/Post.php:479
+#: src/Object/Post.php:489
 msgid "via"
 msgstr ""
 
-#: src/Object/Post.php:480
+#: src/Object/Post.php:490
 msgid "Wall-to-Wall"
 msgstr ""
 
-#: src/Object/Post.php:481
+#: src/Object/Post.php:491
 msgid "via Wall-To-Wall:"
 msgstr ""
 
-#: src/Object/Post.php:523
+#: src/Object/Post.php:533
 #, php-format
 msgid "Reply to %s"
 msgstr ""
 
-#: src/Object/Post.php:526
+#: src/Object/Post.php:536
 msgid "More"
 msgstr ""
 
-#: src/Object/Post.php:544
+#: src/Object/Post.php:554
 msgid "Notifier task is pending"
 msgstr ""
 
-#: src/Object/Post.php:545
+#: src/Object/Post.php:555
 msgid "Delivery to remote servers is pending"
 msgstr ""
 
-#: src/Object/Post.php:546
+#: src/Object/Post.php:556
 msgid "Delivery to remote servers is underway"
 msgstr ""
 
-#: src/Object/Post.php:547
+#: src/Object/Post.php:557
 msgid "Delivery to remote servers is mostly done"
 msgstr ""
 
-#: src/Object/Post.php:548
+#: src/Object/Post.php:558
 msgid "Delivery to remote servers is done"
 msgstr ""
 
-#: src/Object/Post.php:568
+#: src/Object/Post.php:578
 #, php-format
 msgid "%d comment"
 msgid_plural "%d comments"
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/Object/Post.php:569
+#: src/Object/Post.php:579
 msgid "Show more"
 msgstr ""
 
-#: src/Object/Post.php:570
+#: src/Object/Post.php:580
 msgid "Show fewer"
 msgstr ""
 
-#: src/Protocol/OStatus.php:1648
+#: src/Protocol/OStatus.php:1705
 #, php-format
 msgid "%s is now following %s."
 msgstr ""
 
-#: src/Protocol/OStatus.php:1649
+#: src/Protocol/OStatus.php:1706
 msgid "following"
 msgstr ""
 
-#: src/Protocol/OStatus.php:1652
+#: src/Protocol/OStatus.php:1709
 #, php-format
 msgid "%s stopped following %s."
 msgstr ""
 
-#: src/Protocol/OStatus.php:1653
+#: src/Protocol/OStatus.php:1710
 msgid "stopped following"
 msgstr ""
 
-#: src/Render/FriendicaSmartyEngine.php:52
+#: src/Render/FriendicaSmartyEngine.php:65
 msgid "The folder view/smarty3/ must be writable by webserver."
 msgstr ""
 
-#: src/Security/Authentication.php:221
+#: src/Security/Authentication.php:226
 msgid "Login failed."
 msgstr ""
 
-#: src/Security/Authentication.php:262
+#: src/Security/Authentication.php:267
 msgid "Login failed. Please check your credentials."
 msgstr ""
 
-#: src/Security/Authentication.php:360
+#: src/Security/Authentication.php:369
 #, php-format
 msgid "Welcome %s"
 msgstr ""
 
-#: src/Security/Authentication.php:361
+#: src/Security/Authentication.php:370
 msgid "Please upload a profile photo."
 msgstr ""
 
@@ -10898,15 +10951,15 @@ msgstr ""
 msgid "%1$d %2$s ago"
 msgstr ""
 
-#: src/Worker/Delivery.php:524
+#: src/Worker/Delivery.php:525
 msgid "(no subject)"
 msgstr ""
 
-#: src/Worker/PushSubscription.php:103
+#: src/Worker/PushSubscription.php:112
 msgid "Notification from Friendica"
 msgstr ""
 
-#: src/Worker/PushSubscription.php:104
+#: src/Worker/PushSubscription.php:113
 msgid "Empty Post"
 msgstr ""
 
@@ -11052,39 +11105,39 @@ msgstr ""
 msgid "Leave background image and color empty for theme defaults"
 msgstr ""
 
-#: view/theme/frio/php/Image.php:40
+#: view/theme/frio/php/Image.php:39
 msgid "Top Banner"
 msgstr ""
 
-#: view/theme/frio/php/Image.php:40
+#: view/theme/frio/php/Image.php:39
 msgid ""
 "Resize image to the width of the screen and show background color below on "
 "long pages."
 msgstr ""
 
-#: view/theme/frio/php/Image.php:41
+#: view/theme/frio/php/Image.php:40
 msgid "Full screen"
 msgstr ""
 
-#: view/theme/frio/php/Image.php:41
+#: view/theme/frio/php/Image.php:40
 msgid ""
 "Resize image to fill entire screen, clipping either the right or the bottom."
 msgstr ""
 
-#: view/theme/frio/php/Image.php:42
+#: view/theme/frio/php/Image.php:41
 msgid "Single row mosaic"
 msgstr ""
 
-#: view/theme/frio/php/Image.php:42
+#: view/theme/frio/php/Image.php:41
 msgid ""
 "Resize image to repeat it on a single row, either vertical or horizontal."
 msgstr ""
 
-#: view/theme/frio/php/Image.php:43
+#: view/theme/frio/php/Image.php:42
 msgid "Mosaic"
 msgstr ""
 
-#: view/theme/frio/php/Image.php:43
+#: view/theme/frio/php/Image.php:42
 msgid "Repeat image to fill the screen."
 msgstr ""
 
index 29d2ab29c6d87f8d42d4250e88ec9cc1d71e67fb..1a9ae91fe57e43831f163d48506101515c95aa7d 100644 (file)
@@ -10,6 +10,7 @@
                                        <th>{{$device_label}}</th>
                                        <th>{{$os_label}}</th>
                                        <th>{{$browser_label}}</th>
+                                       <th>{{$trusted_label}}</th>
                                        <th>{{$created_label}}</th>
                                        <th>{{$last_used_label}}</th>
                                        <th><button type="submit" name="action" class="btn btn-primary btn-small" value="remove_all">{{$remove_all_label}}</button></th>
@@ -28,6 +29,9 @@
                                {{$trusted_browser.browser}}
                                        </td>
                                        <td>
+                        {{$trusted_browser.trusted_labeled}}
+                                       </td>
+                                       <td>
                                                <time class="time" title="{{$trusted_browser.created_local}}" data-toggle="tooltip" datetime="{{$trusted_browser.created_utc}}">{{$trusted_browser.created_ago}}</time>
                                        </td>
                                        <td>
diff --git a/view/templates/twofactor/signout.tpl b/view/templates/twofactor/signout.tpl
new file mode 100644 (file)
index 0000000..f2a2111
--- /dev/null
@@ -0,0 +1,14 @@
+<div class="generic-page-wrapper">
+       <h1>{{$title}}</h1>
+       <div>{{$message nofilter}}</div>
+
+       <form action="" method="post">
+               <input type="hidden" name="form_security_token" value="{{$form_security_token}}">
+
+               <div class="form-group settings-submit-wrapper">
+                       <button type="submit" name="action" id="trust-submit-button" class="btn btn-primary confirm-button" value="sign_out">{{$sign_out_label}}</button>
+                       <button type="submit" name="action" id="dont-trust-submit-button" class="btn confirm-button" value="cancel">{{$cancel_label}}</button>
+                       <button type="submit" name="action" id="not-now-submit-button" class="right-aligned btn confirm-button" value="trust_and_sign_out">{{$trust_and_sign_out_label}}</button>
+               </div>
+       </form>
+</div>
diff --git a/view/templates/twofactor/trust.tpl b/view/templates/twofactor/trust.tpl
new file mode 100644 (file)
index 0000000..6bfe307
--- /dev/null
@@ -0,0 +1,14 @@
+<div class="generic-page-wrapper">
+       <h1>{{$title}}</h1>
+       <div>{{$message nofilter}}</div>
+
+       <form action="" method="post">
+               <input type="hidden" name="form_security_token" value="{{$form_security_token}}">
+
+               <div class="form-group settings-submit-wrapper">
+                       <button type="submit" name="action" id="trust-submit-button" class="btn btn-primary confirm-button" value="trust">{{$trust_label}}</button>
+                       <button type="submit" name="action" id="dont-trust-submit-button" class="btn confirm-button" value="dont_trust">{{$dont_trust_label}}</button>
+                       <button type="submit" name="action" id="not-now-submit-button" class="right-aligned btn confirm-button" value="not_now_label">{{$not_now_label}}</button>
+               </div>
+       </form>
+</div>
index 938f98da091ede5a860f2d10e9535e93cd372643..2b1fe31421f2785ba5ee47634ca8d8bcc54926bf 100644 (file)
@@ -18,8 +18,6 @@
 
                {{include file="field_input.tpl" field=$verify_code}}
 
-               {{include file="field_checkbox.tpl" field=$trust_browser}}
-
                <div class="form-group settings-submit-wrapper">
                        <button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="verify">{{$verify_label}}</button>
                </div>