Merge pull request #7806 from tobiasd/20191102-csvexport
added export and import of followed contacts to and from CSV files
This commit is contained in:
commit
d0068170db
23
doc/Export-Import-Contacts.md
Normal file
23
doc/Export-Import-Contacts.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Export / Import of followed Contacts
|
||||||
|
|
||||||
|
* [Home](help)
|
||||||
|
|
||||||
|
In addition to [move your account](help/Move-Account) you can export and import the list of accounts you follow.
|
||||||
|
The exported list is stored as CSV file that is compatible to the format used by other platforms as e.g. Mastodon or Pleroma.
|
||||||
|
|
||||||
|
## Export of followed Contacts
|
||||||
|
|
||||||
|
To export the list of accounts that you follow, go to the [Settings Export personal date](settings/userexport) and click the [Export Contacts to CSV](settings/userexport/contact).
|
||||||
|
|
||||||
|
## Import of followed Contacts
|
||||||
|
|
||||||
|
To import contacts from a CSV file, go to the [Settings page](settings).
|
||||||
|
At the bottom of the *account settings* page you'll find the *import contacts* section.
|
||||||
|
Upload the CSV file there.
|
||||||
|
|
||||||
|
### Supported File Format
|
||||||
|
|
||||||
|
The CSV file *must* contain at least one column.
|
||||||
|
In the first column the table should contain either the handle or URL of an followed account.
|
||||||
|
(one account per row.)
|
||||||
|
Other columns in the CSV file will be ignored.
|
|
@ -21,6 +21,7 @@ Friendica Documentation and Resources
|
||||||
* [Chats](help/Chats)
|
* [Chats](help/Chats)
|
||||||
* Further information
|
* Further information
|
||||||
* [Move your account](help/Move-Account)
|
* [Move your account](help/Move-Account)
|
||||||
|
* [Export / Import of followed Contacts](help/Export-Import-Contacts)
|
||||||
* [Delete your account](help/Remove-Account)
|
* [Delete your account](help/Remove-Account)
|
||||||
* [Frequently asked questions (FAQ)](help/FAQ)
|
* [Frequently asked questions (FAQ)](help/FAQ)
|
||||||
|
|
||||||
|
|
23
doc/de/Export-Import-Contacts.md
Normal file
23
doc/de/Export-Import-Contacts.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Export / Import von gefolgten Kontakte
|
||||||
|
|
||||||
|
* [Home](help)
|
||||||
|
|
||||||
|
Zusätzlich zum [Umziehen des Accounts](help/Move-Account) kannst du die Liste der von dir gefolgten Kontakte exportieren und importieren.
|
||||||
|
Die exportierte Liste wird als CSV Datei in einem zu anderen Plattformen, z.B. Mastodon oder Pleroma, kompatiblen Format gespeichert.
|
||||||
|
|
||||||
|
## Export der gefolgten Kontakte
|
||||||
|
|
||||||
|
Um die Liste der Kontakte *denen du folgst* zu exportieren, geht die [Einstellungen Persönliche Daten exportieren](settings/userexport) und klicke den [Exportiere Kontakte als CSV](settings/userexport/contact) an.
|
||||||
|
|
||||||
|
## Import der gefolgten Kontakte
|
||||||
|
|
||||||
|
Um die Kontakt CSV Datei zu importieren, gehe in die [Einstellungen](settings).
|
||||||
|
Am Ende der Einstellungen zum Nutzerkonto findest du den Abschnitt "Kontakte Importieren".
|
||||||
|
Hier kannst du die CSV Datei auswählen und hoch laden.
|
||||||
|
|
||||||
|
### Unterstütztes Datei Format
|
||||||
|
|
||||||
|
Die CSV Datei *muss* mindestens eine Spalte beinhalten.
|
||||||
|
In der ersten Spalte der Tabelle sollte *sollte* entweder das Handle oder die URL des gefolgten Kontakts.
|
||||||
|
(Ein Kontakt pro Zeile.)
|
||||||
|
Alle anderen Spalten der CSV Datei werden beim Importieren ignoriert.
|
|
@ -21,6 +21,7 @@ Friendica - Dokumentation und Ressourcen
|
||||||
* [Chats](help/Chats)
|
* [Chats](help/Chats)
|
||||||
* Weiterführende Informationen
|
* Weiterführende Informationen
|
||||||
* [Account umziehen](help/Move-Account)
|
* [Account umziehen](help/Move-Account)
|
||||||
|
* [Export / Import gefolgter Kontakte](help/Export-Import-Contacts)
|
||||||
* [Account löschen](help/Remove-Account)
|
* [Account löschen](help/Remove-Account)
|
||||||
* [Bugs und Probleme](help/Bugs-and-Issues)
|
* [Bugs und Probleme](help/Bugs-and-Issues)
|
||||||
* [Häufig gestellte Fragen (FAQ)](help/FAQ)
|
* [Häufig gestellte Fragen (FAQ)](help/FAQ)
|
||||||
|
|
|
@ -390,6 +390,33 @@ function settings_post(App $a)
|
||||||
|
|
||||||
BaseModule::checkFormSecurityTokenRedirectOnError('/settings', 'settings');
|
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'])) {
|
if (!empty($_POST['resend_relocate'])) {
|
||||||
Worker::add(PRIORITY_HIGH, 'Notifier', Delivery::RELOCATION, local_user());
|
Worker::add(PRIORITY_HIGH, 'Notifier', Delivery::RELOCATION, local_user());
|
||||||
info(L10n::t("Relocate message has been send to your contacts"));
|
info(L10n::t("Relocate message has been send to your contacts"));
|
||||||
|
@ -1223,6 +1250,10 @@ function settings_content(App $a)
|
||||||
'$h_descadvn' => L10n::t('Change the behaviour of this account for special situations'),
|
'$h_descadvn' => L10n::t('Change the behaviour of this account for special situations'),
|
||||||
'$pagetype' => $pagetype,
|
'$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'),
|
||||||
|
'$importcontact_maxsize' => Config::get('system', max_csv_file_size, 30720),
|
||||||
'$relocate' => L10n::t('Relocate'),
|
'$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_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"),
|
'$relocate_button' => L10n::t("Resend relocate message to contacts"),
|
||||||
|
|
|
@ -23,10 +23,11 @@ class UserExport extends BaseSettingsModule
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Handle the request to export data.
|
* 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
|
* 1. The profile data that can be used by uimport to resettle
|
||||||
* to a different Friendica instance
|
* to a different Friendica instance
|
||||||
* 2. The entire data-set, profile plus postings
|
* 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
|
* If there is an action required through the URL / path, react
|
||||||
* accordingly and export the requested data.
|
* accordingly and export the requested data.
|
||||||
|
@ -42,6 +43,7 @@ class UserExport extends BaseSettingsModule
|
||||||
$options = [
|
$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/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/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/contact', 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);
|
Hook::callAll('uexport_options', $options);
|
||||||
|
|
||||||
|
@ -64,17 +66,25 @@ class UserExport extends BaseSettingsModule
|
||||||
// @TODO Replace with router-provided arguments
|
// @TODO Replace with router-provided arguments
|
||||||
$action = $args->get(2);
|
$action = $args->get(2);
|
||||||
$user = self::getApp()->user;
|
$user = self::getApp()->user;
|
||||||
header("Content-type: application/json");
|
|
||||||
header('Content-Disposition: attachment; filename="' . $user['nickname'] . '.' . $action . '"');
|
|
||||||
switch ($action) {
|
switch ($action) {
|
||||||
case "backup":
|
case "backup":
|
||||||
|
header("Content-type: application/json");
|
||||||
|
header('Content-Disposition: attachment; filename="' . $user['nickname'] . '.' . $action . '"');
|
||||||
self::exportAll(self::getApp());
|
self::exportAll(self::getApp());
|
||||||
exit();
|
exit();
|
||||||
break;
|
break;
|
||||||
case "account":
|
case "account":
|
||||||
|
header("Content-type: application/json");
|
||||||
|
header('Content-Disposition: attachment; filename="' . $user['nickname'] . '.' . $action . '"');
|
||||||
self::exportAccount(self::getApp());
|
self::exportAccount(self::getApp());
|
||||||
exit();
|
exit();
|
||||||
break;
|
break;
|
||||||
|
case "contact":
|
||||||
|
header("Content-type: application/csv");
|
||||||
|
header('Content-Disposition: attachment; filename="' . $user['nickname'] . '-contacts.csv'. '"');
|
||||||
|
self::exportContactsAsCSV();
|
||||||
|
exit();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
@ -134,6 +144,20 @@ class UserExport extends BaseSettingsModule
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export a list of the contacts as CSV file as e.g. Mastodon and Pleroma are doing.
|
||||||
|
**/
|
||||||
|
private static function exportContactsAsCSV()
|
||||||
|
{
|
||||||
|
// write the table header (like Mastodon)
|
||||||
|
echo "Account address, Show boosts\n";
|
||||||
|
// get all the contacts
|
||||||
|
$contacts = DBA::select('contact', ['addr'], ['uid' => $_SESSION['uid'], 'self' => false, 'rel' => [1,3], 'deleted' => false]);
|
||||||
|
while ($contact = DBA::fetch($contacts)) {
|
||||||
|
echo $contact['addr'] . ", true\n";
|
||||||
|
}
|
||||||
|
DBA::close($contacts);
|
||||||
|
}
|
||||||
private static function exportAccount(App $a)
|
private static function exportAccount(App $a)
|
||||||
{
|
{
|
||||||
$user = self::exportRow(
|
$user = self::exportRow(
|
||||||
|
|
|
@ -124,6 +124,12 @@ return [
|
||||||
// The fully-qualified URL of this Friendica node.
|
// The fully-qualified URL of this Friendica node.
|
||||||
// Used by the worker in a non-HTTP execution environment.
|
// Used by the worker in a non-HTTP execution environment.
|
||||||
'url' => '',
|
'url' => '',
|
||||||
|
|
||||||
|
// max_csv_file_size (Integer)
|
||||||
|
// When uploading a CSV with account addresses to follow
|
||||||
|
// in the user settings, this controls the maximum file
|
||||||
|
// size of the upload file.
|
||||||
|
'max_csv_file_size' => 30720,
|
||||||
],
|
],
|
||||||
|
|
||||||
// Used in the admin settings to lock certain features
|
// Used in the admin settings to lock certain features
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
{{$nickname_block nofilter}}
|
{{$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}}'>
|
<input type='hidden' name='form_security_token' value='{{$form_security_token}}'>
|
||||||
|
|
||||||
<h3 class="settings-heading"><a href="javascript:;">{{$h_pass}}</a></h3>
|
<h3 class="settings-heading"><a href="javascript:;">{{$h_pass}}</a></h3>
|
||||||
|
@ -196,6 +196,17 @@
|
||||||
</div>
|
</div>
|
||||||
</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="{{$importcontact_maxsize}}" />
|
||||||
|
<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>
|
<h3 class="settings-heading"><a href="javascript:;">{{$relocate}}</a></h3>
|
||||||
<div class="settings-content-block">
|
<div class="settings-content-block">
|
||||||
<div id="settings-pagetype-desc">{{$relocate_text}}</div>
|
<div id="settings-pagetype-desc">{{$relocate_text}}</div>
|
||||||
|
|
|
@ -270,6 +270,31 @@
|
||||||
</div>
|
</div>
|
||||||
</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="{{$importcontact_maxsize}}" />
|
||||||
|
<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 *}}
|
{{* The relocate setting section *}}
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<div class="section-subtitle-wrapper" role="tab" id="relocate-settings">
|
<div class="section-subtitle-wrapper" role="tab" id="relocate-settings">
|
||||||
|
|
Loading…
Reference in a new issue