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"));
'$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"),
{
/**
* 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.
$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);
// @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();
}
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(
{{$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>
</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">