]> git.mxchange.org Git - friendica.git/commitdiff
added export and import of followed contacts to and from CSV files
authorTobias Diekershoff <tobias.diekershoff@gmx.net>
Sat, 2 Nov 2019 23:12:16 +0000 (00:12 +0100)
committerTobias Diekershoff <tobias.diekershoff@gmx.net>
Sat, 2 Nov 2019 23:12:16 +0000 (00:12 +0100)
mod/settings.php
src/Module/Settings/UserExport.php
view/templates/settings/settings.tpl
view/theme/frio/templates/settings/settings.tpl

index 3d471a5ccad2f68df49647706f538e1850194d7b..ff38a97a9968e602db4315949cc622bf6fa73c81 100644 (file)
@@ -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"),
index 41072d7ef5882e0299857f430a2b79c7ba064545..ee6460c7024b85cc99b83c813b84ae39fe5a56a9 100644 (file)
@@ -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(
index c95c0b14307787e6935a6f60f953ac5d4482eede..8ecc332d7d5e2a28f3a30cd8534977a144e0f349 100644 (file)
@@ -2,7 +2,7 @@
 
 {{$nickname_block nofilter}}
 
-<form action="settings" id="settings-form" method="post" autocomplete="off" >
+<form action="settings" id="settings-form" method="post" autocomplete="off" enctype="multipart/form-data" >
 <input type='hidden' name='form_security_token' value='{{$form_security_token}}'>
 
 <h3 class="settings-heading"><a href="javascript:;">{{$h_pass}}</a></h3>
 </div>
 </div>
 
+<h3 class="settings-heading"><a href="javascript:;">{{$importcontact}}</a></h3>
+<div class="settings-content-block">
+<input type="hidden" name="MAX_FILE_SIZE" value="30720" />
+<div id="settings-pagetype-desc">{{$importcontact_text}}</div>
+<input type="file" name="importcontact-filename" />
+
+<div class="settings-submit-wrapper" >
+<input type="submit" name="importcontact-submit" class="importcontact-submit" value="{{$importcontact_button}}" />
+</div>
+</div>
+
 <h3 class="settings-heading"><a href="javascript:;">{{$relocate}}</a></h3>
 <div class="settings-content-block">
 <div id="settings-pagetype-desc">{{$relocate_text}}</div>
index 700c2c06c4c24d4d6ffe458059ee8d31c197fa92..d35094194398982067a39719109fa3938ca405fc 100644 (file)
                                </div>
                        </div>
 
+                       {{* Import contacts CSV *}}
+                       <div class="panel">
+                               <div class="section-subtitle-wrapper" role="tab" id="importcontact-settings">
+                                       <h4>
+                                               <a class="accordion-toggle collapsed" data-toggle="collapse" data-parent="#settings" href="#importcontact-settings-collapse" aria-expanded="false" aria-controls="importcontact-settings-collapse">
+                                                       {{$importcontact}}
+                                               </a>
+                                       </h4>
+                               </div>
+                               <div id="importcontact-settings-collapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="importcontact-settings">
+                                       <div class="section-content-tools-wrapper">
+
+                                               <div id="importcontact-relocate-desc">{{$importcontact_text}}</div>
+                                               <input type="hidden" name="MAX_FILE_SIZE" value="30720" />
+                                               <input type="file" name="importcontact-filename" />
+
+                                               <br/>
+                                               <div class="form-group pull-right settings-submit-wrapper" >
+                                                       <button type="submit" name="importcontact-submit" class="btn btn-primary" value="{{$importcontact_button}}">{{$importcontact_button}}</button>
+                                               </div>
+                                               <div class="clear"></div>
+                                       </div>
+                               </div>
+                       </div>
+
                        {{* The relocate setting section *}}
                        <div class="panel">
                                <div class="section-subtitle-wrapper" role="tab" id="relocate-settings">