]> git.mxchange.org Git - friendica.git/commitdiff
Create new User\Import module class
authorHypolite Petovan <hypolite@mrpetovan.com>
Mon, 31 Oct 2022 18:42:43 +0000 (14:42 -0400)
committerHypolite Petovan <hypolite@mrpetovan.com>
Wed, 2 Nov 2022 15:27:31 +0000 (11:27 -0400)
- Merge Core\UserImport class in Module class

doc/Move-Account.md
doc/de/Move-Account.md
src/Core/UserImport.php [deleted file]
src/Module/User/Import.php [new file with mode: 0644]
static/routes.config.php
view/templates/user/import.tpl [new file with mode: 0644]

index 2193c42c4b8c712a8fad523bb787fd8f4f79b788..70d23daa97e914897dd3397d1f7f13e6baba0e59 100644 (file)
@@ -9,8 +9,8 @@ How to move your account between servers
 * Go to "Settings" -> "[Export personal data](uexport)"
 * Click on "Export account" to save your account data.
 * **Save the file in a secure place!** It contains your details, your contacts, groups, and personal settings. It also contains your secret keys to authenticate yourself to your contacts.
-* Go to your new server, and open *http://newserver.com/uimport* (there is not a direct link to this page at the moment). Please consider that this is only possible on servers with open registration. On other systems only the administrator can add accounts with an uploaded file.
-* Do NOT create a new account prior to importing your old settings - uimport should be used *instead* of register.
+* Go to your new server, and open *http://newserver.com/user/import* (there is not a direct link to this page at the moment). Please consider that this is only possible on servers with open registration. On other systems only the administrator can add accounts with an uploaded file.
+* Do NOT create a new account prior to importing your old settings - user import should be used *instead* of register.
 * Load your saved account file and click "Import".
 * After the move, the account on the old server will not work reliably anymore, and should be not used.
 
index 79e7eae917b8a27bb5cd61d0f909bb3c04ceed02..2573113bba9e7dabd3581d7239ef95f0a9ba2c74 100644 (file)
@@ -15,14 +15,14 @@ Außerdem enthält sie deinen geheimen Schlüssel mit dem du dich deinen Kontakt
 
 **Speichere diese Datei an einem sicheren Ort**!
 
-Rufe nun dem neuen Server die Seite *http://newserver.com/uimport* auf (es gibt derzeit keinen direkten Link auf diese Seite).
+Rufe nun dem neuen Server die Seite *http://newserver.com/user/import* auf (es gibt derzeit keinen direkten Link auf diese Seite).
 
 Bitte beachte, dass dies nur auf Servern möglich ist, an denen man sich offen anmelden kann.
 Bei Servern, bei denen der Administrator Accounts freigeben muss, ist das Hochladen nicht möglich.
 Hier kann dies nur der Administrator selber durchführen.
 
 Lege auf dem neuen Server auf keinen Fall einen gleichnamigen Account an!
-uimport muss anstelle des Registrierens verwendet werden.
+user import muss anstelle des Registrierens verwendet werden.
 
 Wähle die gesicherte Account Datei aus und klicke "Importieren".
 
diff --git a/src/Core/UserImport.php b/src/Core/UserImport.php
deleted file mode 100644 (file)
index be7bbf0..0000000
+++ /dev/null
@@ -1,330 +0,0 @@
-<?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\Core;
-
-use Friendica\Database\DBA;
-use Friendica\Database\DBStructure;
-use Friendica\DI;
-use Friendica\Model\Photo;
-use Friendica\Model\Profile;
-use Friendica\Object\Image;
-use Friendica\Security\PermissionSet\Repository\PermissionSet;
-use Friendica\Util\Strings;
-use Friendica\Worker\Delivery;
-
-/**
- * UserImport class
- */
-class UserImport
-{
-       const IMPORT_DEBUG = false;
-
-       private static function lastInsertId()
-       {
-               if (self::IMPORT_DEBUG) {
-                       return 1;
-               }
-
-               return DBA::lastInsertId();
-       }
-
-       /**
-        * Remove columns from array $arr that aren't in table $table
-        *
-        * @param string $table Table name
-        * @param array &$arr   Column=>Value array from json (by ref)
-        * @throws \Exception
-        */
-       private static function checkCols($table, &$arr)
-       {
-               $tableColumns = DBStructure::getColumns($table);
-
-               $tcols = [];
-               $ttype = [];
-               // get a plain array of column names
-               foreach ($tableColumns as $tcol) {
-                       $tcols[] = $tcol['Field'];
-                       $ttype[$tcol['Field']] = $tcol['Type'];
-               }
-               // remove inexistent columns
-               foreach ($arr as $icol => $ival) {
-                       if (!in_array($icol, $tcols)) {
-                               unset($arr[$icol]);
-                               continue;
-                       }
-
-                       if ($ttype[$icol] === 'datetime') {
-                               $arr[$icol] = $ival ?? DBA::NULL_DATETIME;
-                       }
-               }
-       }
-
-       /**
-        * Import data into table $table
-        *
-        * @param string $table Table name
-        * @param array  $arr   Column=>Value array from json
-        * @return array|bool
-        * @throws \Exception
-        */
-       private static function dbImportAssoc(string $table, array $arr)
-       {
-               if (isset($arr['id'])) {
-                       unset($arr['id']);
-               }
-
-               self::checkCols($table, $arr);
-
-               if (self::IMPORT_DEBUG) {
-                       return true;
-               }
-
-               return DBA::insert($table, $arr);
-       }
-
-       /**
-        * Import account file exported from mod/uexport
-        *
-        * @param array $file array from $_FILES
-        * @return void
-        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
-        * @throws \ImagickException
-        */
-       public static function importAccount(array $file)
-       {
-               Logger::notice("Start user import from " . $file['tmp_name']);
-               /*
-               STEPS
-               1. checks
-               2. replace old baseurl with new baseurl
-               3. import data (look at user id and contacts id)
-               4. archive non-dfrn contacts
-               5. send message to dfrn contacts
-               */
-
-               $account = json_decode(file_get_contents($file['tmp_name']), true);
-               if ($account === null) {
-                       DI::sysmsg()->addNotice(DI::l10n()->t("Error decoding account file"));
-                       return;
-               }
-
-
-               if (empty($account['version'])) {
-                       DI::sysmsg()->addNotice(DI::l10n()->t("Error! No version data in file! This is not a Friendica account file?"));
-                       return;
-               }
-
-               // check for username
-               // check if username matches deleted account
-               if (DBA::exists('user', ['nickname' => $account['user']['nickname']])
-                       || DBA::exists('userd', ['username' => $account['user']['nickname']])) {
-                       DI::sysmsg()->addNotice(DI::l10n()->t("User '%s' already exists on this server!", $account['user']['nickname']));
-                       return;
-               }
-
-               $oldbaseurl = $account['baseurl'];
-               $newbaseurl = DI::baseUrl();
-
-               $oldaddr = str_replace('http://', '@', Strings::normaliseLink($oldbaseurl));
-               $newaddr = str_replace('http://', '@', Strings::normaliseLink($newbaseurl));
-
-               if (!empty($account['profile']['addr'])) {
-                       $old_handle = $account['profile']['addr'];
-               } else {
-                       $old_handle = $account['user']['nickname'].$oldaddr;
-               }
-
-               // Creating a new guid to avoid problems with Diaspora
-               $account['user']['guid'] = System::createUUID();
-
-               $olduid = $account['user']['uid'];
-
-               unset($account['user']['uid']);
-               unset($account['user']['account_expired']);
-               unset($account['user']['account_expires_on']);
-               unset($account['user']['expire_notification_sent']);
-
-               $callback = function (&$value) use ($oldbaseurl, $oldaddr, $newbaseurl, $newaddr) {
-                       $value =  str_replace([$oldbaseurl, $oldaddr], [$newbaseurl, $newaddr], $value);
-               };
-
-               array_walk($account['user'], $callback);
-
-               // import user
-               $r = self::dbImportAssoc('user', $account['user']);
-               if ($r === false) {
-                       Logger::warning("uimport:insert user : ERROR : " . DBA::errorMessage());
-                       DI::sysmsg()->addNotice(DI::l10n()->t("User creation error"));
-                       return;
-               }
-               $newuid = self::lastInsertId();
-
-               DI::pConfig()->set($newuid, 'system', 'previous_addr', $old_handle);
-
-               $errorcount = 0;
-               foreach ($account['contact'] as &$contact) {
-                       if ($contact['uid'] == $olduid && $contact['self'] == '1') {
-                               foreach ($contact as $k => &$v) {
-                                       $v = str_replace([$oldbaseurl, $oldaddr], [$newbaseurl, $newaddr], $v);
-                                       foreach (["profile", "avatar", "micro"] as $k) {
-                                               $v = str_replace($oldbaseurl . "/photo/" . $k . "/" . $olduid . ".jpg", $newbaseurl . "/photo/" . $k . "/" . $newuid . ".jpg", $v);
-                                       }
-                               }
-                       }
-                       if ($contact['uid'] == $olduid && $contact['self'] == '0') {
-                               // set contacts 'avatar-date' to NULL_DATE to let worker to update urls
-                               $contact["avatar-date"] = DBA::NULL_DATETIME;
-
-                               switch ($contact['network']) {
-                                       case Protocol::DFRN:
-                                       case Protocol::DIASPORA:
-                                               //  send relocate message (below)
-                                               break;
-                                       case Protocol::FEED:
-                                       case Protocol::MAIL:
-                                               // Nothing to do
-                                               break;
-                                       default:
-                                               // archive other contacts
-                                               $contact['archive'] = "1";
-                               }
-                       }
-                       $contact['uid'] = $newuid;
-                       $r = self::dbImportAssoc('contact', $contact);
-                       if ($r === false) {
-                               Logger::warning("uimport:insert contact " . $contact['nick'] . "," . $contact['network'] . " : ERROR : " . DBA::errorMessage());
-                               $errorcount++;
-                       } else {
-                               $contact['newid'] = self::lastInsertId();
-                       }
-               }
-               if ($errorcount > 0) {
-                       DI::sysmsg()->addNotice(DI::l10n()->tt("%d contact not imported", "%d contacts not imported", $errorcount));
-               }
-
-               foreach ($account['group'] as &$group) {
-                       $group['uid'] = $newuid;
-                       $r = self::dbImportAssoc('group', $group);
-                       if ($r === false) {
-                               Logger::warning("uimport:insert group " . $group['name'] . " : ERROR : " . DBA::errorMessage());
-                       } else {
-                               $group['newid'] = self::lastInsertId();
-                       }
-               }
-
-               foreach ($account['group_member'] as &$group_member) {
-                       $import = 0;
-                       foreach ($account['group'] as $group) {
-                               if ($group['id'] == $group_member['gid'] && isset($group['newid'])) {
-                                       $group_member['gid'] = $group['newid'];
-                                       $import++;
-                                       break;
-                               }
-                       }
-                       foreach ($account['contact'] as $contact) {
-                               if ($contact['id'] == $group_member['contact-id'] && isset($contact['newid'])) {
-                                       $group_member['contact-id'] = $contact['newid'];
-                                       $import++;
-                                       break;
-                               }
-                       }
-                       if ($import == 2) {
-                               $r = self::dbImportAssoc('group_member', $group_member);
-                               if ($r === false) {
-                                       Logger::warning("uimport:insert group member " . $group_member['id'] . " : ERROR : " . DBA::errorMessage());
-                               }
-                       }
-               }
-
-               foreach ($account['profile'] as &$profile) {
-                       unset($profile['id']);
-                       $profile['uid'] = $newuid;
-
-                       foreach ($profile as $k => &$v) {
-                               $v = str_replace([$oldbaseurl, $oldaddr], [$newbaseurl, $newaddr], $v);
-                               foreach (["profile", "avatar"] as $k) {
-                                       $v = str_replace($oldbaseurl . "/photo/" . $k . "/" . $olduid . ".jpg", $newbaseurl . "/photo/" . $k . "/" . $newuid . ".jpg", $v);
-                               }
-                       }
-
-                       if (count($account['profile']) === 1 || $profile['is-default']) {
-                               $r = self::dbImportAssoc('profile', $profile);
-
-                               if ($r === false) {
-                                       Logger::warning("uimport:insert profile: ERROR : " . DBA::errorMessage());
-                                       DI::sysmsg()->addNotice(DI::l10n()->t("User profile creation error"));
-                                       DBA::delete('user', ['uid' => $newuid]);
-                                       DBA::delete('profile_field', ['uid' => $newuid]);
-                                       return;
-                               }
-
-                               $profile['id'] = DBA::lastInsertId();
-                       }
-
-                       Profile::migrate($profile);
-               }
-
-               $permissionSet = DI::permissionSet()->selectDefaultForUser($newuid);
-
-               foreach ($account['profile_fields'] ?? [] as $profile_field) {
-                       $profile_field['uid'] = $newuid;
-
-                       ///@TODO Replace with permissionset import
-                       $profile_field['psid'] = $profile_field['psid'] ? $permissionSet->uid : PermissionSet::PUBLIC;
-
-                       if (self::dbImportAssoc('profile_field', $profile_field) === false) {
-                               Logger::info("uimport:insert profile field " . $profile_field['id'] . " : ERROR : " . DBA::errorMessage());
-                       }
-               }
-
-               foreach ($account['photo'] as &$photo) {
-                       $photo['uid'] = $newuid;
-                       $photo['data'] = hex2bin($photo['data']);
-
-                       $Image = new Image($photo['data'], $photo['type']);
-                       $r = Photo::store(
-                               $Image,
-                               $photo['uid'], $photo['contact-id'], //0
-                               $photo['resource-id'], $photo['filename'], $photo['album'], $photo['scale'], $photo['profile'], //1
-                               $photo['allow_cid'], $photo['allow_gid'], $photo['deny_cid'], $photo['deny_gid']
-                       );
-
-                       if ($r === false) {
-                               Logger::warning("uimport:insert photo " . $photo['resource-id'] . "," . $photo['scale'] . " : ERROR : " . DBA::errorMessage());
-                       }
-               }
-
-               foreach ($account['pconfig'] as &$pconfig) {
-                       $pconfig['uid'] = $newuid;
-                       $r = self::dbImportAssoc('pconfig', $pconfig);
-                       if ($r === false) {
-                               Logger::warning("uimport:insert pconfig " . $pconfig['id'] . " : ERROR : " . DBA::errorMessage());
-                       }
-               }
-
-               // send relocate messages
-               Worker::add(Worker::PRIORITY_HIGH, 'Notifier', Delivery::RELOCATION, $newuid);
-
-               DI::sysmsg()->addInfo(DI::l10n()->t("Done. You can now login with your username and password"));
-               DI::baseUrl()->redirect('login');
-       }
-}
diff --git a/src/Module/User/Import.php b/src/Module/User/Import.php
new file mode 100644 (file)
index 0000000..5083111
--- /dev/null
@@ -0,0 +1,415 @@
+<?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\User;
+
+use Friendica\App;
+use Friendica\Core\Config\Capability\IManageConfigValues;
+use Friendica\Core\L10n;
+use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
+use Friendica\Core\Protocol;
+use Friendica\Core\Renderer;
+use Friendica\Core\System;
+use Friendica\Core\Worker;
+use Friendica\Database\Database;
+use Friendica\Database\DBA;
+use Friendica\Database\DBStructure;
+use Friendica\Model\Photo;
+use Friendica\Model\Profile;
+use Friendica\Module\Response;
+use Friendica\Navigation\SystemMessages;
+use Friendica\Network\HTTPException;
+use Friendica\Object\Image;
+use Friendica\Security\PermissionSet\Repository\PermissionSet;
+use Friendica\Util\Profiler;
+use Friendica\Util\Strings;
+use Friendica\Worker\Delivery;
+use Psr\Log\LoggerInterface;
+
+class Import extends \Friendica\BaseModule
+{
+       const IMPORT_DEBUG = false;
+
+       /** @var App */
+       private $app;
+
+       /** @var IManageConfigValues */
+       private $config;
+
+       /** @var IManagePersonalConfigValues */
+       private $pconfig;
+
+       /** @var SystemMessages */
+       private $systemMessages;
+
+       /** @var Database */
+       private $database;
+
+       /** @var PermissionSet */
+       private $permissionSet;
+
+       public function __construct(PermissionSet $permissionSet, IManagePersonalConfigValues $pconfig, Database $database, SystemMessages $systemMessages, IManageConfigValues $config, App $app, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
+       {
+               parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
+
+               $this->app            = $app;
+               $this->config         = $config;
+               $this->pconfig        = $pconfig;
+               $this->systemMessages = $systemMessages;
+               $this->database       = $database;
+               $this->permissionSet  = $permissionSet;
+       }
+
+       protected function post(array $request = [])
+       {
+               if ($this->config->get('config', 'register_policy') != \Friendica\Module\Register::OPEN && !$this->app->isSiteAdmin()) {
+                       throw new HttpException\ForbiddenException($this->t('Permission denied.'));
+               }
+
+               $max_dailies = intval($this->config->get('system', 'max_daily_registrations'));
+               if ($max_dailies) {
+                       $total = $this->database->count('user', ["`register_date` > UTC_TIMESTAMP - INTERVAL 1 DAY"]);
+                       if ($total >= $max_dailies) {
+                               throw new HttpException\ForbiddenException($this->t('Permission denied.'));
+                       }
+               }
+
+               if (!empty($_FILES['accountfile'])) {
+                       $this->importAccount($_FILES['accountfile']);
+               }
+       }
+
+       protected function content(array $request = []): string
+       {
+               if (($this->config->get('config', 'register_policy') != \Friendica\Module\Register::OPEN) && !$this->app->isSiteAdmin()) {
+                       $this->systemMessages->addNotice($this->t('User imports on closed servers can only be done by an administrator.'));
+               }
+
+               $max_dailies = intval($this->config->get('system', 'max_daily_registrations'));
+               if ($max_dailies) {
+                       $total = $this->database->count('user', ["`register_date` > UTC_TIMESTAMP - INTERVAL 1 DAY"]);
+                       if ($total >= $max_dailies) {
+                               $this->logger->notice('max daily registrations exceeded.');
+                               $this->systemMessages->addNotice($this->t('This site has exceeded the number of allowed daily account registrations. Please try again tomorrow.'));
+                       }
+               }
+
+               $tpl = Renderer::getMarkupTemplate('user/import.tpl');
+               return Renderer::replaceMacros($tpl, [
+                       '$regbutt' => $this->t('Import'),
+                       '$import'  => [
+                               'title'    => $this->t('Move account'),
+                               'intro'    => $this->t('You can import an account from another Friendica server.'),
+                               'instruct' => $this->t('You need to export your account from the old server and upload it here. We will recreate your old account here with all your contacts. We will try also to inform your friends that you moved here.'),
+                               'warn'     => $this->t("This feature is experimental. We can't import contacts from the OStatus network (GNU Social/Statusnet) or from Diaspora"),
+                               'field'    => ['accountfile', $this->t('Account file'), '<input id="id_accountfile" name="accountfile" type="file">', $this->t('To export your account, go to "Settings->Export your personal data" and select "Export account"')],
+                       ],
+               ]);
+       }
+
+       private function lastInsertId(): int
+       {
+               if (self::IMPORT_DEBUG) {
+                       return 1;
+               }
+
+               return $this->database->lastInsertId();
+       }
+
+       /**
+        * Remove columns from array $arr that aren't in table $table
+        *
+        * @param string $table Table name
+        * @param array &$arr   Column=>Value array from json (by ref)
+        * @throws \Exception
+        */
+       private function checkCols(string $table, array &$arr)
+       {
+               $tableColumns = DBStructure::getColumns($table);
+
+               $tcols = [];
+               $ttype = [];
+               // get a plain array of column names
+               foreach ($tableColumns as $tcol) {
+                       $tcols[]               = $tcol['Field'];
+                       $ttype[$tcol['Field']] = $tcol['Type'];
+               }
+
+               // remove inexistent columns
+               foreach ($arr as $icol => $ival) {
+                       if (!in_array($icol, $tcols)) {
+                               unset($arr[$icol]);
+                               continue;
+                       }
+
+                       if ($ttype[$icol] === 'datetime') {
+                               $arr[$icol] = $ival ?? DBA::NULL_DATETIME;
+                       }
+               }
+       }
+
+       /**
+        * Import data into table $table
+        *
+        * @param string $table Table name
+        * @param array  $arr   Column=>Value array from json
+        * @return bool
+        * @throws \Exception
+        */
+       private function dbImportAssoc(string $table, array $arr): bool
+       {
+               if (isset($arr['id'])) {
+                       unset($arr['id']);
+               }
+
+               $this->checkCols($table, $arr);
+
+               if (self::IMPORT_DEBUG) {
+                       return true;
+               }
+
+               return $this->database->insert($table, $arr);
+       }
+
+       /**
+        * Import account file exported from mod/uexport
+        *
+        * @param array $file array from $_FILES
+        * @return void
+        * @throws HTTPException\FoundException
+        * @throws HTTPException\InternalServerErrorException
+        * @throws HTTPException\MovedPermanentlyException
+        * @throws HTTPException\TemporaryRedirectException
+        * @throws \ImagickException
+        */
+       private function importAccount(array $file)
+       {
+               $this->logger->notice('Start user import from ' . $file['tmp_name']);
+               /*
+               STEPS
+               1. checks
+               2. replace old baseurl with new baseurl
+               3. import data (look at user id and contacts id)
+               4. archive non-dfrn contacts
+               5. send message to dfrn contacts
+               */
+
+               $account = json_decode(file_get_contents($file['tmp_name']), true);
+               if ($account === null) {
+                       $this->systemMessages->addNotice($this->t('Error decoding account file'));
+                       return;
+               }
+
+               if (empty($account['version'])) {
+                       $this->systemMessages->addNotice($this->t('Error! No version data in file! This is not a Friendica account file?'));
+                       return;
+               }
+
+               // check for username
+               // check if username matches deleted account
+               if ($this->database->exists('user', ['nickname' => $account['user']['nickname']])
+                       || $this->database->exists('userd', ['username' => $account['user']['nickname']])) {
+                       $this->systemMessages->addNotice($this->t("User '%s' already exists on this server!", $account['user']['nickname']));
+                       return;
+               }
+
+               $oldBaseUrl = $account['baseurl'];
+               $newBaseUrl = $this->baseUrl;
+
+               $oldAddr = str_replace('http://', '@', Strings::normaliseLink($oldBaseUrl));
+               $newAddr = str_replace('http://', '@', Strings::normaliseLink($newBaseUrl));
+
+               if (!empty($account['profile']['addr'])) {
+                       $oldHandle = $account['profile']['addr'];
+               } else {
+                       $oldHandle = $account['user']['nickname'] . $oldAddr;
+               }
+
+               // Creating a new guid to avoid problems with Diaspora
+               $account['user']['guid'] = System::createUUID();
+
+               $oldUid = $account['user']['uid'];
+
+               unset($account['user']['uid']);
+               unset($account['user']['account_expired']);
+               unset($account['user']['account_expires_on']);
+               unset($account['user']['expire_notification_sent']);
+
+               array_walk($account['user'], function (&$user) use ($oldBaseUrl, $oldAddr, $newBaseUrl, $newAddr) {
+                       $user = str_replace([$oldBaseUrl, $oldAddr], [$newBaseUrl, $newAddr], $user);
+               });
+
+               // import user
+               if ($this->dbImportAssoc('user', $account['user']) === false) {
+                       $this->logger->warning('Error inserting user', ['user' => $account['user'], 'error' => $this->database->errorMessage()]);
+                       $this->systemMessages->addNotice($this->t('User creation error'));
+                       return;
+               }
+
+               $newUid = $this->lastInsertId();
+
+               $this->pconfig->set($newUid, 'system', 'previous_addr', $oldHandle);
+
+               $errorCount = 0;
+
+               array_walk($account['contact'], function (&$contact) use (&$errorCount, $oldUid, $oldBaseUrl, $oldAddr, $newUid, $newBaseUrl, $newAddr) {
+                       if ($contact['uid'] == $oldUid && $contact['self'] == '1') {
+                               array_walk($contact, function (&$field) use ($oldUid, $oldBaseUrl, $oldAddr, $newUid, $newBaseUrl, $newAddr) {
+                                       $field = str_replace([$oldBaseUrl, $oldAddr], [$newBaseUrl, $newAddr], $field);
+                                       foreach (['profile', 'avatar', 'micro'] as $key) {
+                                               $field = str_replace($oldBaseUrl . '/photo/' . $key . '/' . $oldUid . '.jpg', $newBaseUrl . '/photo/' . $key . '/' . $newUid . '.jpg', $field);
+                                       }
+                               });
+                       }
+
+                       if ($contact['uid'] == $oldUid && $contact['self'] == '0') {
+                               // set contacts 'avatar-date' to NULL_DATE to let worker update the URLs
+                               $contact['avatar-date'] = DBA::NULL_DATETIME;
+
+                               switch ($contact['network']) {
+                                       case Protocol::DFRN:
+                                       case Protocol::DIASPORA:
+                                               //  send relocate message (below)
+                                               break;
+                                       case Protocol::FEED:
+                                       case Protocol::MAIL:
+                                               // Nothing to do
+                                               break;
+                                       default:
+                                               // archive other contacts
+                                               $contact['archive'] = '1';
+                               }
+                       }
+
+                       $contact['uid'] = $newUid;
+                       if ($this->dbImportAssoc('contact', $contact) === false) {
+                               $this->logger->warning('Error inserting contact', ['nick' => $contact['nick'], 'network' => $contact['network'], 'error' => $this->database->errorMessage()]);
+                               $errorCount++;
+                       } else {
+                               $contact['newid'] = $this->lastInsertId();
+                       }
+               });
+
+               if ($errorCount > 0) {
+                       $this->systemMessages->addNotice($this->tt('%d contact not imported', '%d contacts not imported', $errorCount));
+               }
+
+               array_walk($account['group'], function (&$group) use ($newUid) {
+                       $group['uid'] = $newUid;
+                       if ($this->dbImportAssoc('group', $group) === false) {
+                               $this->logger->warning('Error inserting group', ['name' => $group['name'], 'error' => $this->database->errorMessage()]);
+                       } else {
+                               $group['newid'] = $this->lastInsertId();
+                       }
+               });
+
+               foreach ($account['group_member'] as $group_member) {
+                       $import = 0;
+                       foreach ($account['group'] as $group) {
+                               if ($group['id'] == $group_member['gid'] && isset($group['newid'])) {
+                                       $group_member['gid'] = $group['newid'];
+                                       $import++;
+                                       break;
+                               }
+                       }
+
+                       foreach ($account['contact'] as $contact) {
+                               if ($contact['id'] == $group_member['contact-id'] && isset($contact['newid'])) {
+                                       $group_member['contact-id'] = $contact['newid'];
+                                       $import++;
+                                       break;
+                               }
+                       }
+
+                       if ($import == 2 && $this->dbImportAssoc('group_member', $group_member) === false) {
+                               $this->logger->warning('Error inserting group member', ['gid' => $group_member['id'], 'error' => $this->database->errorMessage()]);
+                       }
+               }
+
+               foreach ($account['profile'] as $profile) {
+                       unset($profile['id']);
+                       $profile['uid'] = $newUid;
+
+                       array_walk($profile, function (&$field) use ($oldUid, $oldBaseUrl, $oldAddr, $newUid, $newBaseUrl, $newAddr) {
+                               $field = str_replace([$oldBaseUrl, $oldAddr], [$newBaseUrl, $newAddr], $field);
+                               foreach (['profile', 'avatar'] as $key) {
+                                       $field = str_replace($oldBaseUrl . '/photo/' . $key . '/' . $oldUid . '.jpg', $newBaseUrl . '/photo/' . $key . '/' . $newUid . '.jpg', $field);
+                               }
+                       });
+
+                       if (count($account['profile']) === 1 || $profile['is-default']) {
+                               if ($this->dbImportAssoc('profile', $profile) === false) {
+                                       $this->logger->warning('Error inserting profile', ['error' => $this->database->errorMessage()]);
+                                       $this->systemMessages->addNotice($this->t('User profile creation error'));
+                                       $this->database->delete('user', ['uid' => $newUid]);
+                                       $this->database->delete('profile_field', ['uid' => $newUid]);
+                                       return;
+                               }
+
+                               $profile['id'] = $this->database->lastInsertId();
+                       }
+
+                       Profile::migrate($profile);
+               }
+
+               $permissionSet = $this->permissionSet->selectDefaultForUser($newUid);
+
+               foreach ($account['profile_fields'] ?? [] as $profile_field) {
+                       $profile_field['uid'] = $newUid;
+
+                       ///@TODO Replace with permissionset import
+                       $profile_field['psid'] = $profile_field['psid'] ? $permissionSet->id : PermissionSet::PUBLIC;
+
+                       if ($this->dbImportAssoc('profile_field', $profile_field) === false) {
+                               $this->logger->info('Error inserting profile field', ['profile_id' => $profile_field['id'], 'error' => $this->database->errorMessage()]);
+                       }
+               }
+
+               foreach ($account['photo'] as $photo) {
+                       $photo['uid']  = $newUid;
+                       $photo['data'] = hex2bin($photo['data']);
+
+                       $r = Photo::store(
+                               new Image($photo['data'], $photo['type']),
+                               $photo['uid'], $photo['contact-id'], //0
+                               $photo['resource-id'], $photo['filename'], $photo['album'], $photo['scale'], $photo['profile'], //1
+                               $photo['allow_cid'], $photo['allow_gid'], $photo['deny_cid'], $photo['deny_gid']
+                       );
+
+                       if ($r === false) {
+                               $this->logger->warning('Error inserting photo', ['resource-id' => $photo['resource-id'], 'scale' => $photo['scale'], 'error' => $this->database->errorMessage()]);
+                       }
+               }
+
+               foreach ($account['pconfig'] as $pconfig) {
+                       $pconfig['uid'] = $newUid;
+                       if ($this->dbImportAssoc('pconfig', $pconfig) === false) {
+                               $this->logger->warning('Error inserting pconfig', ['pconfig_id' => $pconfig['id'], 'error' => $this->database->errorMessage()]);
+                       }
+               }
+
+               // send relocate messages
+               Worker::add(Worker::PRIORITY_HIGH, 'Notifier', Delivery::RELOCATION, $newUid);
+
+               $this->systemMessages->addInfo($this->t('Done. You can now login with your username and password'));
+               $this->baseUrl->redirect('login');
+       }
+}
index c03a4b77105fa8f85ecfc1644557de2a64d1146a..53bbf3eb7f95c5492888b977b4a58a898cb3dd32 100644 (file)
@@ -611,6 +611,8 @@ return [
 
        '/update_profile'                => [Module\Update\Profile::class,        [R::GET]],
 
+       '/user/import'                   => [Module\User\Import::class,           [R::GET, R::POST]],
+
        '/view/theme/{theme}/style.pcss' => [Module\Theme::class,                 [R::GET]],
        '/viewsrc/{item:\d+}'            => [Module\Debug\ItemBody::class,        [R::GET]],
        '/webfinger'                     => [Module\Debug\WebFinger::class,       [R::GET]],
diff --git a/view/templates/user/import.tpl b/view/templates/user/import.tpl
new file mode 100644 (file)
index 0000000..e33152c
--- /dev/null
@@ -0,0 +1,13 @@
+<form action="uimport" method="post" id="uimport-form" enctype="multipart/form-data">
+       <h2>{{$import.title}}</h2>
+       <p>{{$import.intro}}</p>
+       <p>{{$import.instruct}}</p>
+       <p><b>{{$import.warn}}</b></p>
+
+       {{include file="field_custom.tpl" field=$import.field}}
+
+       <div id="register-submit-wrapper">
+               <button type="submit" name="submit" id="register-submit-button" class="btn btn-primary">{{$regbutt}}</button>
+       </div>
+       <div id="register-submit-end"></div>
+</form>