From 955a84a2667e970e66f17aa7557f92cd881a17bd Mon Sep 17 00:00:00 2001 From: Tobias Diekershoff Date: Sun, 3 Nov 2019 00:12:16 +0100 Subject: [PATCH 1/8] added export and import of followed contacts to and from CSV files --- mod/settings.php | 30 ++++++++++++++++ src/Module/Settings/UserExport.php | 35 +++++++++++++++++-- view/templates/settings/settings.tpl | 13 ++++++- .../frio/templates/settings/settings.tpl | 25 +++++++++++++ 4 files changed, 99 insertions(+), 4 deletions(-) diff --git a/mod/settings.php b/mod/settings.php index 3d471a5cc..ff38a97a9 100644 --- a/mod/settings.php +++ b/mod/settings.php @@ -390,6 +390,33 @@ function settings_post(App $a) BaseModule::checkFormSecurityTokenRedirectOnError('/settings', 'settings'); + // Import Contacts from CSV file + if (!empty($_POST['importcontact-submit'])) { + if (isset($_FILES['importcontact-filename'])) { + // was there an error + if ($_FILES['importcontact-filename']['error'] > 0) { + Logger::notice('Contact CSV file upload error'); + info(L10n::t('Contact CSV file upload error')); + } else { + $csvArray = array_map('str_getcsv', file($_FILES['importcontact-filename']['tmp_name'])); + // 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) || (strpos($csvRow[0],'http') !== false)) { + $arr = Contact::createFromProbe($_SESSION['uid'], $csvRow[0], '', false); + } + } + info(L10n::t('Importing Contacts done')); + // delete temp file + unlink($filename); + } + } + } + if (!empty($_POST['resend_relocate'])) { Worker::add(PRIORITY_HIGH, 'Notifier', Delivery::RELOCATION, local_user()); info(L10n::t("Relocate message has been send to your contacts")); @@ -1223,6 +1250,9 @@ function settings_content(App $a) '$h_descadvn' => L10n::t('Change the behaviour of this account for special situations'), '$pagetype' => $pagetype, + '$importcontact' => L10n::t('Import Contacts'), + '$importcontact_text' => 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' => L10n::t('Upload File'), '$relocate' => L10n::t('Relocate'), '$relocate_text' => 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' => L10n::t("Resend relocate message to contacts"), diff --git a/src/Module/Settings/UserExport.php b/src/Module/Settings/UserExport.php index 41072d7ef..ee6460c70 100644 --- a/src/Module/Settings/UserExport.php +++ b/src/Module/Settings/UserExport.php @@ -23,10 +23,11 @@ class UserExport extends BaseSettingsModule { /** * Handle the request to export data. - * At the moment one can export two different data set + * At the moment one can export three different data set * 1. The profile data that can be used by uimport to resettle * to a different Friendica instance * 2. The entire data-set, profile plus postings + * 3. A list of contacts as CSV file similar to the export of Mastodon * * If there is an action required through the URL / path, react * accordingly and export the requested data. @@ -42,6 +43,7 @@ class UserExport extends BaseSettingsModule $options = [ ['settings/userexport/account', L10n::t('Export account'), L10n::t('Export your account info and contacts. Use this to make a backup of your account and/or to move it to another server.')], ['settings/userexport/backup', L10n::t('Export all'), L10n::t("Export your accout info, contacts and all your items as json. Could be a very big file, and could take a lot of time. Use this to make a full backup of your account \x28photos are not exported\x29")], + ['settings/userexport/contactcsv', L10n::t('Export Contacts to CSV'), L10n::t("Export the list of the accounts you are following as CSV file. Compatible to e.g. Mastodon.")], ]; Hook::callAll('uexport_options', $options); @@ -64,17 +66,25 @@ class UserExport extends BaseSettingsModule // @TODO Replace with router-provided arguments $action = $args->get(2); $user = self::getApp()->user; - header("Content-type: application/json"); - header('Content-Disposition: attachment; filename="' . $user['nickname'] . '.' . $action . '"'); switch ($action) { case "backup": + header("Content-type: application/json"); + header('Content-Disposition: attachment; filename="' . $user['nickname'] . '.' . $action . '"'); self::exportAll(self::getApp()); exit(); break; case "account": + header("Content-type: application/json"); + header('Content-Disposition: attachment; filename="' . $user['nickname'] . '.' . $action . '"'); self::exportAccount(self::getApp()); exit(); break; + case "contactcsv": + header("Content-type: application/csv"); + header('Content-Disposition: attachment; filename="' . $user['nickname'] . '-contacts.csv'. '"'); + self::exportContactsAsCSV(self::getApp()); + exit(); + break; default: exit(); } @@ -134,6 +144,25 @@ class UserExport extends BaseSettingsModule return $result; } + /** + * Export a list of the contacts as CSV file as e.g. Mastodon and Pleroma are doing. + * + * @param App $a the app data + **/ + private static function exportContactsAsCSV(App $a) + { + // write the table header (like Mastodon) + echo "Account address, Show boosts\n"; + // get all the contacts + $query = sprintf("SELECT addr FROM `contact` WHERE `uid` = %d AND `self` = 0 AND rel IN (1,3) AND NOT `deleted`;", intval($_SESSION['uid'])); + $contacts = q($query); + if (DBA::isResult($contacts)) { + foreach ($contacts as $contact) { + // export row, set Show boosts to true + echo $contact['addr'] . ", true\n"; + } + } + } private static function exportAccount(App $a) { $user = self::exportRow( diff --git a/view/templates/settings/settings.tpl b/view/templates/settings/settings.tpl index c95c0b143..8ecc332d7 100644 --- a/view/templates/settings/settings.tpl +++ b/view/templates/settings/settings.tpl @@ -2,7 +2,7 @@ {{$nickname_block nofilter}} -
+

{{$h_pass}}

@@ -196,6 +196,17 @@ +

{{$importcontact}}

+
+ +
{{$importcontact_text}}
+ + +
+ +
+
+

{{$relocate}}

{{$relocate_text}}
diff --git a/view/theme/frio/templates/settings/settings.tpl b/view/theme/frio/templates/settings/settings.tpl index 700c2c06c..d35094194 100644 --- a/view/theme/frio/templates/settings/settings.tpl +++ b/view/theme/frio/templates/settings/settings.tpl @@ -270,6 +270,31 @@
+ {{* Import contacts CSV *}} +
+ +
+
+ +
{{$importcontact_text}}
+ + + +
+
+ +
+
+
+
+
+ {{* The relocate setting section *}}