diff --git a/src/Module/BaseSettings.php b/src/Module/BaseSettings.php index 693554f493..49e75ebaff 100644 --- a/src/Module/BaseSettings.php +++ b/src/Module/BaseSettings.php @@ -149,6 +149,13 @@ class BaseSettings extends BaseModule 'accesskey' => 's', ]; + $tabs[] = [ + 'label' => $this->t('Import Contacts'), + 'url' => 'settings/importcontacts', + 'selected' => static::class == Settings\UserExport::class ? 'active' : '', + 'accesskey' => '', + ]; + $tabs[] = [ 'label' => $this->t('Export personal data'), 'url' => 'settings/userexport', diff --git a/src/Module/Settings/Account.php b/src/Module/Settings/Account.php index 8e8bd5a909..567ac41fd6 100644 --- a/src/Module/Settings/Account.php +++ b/src/Module/Settings/Account.php @@ -9,7 +9,6 @@ namespace Friendica\Module\Settings; use Exception; use Friendica\Core\ACL; -use Friendica\Core\Logger; use Friendica\Core\Renderer; use Friendica\Core\Search; use Friendica\Core\Worker; @@ -25,7 +24,6 @@ use Friendica\Module\BaseSettings; use Friendica\Network\HTTPException; use Friendica\Protocol\Activity; use Friendica\Protocol\Delivery; -use Friendica\Util\Network; use Friendica\Util\Temporal; class Account extends BaseSettings @@ -324,42 +322,6 @@ class Account extends BaseSettings DI::baseUrl()->redirect($redirectUrl); } - // Import Contacts from CSV file - if (!empty($request['importcontact-submit'])) { - if (isset($_FILES['importcontact-filename'])) { - // was there an error - if ($_FILES['importcontact-filename']['error'] > 0) { - Logger::notice('Contact CSV file upload error', ['error' => $_FILES['importcontact-filename']['error']]); - DI::sysmsg()->addNotice(DI::l10n()->t('Contact CSV file upload error')); - } else { - $csvArray = array_map('str_getcsv', file($_FILES['importcontact-filename']['tmp_name'])); - Logger::notice('Import started', ['lines' => count($csvArray)]); - // import contacts - foreach ($csvArray as $csvRow) { - // The 1st row may, or may not contain the headers of the table - // We expect the 1st field of the row to contain either the URL - // or the handle of the account, therefore we check for either - // "http" or "@" to be present in the string. - // All other fields from the row will be ignored - if ((strpos($csvRow[0], '@') !== false) || Network::isValidHttpUrl($csvRow[0])) { - Worker::add(Worker::PRIORITY_MEDIUM, 'AddContact', DI::userSession()->getLocalUserId(), trim($csvRow[0], '@')); - } else { - Logger::notice('Invalid account', ['url' => $csvRow[0]]); - } - } - Logger::notice('Import done'); - - DI::sysmsg()->addInfo(DI::l10n()->t('Importing Contacts done')); - // delete temp file - unlink($_FILES['importcontact-filename']['tmp_name']); - } - } else { - Logger::notice('Import triggered, but no import file was found.'); - } - - DI::baseUrl()->redirect($redirectUrl); - } - if (!empty($request['relocate-submit'])) { Worker::add(Worker::PRIORITY_HIGH, 'Notifier', Delivery::RELOCATION, DI::userSession()->getLocalUserId()); DI::sysmsg()->addInfo(DI::l10n()->t("Relocate message has been send to your contacts")); @@ -637,11 +599,6 @@ class Account extends BaseSettings '$h_descadvn' => DI::l10n()->t('Change the behaviour of this account for special situations'), '$pagetype' => $pagetype, - '$importcontact' => DI::l10n()->t('Import Contacts'), - '$importcontact_text' => DI::l10n()->t('Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'), - '$importcontact_button' => DI::l10n()->t('Upload File'), - '$importcontact_maxsize' => DI::config()->get('system', 'max_csv_file_size', 30720), - '$relocate' => DI::l10n()->t('Relocate'), '$relocate_text' => DI::l10n()->t("If you have moved this profile from another server, and some of your contacts don't receive your updates, try pushing this button."), '$relocate_button' => DI::l10n()->t("Resend relocate message to contacts"), diff --git a/src/Module/Settings/Connectors.php b/src/Module/Settings/Connectors.php index 3e6829a0d6..7e5b57a783 100644 --- a/src/Module/Settings/Connectors.php +++ b/src/Module/Settings/Connectors.php @@ -62,7 +62,6 @@ class Connectors extends BaseSettings $this->pconfig->set($this->session->getLocalUserId(), 'system', 'api_spoiler_title', intval($request['api_spoiler_title'])); $this->pconfig->set($this->session->getLocalUserId(), 'system', 'api_auto_attach', intval($request['api_auto_attach'])); $this->pconfig->set($this->session->getLocalUserId(), 'system', 'article_mode', intval($request['article_mode'])); - $this->pconfig->set($this->session->getLocalUserId(), 'ostatus', 'legacy_contact', $request['legacy_contact']); } elseif (!empty($request['mail-submit']) && function_exists('imap_open') && !$this->config->get('system', 'imap_disabled')) { $mail_server = $request['mail_server'] ?? ''; $mail_port = $request['mail_port'] ?? ''; @@ -127,11 +126,6 @@ class Connectors extends BaseSettings $api_spoiler_title = intval($this->pconfig->get($this->session->getLocalUserId(), 'system', 'api_spoiler_title', true)); $api_auto_attach = intval($this->pconfig->get($this->session->getLocalUserId(), 'system', 'api_auto_attach', false)); $article_mode = intval($this->pconfig->get($this->session->getLocalUserId(), 'system', 'article_mode')); - $legacy_contact = $this->pconfig->get($this->session->getLocalUserId(), 'ostatus', 'legacy_contact'); - - if (!empty($legacy_contact)) { - $this->baseUrl->redirect('ostatus/subscribe?url=' . urlencode($legacy_contact)); - } $connector_settings_forms = []; foreach ($this->database->selectToArray('hook', ['file', 'function'], ['hook' => 'connector_settings']) as $hook) { @@ -215,7 +209,6 @@ class Connectors extends BaseSettings '$api_spoiler_title' => ['api_spoiler_title', $this->t('API: Use spoiler field as title'), $api_spoiler_title, $this->t('When activated, the "spoiler_text" field in the API will be used for the title on standalone posts. When deactivated it will be used for spoiler text. For comments it will always be used for spoiler text.')], '$api_auto_attach' => ['api_auto_attach', $this->t('API: Automatically links at the end of the post as attached posts'), $api_auto_attach, $this->t('When activated, added links at the end of the post react the same way as added links in the web interface.')], '$article_mode' => ['article_mode', $this->t('Article Mode'), $article_mode, $this->t("Controls how posts with titles are transmitted. Mastodon and its forks don't display the content of these posts if the post is created in the correct (default) way."), $article_modes], - '$legacy_contact' => ['legacy_contact', $this->t('Your legacy ActivityPub/GNU Social account'), $legacy_contact, $this->t('If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.')], '$connector_settings_forms' => $connector_settings_forms, diff --git a/src/Module/Settings/ContactImport.php b/src/Module/Settings/ContactImport.php new file mode 100644 index 0000000000..991b447548 --- /dev/null +++ b/src/Module/Settings/ContactImport.php @@ -0,0 +1,118 @@ +config = $config; + $this->pconfig = $pconfig; + $this->systemMessages = $systemMessages; + } + + protected function post(array $request = []) + { + if (!$this->session->getLocalUserId()) { + throw new HTTPException\ForbiddenException($this->l10n->t('Permission denied.')); + } + + self::checkFormSecurityTokenRedirectOnError($this->args->getQueryString(), 'contactimport'); + + parent::post(); + + // Import Contacts from CSV file + if (!empty($request['importcontact-submit'])) { + $this->pconfig->set($this->session->getLocalUserId(), 'ostatus', 'legacy_contact', $request['legacy_contact']); + if (isset($_FILES['importcontact-filename']) && !empty($_FILES['importcontact-filename']['tmp_name'])) { + // was there an error + if ($_FILES['importcontact-filename']['error'] > 0) { + $this->logger->notice('Contact CSV file upload error', ['error' => $_FILES['importcontact-filename']['error']]); + $this->systemMessages->addNotice($this->l10n->t('Contact CSV file upload error')); + } else { + $csvArray = array_map('str_getcsv', file($_FILES['importcontact-filename']['tmp_name'])); + $this->logger->notice('Import started', ['lines' => count($csvArray)]); + // import contacts + foreach ($csvArray as $csvRow) { + // The 1st row may, or may not contain the headers of the table + // We expect the 1st field of the row to contain either the URL + // or the handle of the account, therefore we check for either + // "http" or "@" to be present in the string. + // All other fields from the row will be ignored + if ((strpos($csvRow[0], '@') !== false) || Network::isValidHttpUrl($csvRow[0])) { + Worker::add(Worker::PRIORITY_MEDIUM, 'AddContact', $this->session->getLocalUserId(), trim($csvRow[0], '@')); + } else { + $this->logger->notice('Invalid account', ['url' => $csvRow[0]]); + } + } + $this->logger->notice('Import done'); + + $this->systemMessages->addInfo($this->l10n->t('Importing Contacts done')); + // delete temp file + unlink($_FILES['importcontact-filename']['tmp_name']); + } + } else { + $this->logger->notice('Import triggered, but no import file was found.'); + } + } + $this->baseUrl->redirect($this->args->getQueryString()); + } + + protected function content(array $request = []): string + { + if (!$this->session->getLocalUserId()) { + throw new HTTPException\ForbiddenException($this->l10n->t('Permission denied.')); + } + + parent::content(); + + $legacy_contact = $this->pconfig->get($this->session->getLocalUserId(), 'ostatus', 'legacy_contact'); + + if (!empty($legacy_contact)) { + $this->baseUrl->redirect('ostatus/subscribe?url=' . urlencode($legacy_contact)); + } + + $tpl = Renderer::getMarkupTemplate('settings/contactimport.tpl'); + return Renderer::replaceMacros($tpl, [ + '$title' => $this->l10n->t('Import Contacts'), + '$submit' => $this->l10n->t('Save Settings'), + '$form_security_token' => self::getFormSecurityToken('contactimport'), + '$importcontact_text' => $this->l10n->t('Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'), + '$importcontact_button' => $this->l10n->t('Upload File'), + '$importcontact_maxsize' => $this->config->get('system', 'max_csv_file_size', 30720), + '$legacy_contact' => ['legacy_contact', $this->t('Your legacy ActivityPub/GNU Social account'), $legacy_contact, $this->t('If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.')], + ]); + } +} diff --git a/static/routes.config.php b/static/routes.config.php index f9680216e2..5ebce2738b 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -622,6 +622,7 @@ return [ '/delegation[/{action}/{user_id}]' => [Module\Settings\Delegation::class, [R::GET, R::POST]], '/display' => [Module\Settings\Display::class, [R::GET, R::POST]], '/features' => [Module\Settings\Features::class, [R::GET, R::POST]], + '/importcontacts' => [Module\Settings\ContactImport::class, [R::GET, R::POST]], '/oauth' => [Module\Settings\OAuth::class, [R::GET, R::POST]], '/profile' => [ '[/]' => [Module\Settings\Profile\Index::class, [R::GET, R::POST]], diff --git a/view/templates/settings/account.tpl b/view/templates/settings/account.tpl index eb7b9c7dc7..daaa6c178a 100644 --- a/view/templates/settings/account.tpl +++ b/view/templates/settings/account.tpl @@ -178,18 +178,6 @@ -

{{$importcontact}}

-
- - -

{{$importcontact_text}}

-

- -
- -
-
-

{{$relocate}}

diff --git a/view/templates/settings/connectors.tpl b/view/templates/settings/connectors.tpl index 7c92f54b41..a62da74c0d 100644 --- a/view/templates/settings/connectors.tpl +++ b/view/templates/settings/connectors.tpl @@ -25,7 +25,6 @@ {{include file="field_checkbox.tpl" field=$api_spoiler_title}} {{include file="field_checkbox.tpl" field=$api_auto_attach}} {{include file="field_select.tpl" field=$article_mode}} - {{include file="field_input.tpl" field=$legacy_contact}}
diff --git a/view/templates/settings/contactimport.tpl b/view/templates/settings/contactimport.tpl new file mode 100644 index 0000000000..d132b26b25 --- /dev/null +++ b/view/templates/settings/contactimport.tpl @@ -0,0 +1,23 @@ +{{* + * Copyright (C) 2010-2024, the Friendica project + * SPDX-FileCopyrightText: 2010-2024 the Friendica project + * + * SPDX-License-Identifier: AGPL-3.0-or-later + *}} +

{{$title}}

+ +
+ + + + {{include file="field_input.tpl" field=$legacy_contact}} +
+

{{$importcontact_text}}

+

+ +
+ +
+ +
\ No newline at end of file diff --git a/view/theme/frio/templates/settings/account.tpl b/view/theme/frio/templates/settings/account.tpl index 22a77d0226..2a8fd1e1bc 100644 --- a/view/theme/frio/templates/settings/account.tpl +++ b/view/theme/frio/templates/settings/account.tpl @@ -246,28 +246,6 @@
- {{* Import contacts CSV *}} -
- - -
-
-
{{$importcontact_text}}
- - -
- -
-
- {{* The relocate setting section *}}
diff --git a/view/theme/frio/templates/settings/connectors.tpl b/view/theme/frio/templates/settings/connectors.tpl index aa1e0e7dbd..31f5e7b0dd 100644 --- a/view/theme/frio/templates/settings/connectors.tpl +++ b/view/theme/frio/templates/settings/connectors.tpl @@ -39,8 +39,6 @@ {{include file="field_checkbox.tpl" field=$api_auto_attach}} {{include file="field_select.tpl" field=$article_mode}} - - {{include file="field_input.tpl" field=$legacy_contact}}
\ No newline at end of file