Merge branch 'develop' into refactor-entrypoint-auth_ejabberd.php

This commit is contained in:
Art4 2024-12-26 06:31:21 +00:00
commit e59dd9d9d4
35 changed files with 635 additions and 638 deletions

View file

@ -94,9 +94,9 @@ steps:
- friendica/friendica
commands:
- codecov -R '.' -Z -f 'clover.xml'
secrets:
- source: codecov-token
target: codecov_token
environment:
CODECOV_TOKEN:
from_secret: codecov-token
services:
mariadb:

View file

@ -604,7 +604,7 @@ Hook data:
### follow
Called before adding a new contact for a user to handle non-native network remote contact (like Twitter).
Called before adding a new contact for a user to handle non-native network remote contact (like Bluesky).
Hook data:
@ -613,7 +613,7 @@ Hook data:
### unfollow
Called when unfollowing a remote contact on a non-native network (like Twitter)
Called when unfollowing a remote contact on a non-native network (like Bluesky)
Hook data:
- **contact** (input): the target public contact (uid = 0) array.
@ -622,7 +622,7 @@ Hook data:
### revoke_follow
Called when making a remote contact on a non-native network (like Twitter) unfollow you.
Called when making a remote contact on a non-native network (like Bluesky) unfollow you.
Hook data:
- **contact** (input): the target public contact (uid = 0) array.
@ -631,7 +631,7 @@ Hook data:
### block
Called when blocking a remote contact on a non-native network (like Twitter).
Called when blocking a remote contact on a non-native network (like Bluesky).
Hook data:
- **contact** (input): the remote contact (uid = 0) array.
@ -640,7 +640,7 @@ Hook data:
### unblock
Called when unblocking a remote contact on a non-native network (like Twitter).
Called when unblocking a remote contact on a non-native network (like Bluesky).
Hook data:
- **contact** (input): the remote contact (uid = 0) array.

View file

@ -95,14 +95,6 @@ However we take privacy seriously and don't behave like some networks that __pre
Your profile and "wall" may also be visited by your friends from other networks, and you can block access to these by web visitors that Friendica doesn't know.
Be aware that this could include some of your friends on other networks.
This may produce undesired results when posting a long status message to (for instance) Twitter.
When Friendica sends a post to these networks which exceeds the service length limit, we truncate it and provide a link to the original.
The original is a link back to your Friendica profile.
As Friendica cannot prove who they are, it may not be possible for these people to view your post in full.
For people in this situation we would recommend providing a "Twitter-length" summary, with more detail for friends that can see the post in full.
You can do so by including the BBCode tag *abstract* in your posting.
Blocking your profile or entire Friendica site from unknown web visitors also has serious implications for communicating with GNU Social members.
These networks communicate with others via public protocols that are not authenticated.
In order to view your posts, these networks have to access them as an "unknown web visitor".

View file

@ -4,15 +4,10 @@ Connectors
* [Home](help)
Connectors allow you to connect with external social networks and services.
They are only required for posting to existing accounts on Twitter or GNU Social.
They are only required for posting to existing accounts on for example Bluesky, Tumblr or Twitter.
For Bluesky and Tumblr you can also enable a bidirectional synchronisation, so that you can use Friendica to read your timeline from Tumblr or Bluesky.
There is also a connector for accessing your email INBOX.
If the following network connectors are installed on your system, select the following links to visit the appropriate settings page and configure them for your account:
* [Twitter](/settings/addons)
* [GNU Social](/settings/addons)
* [Email](/settings)
Instructions For Connecting To People On Specific Services
==========================================================
@ -37,14 +32,6 @@ PLease note that you will not be able to reply to these contacts.
This feed reader feature will allow you to _connect_ with millions of pages on the internet.
All that the pages need to have is a discoverable feed using either the RSS or Atom syndication format, and which provides an author name and a site image in a form which we can extract.
Twitter
---
To follow a Twitter member, the Twitter-Connector (Addon) needs to be configured on your node.
If this is the case put the URL of the Twitter member's main page into the Connect box on your [Contacts](contacts) page.
To reply, you must have the Twitter connector installed, and reply using your own status editor.
Begin the message with @twitterperson replacing with the Twitter username.
Email
---

View file

@ -11,8 +11,7 @@ To export the list of accounts that you follow, go to the [Settings Export perso
## 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.
To import contacts from a CSV file, go to the [import contacts](settings/importcontacts).
Upload the CSV file there.
### Supported File Format

View file

@ -31,7 +31,7 @@ Help
* [Install](help/Install)
* [Update](help/Update)
* [Settings & Admin Panel](help/Settings)
* [Installing Connectors (Twitter/GNU Social)](help/Installing-Connectors)
* [Installing Connectors](help/Installing-Connectors)
* [Install an ejabberd server (XMPP chat) with synchronized credentials](help/install-ejabberd)
* [Using SSL with Friendica](help/SSL)
* [Config values that can only be set in config/local.config.php](help/Config)

View file

@ -1,89 +1,34 @@
Installing Connectors (Twitter/GNU Social)
Installing Connectors
==================================================
* [Home](help)
Friendica uses add-ons to connect to some networks, such as Tumblr or Bluesky.
Friendica uses addons to provide connectivity to some networks, such as Twitter.
All of these add-ons require an account on the target network.
In addition, you (or usually the server administrator) will need to obtain an API key to allow authenticated access to your Friendica server.
There is also a addon to post through to an existing account on a GNU Social service.
You only need this to post to an already existing GNU Social account, but not to communicate with GNU Social members in general.
All three addons require an account on the target network.
In addition you (or typically the server administrator) will need to obtain an API key to provide authenticated access to your Friendica server.
Site Configuration
Site configuration
---
Addons must be installed by the site administrator before they can be used.
This is accomplished through the site administration panel.
Addons need to be installed by the site administrator before they can be used.
This is done through the site administration panel.
Each of the connectors also requires an "API key" from the service you wish to connect with.
Some addons allow you to enter this information in the site administration pages, while others may require you to edit your configuration file (config/local.config.php).
The ways to obtain these keys vary between the services, but they all require an existing account on the target service.
Once installed, these API keys can usually be shared by all site members.
Some of the connectors also require an "API key" from the service you wish to connect to.
For Tumblr, this information can be found in the site administration pages, while for Twitter (X) each user has to create their own API key.
Other connectors, such as Bluesky, don't require an API key at all.
The details of configuring each service follow (much of this information comes directly from the addon source files):
You can find more information about specific requirements on each addon's settings page, either on the admin page or the user page.
Twitter Addon for Friendica
Bluesky Jetstream
---
* Author: Tobias Diekershoff
* tobias.diekershoff@gmx.net
* License: 3-clause BSD license
To further improve connectivity to Bluesky, Admins can choose to enable 'Jetstream' connectivity.
Jetstream is a service that connects to the Bluesky firehose.
With Jetstream, messages arrive in real time rather than having to be polled.
It also enables real-time processing of blocks or tracking activities performed by the user via the Bluesky website or application.
### Configuration
To use this addon you need a OAuth Consumer key pair (key & secret).
You can get it from [Twitter](https://twitter.com/apps).
To enable Jetstream processing, run `bin/jetstream.php' from the command line.
You will need to define the process id file in local.config.php in the 'jetstream' section using the key 'pidfile'.
Register your Friendica site as "Client" application with "Read & Write" access.
We do not need "Twitter as login".
When you've registered the app you get a key pair with an OAuth Consumer key and a secret key for your application/site.
Add this key pair to your config/local.config.php:
[twitter]
consumerkey = your consumer_key here
consumersecret = your consumer_secret here
After this, your users can configure their Twitter account settings from "Settings -> Connector Settings".
### More documentation
Find the author's documentation here: [http://diekershoff.homeunix.net/redmine/wiki/friendikaplugin/Twitter_Plugin](http://diekershoff.homeunix.net/redmine/wiki/friendikaplugin/Twitter_Plugin)
GNU Social Addon for Friendica
---
* Author: Tobias Diekershoff
* tobias.diekershoff@gmx.net
* License: 3-clause BSD license
### Configuration
When the addon is activated the user has to acquire the following in order to connect to the GNU Social account of choice.
* The base URL for the GNU Social API, for quitter.se this is https://quitter.se/api/
* OAuth Consumer key & secret
To get the OAuth Consumer key pair the user has to
1 ask her Friendica admin if a pair already exists or
2 has to register the Friendica server as a client application on the GNU Social server.
This can be done from the account settings under "Settings -> Connections -> Register an OAuth client application -> Register a new application" on the GNU Social server.
During the registration of the OAuth client remember the following:
* Application names must be unique on the GNU Social site, so we recommend a Name of 'friendica-nnnn', replace 'nnnn' with a random number or your website name.
* there is no callback url
* register a desktop client
* with read & write access
* the Source URL should be the URL of your Friendica server
After the required credentials for the application are stored in the configuration you have to actually connect your Friendica account with GNU Social.
This is done from the Settings -> Connector Settings page.
Follow the Sign in with GNU Social button, allow access and then copy the security code into the box provided.
Friendica will then try to acquire the final OAuth credentials from the API.
If successful, the addon settings will allow you to select to post your public messages to your GNU Social account (have a look behind the little lock symbol beneath the status "editor" on your Home or Network pages).
To keep track of the messages processed and the drift (the time difference between the date of the message and the date the system processed that message), some fields are added to the statistics endpoint.

View file

@ -97,13 +97,6 @@ Wir nehmen hingegen Privatsphäre ernst und agieren nicht wie andere Netzwerke,
Dein Profil und deine "Wall" sollen vielleicht auch von Freunden anderer Netzwerke besucht werden können.
Wenn du diese Seiten allerdings für Webbesucher sperrst, die Friendica nicht kennt, kann das auch Freunde anderer Netzwerke blockieren.
Das kann möglicherweise ungewollte Ergebnisse produzieren, wenn du lange Statusbeiträge z.B. für Twitter oder Facebook schreibst.
Wenn Friendica einen Beitrag an diese Netzwerke schickt und nur eine bestimmte Nachrichtenlänge erlaubt ist, dann verkürzen wir diesen und erstellen einen Link, der zum Originalbeitrag führt.
Der Originallink führt zurück zu deinem Friendica-Profil.
Da Friendica nicht bestätigen kann, um wen es sich handelt, kann es passieren, dass diese Leute den Beitrag nicht komplett lesen können.
Für Leute, die davon betroffen sind, schlagen wir vor, eine Zusammenfassung in Twitter-Länge zu erstellen mit mehr Details für Freunde, die den ganzen Beitrag sehen können.
Dein Profil oder deine gesamte Friendica-Seite zu blockieren, hat außerdem ernsthafte Einflüsse auf deine Kommunikation mit GNU Social-Nutzern.
Diese Netzwerke kommunizieren mit anderen über öffentliche Protokolle, die nicht authentifiziert werden.
Um deine Beiträge zu sehen, müssen diese Netzwerke deine Beiträge als "unbekannte Webbesucher" ansehen.

View file

@ -4,15 +4,10 @@ Konnektoren (Connectors)
* [Zur Startseite der Hilfe](help)
Konnektoren erlauben es Dir, Dich mit anderen sozialen Netzwerken zu verbinden.
Konnektoren werden nur bei bestehenden Twitter und GNU Social-Accounts benötigt.
Mit diesen Konnektoren kannst Du z.B. zu Bluesky, Tumblr oder Twitter posten.
Für Bluesky und Tumblr gibt es eine bidirektionale Verbindung, d.h. du kannst Friendica nutzen, um deine Timeline von diesen Diensten zu lesen.
Außerdem gibt es einen Konnektor, um Deinen Email-Posteingang zu nutzen.
Wenn Du keinen eigenen Knoten betreibst und wissen willst, ob der server Deiner Wahl diese Konnektoren installiert hat, kannst Du Dich darüber auf der Seite '<domain_des_friendica-servers>/friendica' informieren.
Sind die Netzwerk-Konnektoren auf Deinem System installiert sind, kannst Du mit den folgenden Links die Einstellungsseiten besuchen und für Deinen Account konfigurieren:
* [Twitter](/settings/connectors)
* [GNU Social](/settings/connectors)
* [Email](/settings/connectors)
Wenn Du keinen eigenen Knoten betreibst und wissen willst, ob der Server Deiner Wahl diese Konnektoren installiert hat, kannst Du Dich darüber auf der Seite '<domain_des_friendica-servers>/friendica' informieren.
Anleitung, um sich mit Personen in bestimmten Netzwerken zu verbinden
==========================================================
@ -35,14 +30,6 @@ Du hast keine Möglichkeit, diesen Kontakten zu antworten.
Das erlaubt Dir, Dich mit Millionen von Seiten im Internet zu _verbinden_.
Alles, was dafür nötig ist, ist dass die Seite einen Feed im RSS- oder Atom Syndication-Format nutzt und welches einen Autoren und ein Bild zur Seite liefert.
**Twitter**
Um einem Twitter-Nutzer zu folgen, trage die URL der Hauptseite des Twitter-Accounts auf Deiner ["Kontakte"-Seite](contacts) in das Feld "Neuen Kontakt hinzufügen" ein.
Um zu antworten, musst Du den Twitter-Konnektor installieren und über Deinen eigenen Status-Editor antworten.
Beginne Deine Nachricht mit @twitternutzer, ersetze das aber durch den richtigen Twitter-Namen.
**Email**
Konfiguriere den Email-Konnektor auf Deiner [Einstellungsseite](settings).

View file

@ -11,8 +11,7 @@ Um die Liste der Kontakte *denen du folgst* zu exportieren, geht die [Einstellun
## 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".
Um die Kontakt CSV Datei zu importieren, gehe zu [Kontakte Importieren](settings/importcontacts).
Hier kannst du die CSV Datei auswählen und hoch laden.
### Unterstütztes Datei Format

View file

@ -32,7 +32,7 @@ Hilfe
* [Update](help/Update) (EN)
* [Konfigurationen & Admin-Panel](help/Settings)
* [Addons](help/Addons)
* [Konnektoren (Connectors) installieren (Twitter/GNU Social)](help/Installing-Connectors)
* [Konnektoren (Connectors) installieren](help/Installing-Connectors)
* [Installation eines ejabberd Servers (XMPP-Chat) mit synchronisierten Anmeldedaten](help/install-ejabberd) (EN)
* [Betreibe deine Seite mit einem SSL-Zertifikat](help/SSL)
* [Konfigurationswerte, die nur in der config/local.config.php gesetzt werden können](help/Config) (EN)

View file

@ -1,85 +1,33 @@
Konnektoren installieren (Twitter/GNU Social)
Konnektoren installieren
==================================================
* [Zur Startseite der Hilfe](help)
Friendica nutzt Erweiterung, um die Verbindung zu anderen Netzwerken wie Twitter oder App.net zu gewährleisten.
Es gibt außerdem ein Erweiterung, um über einen bestehenden GNU Social-Account diesen Service zu nutzen.
Du brauchst dieses Erweiterung aber nicht, um mit GNU Social-Mitgliedern von Friendica aus zu kommunizieren - es sei denn, du wünschst es, über einen existierenden Account einen Beitrag zu schreiben.
Alle drei Erweiterung benötigen einen Account im gewünschten Netzwerk.
Zusätzlich musst du (bzw. der Administrator der Seite) einen API-Schlüssel holen, um einen authentifizierten Zugriff zu deinem Friendica-Server herstellen zu lassen.
Friendica verwendet Konnektoren, um sich mit einigen Netzwerken zu verbinden, wie Tumblr oder Bluesky.
Alle diese Konnektoren erfordern einen Account im Zielnetzwerk.
Außerdem musst du (oder die Server-Administration) in der Regel einen API-Schlüssel erhalten, um die Verbindung zu ermöglichen.
**Seitenkonfiguration**
Erweiterung müssen vom Administrator installiert werden, bevor sie genutzt werden können.
Dieses kann über das Administrationsmenü erstellt werden.
Konnektoren müssen von der Server-Administration installiert werden, bevor sie verwendet werden können.
Dies geschieht über die Server-Verwaltung.
Jeder der Konnektoren benötigt zudem einen API-Schlüssel vom Service, der verbunden werden soll.
Einige Erweiterung erlaube es, diese Informationen auf den Administrationsseiten einzustellen, wohingegen andere eine direkte Bearbeitung der Konfigurationsdatei "config/local.config.php" erfordern.
Der Weg, um diese Schlüssel zu erhalten, variiert stark, jedoch brauchen fast alle einen bestehenden Account im gewünschten Service.
Einmal installiert, können diese Schlüssel von allen Seitennutzern genutzt werden.
Einige der Konnektoren erfordern auch einen „API-Schlüssel“ des Dienstes, mit dem du dich verbinden möchtest.
Für Tumblr findet man diese Informationen auf den Seiten der Server-Verwaltung, während für Twitter (X) jede Person einen eigenen API-Schlüssel erstellen muss.
Andere Konnektoren, wie Bluesky, benötigen überhaupt keinen API-Schlüssel.
Im Folgenden findest du die Einstellungen für die verschiedenen Services (viele dieser Informationen kommen direkt aus den Quelldateien der Erweiterung):
Weitere Informationen zu den spezifischen Anforderungen findest du auf der Einstellungsseite des jeweiligen Addons, entweder auf der Verwaltungsseite oder auf der Benutzerseite.
Bluesky Jetstream
---
**Twitter Erweiterung für Friendica**
Um die Konnektivität mit Bluesky weiter zu verbessern, kann die „Jetstream“-Konnektivität aktiviert werden.
Jetstream ist ein Dienst, der sich mit dem Bluesky-Firehose verbindet.
Mit Jetstream kommen die Nachrichten in Echtzeit an und müssen nicht erst abgefragt werden.
Es ermöglicht auch die Echtzeitverarbeitung von Blöcken oder Tracking-Aktivitäten, die über die Bluesky-Website oder -Anwendung durchgeführt werden.
* Author: Tobias Diekershoff
* tobias.diekershoff@gmx.net
Um die Jetstream-Verarbeitung zu aktivieren, führe `bin/jetstream.php' über die Befehlszeile aus.
Du musst vorher die Prozess-ID-Datei in local.config.php im Abschnitt „jetstream“ mit dem Schlüssel „pidfile“ definieren.
* License:3-clause BSD license
Konfiguration:
Um dieses Erweiterung zu nutzen, benötigst du einen OAuth Consumer-Schlüsselpaar (Schlüssel und Geheimnis), das du auf der Seite [https://twitter.com/apps](https://twitter.com/apps) erhalten kannst
Registriere deine Friendica-Seite als "Client"-Anwendung mit "Read&Write"-Zugriff. Wir benötigen "Twitter als Login" nicht. Sobald du deine Anwendung installiert hast, erhältst du das Schlüsselpaar für deine Seite.
Trage dieses Schlüsselpaar in deine globale "config/local.config.php"-Datei ein.
```
[twitter]
consumerkey = your consumer_key here
consumersecret = your consumer_secret here
```
Anschließend kann der Nutzer deiner Seite die Twitter-Einstellungen selbst eintragen: "Einstellungen -> Connector Einstellungen".
**GNU Social Erweiterung für Friendica**
* Author: Tobias Diekershoff
* tobias.diekershoff@gmx.net
* License:3-clause BSD license
Konfiguration
Wenn das Addon aktiv ist, muss der Nutzer die folgenden Einstellungen vornehmen, um sich mit dem GNU Social-Account seiner Wahl zu verbinden.
* Die Basis-URL des GNU Social-API; für quitter.se ist es https://quitter.se/api/
* OAuth Consumer key & Geheimnis
Um das OAuth-Schlüsselpaar zu erhalten, muss der Nutzer
(a) seinen Friendica-Admin fragen, ob bereits ein Schlüsselpaar existiert oder
(b) einen Friendica-Server als Anwendung auf dem GNU Social-Server anmelden.
Dies kann über Einstellungen --> Connections --> "Register an OAuth client application" -> "Register a new application" auf dem GNU Social-Server durchgeführt werden.
Während der Registrierung des OAuth-Clients ist Folgendes zu beachten:
* Der Anwendungsname muss auf der GNU Social-Seite einzigartig sein, daher empfehlen wir einen Namen wie "friendica-nnnn", ersetze dabei "nnnn" mit einer frei gewählten Nummer oder deinem Webseitennamen.
* es gibt keine Callback-URL
* Registriere einen Desktop-Client
* stelle Lese- und Schreibrechte ein
* die Quell-URL sollte die URL deines Friendica-Servers sein
Sobald die benötigten Daten gespeichert sind, musst du deinen Friendica-Account mit GNU Social verbinden.
Das kannst du über Einstellungen --> Connector-Einstellungen durchführen.
Folge dem "Einloggen mit GNU Social"-Button, erlaube den Zugriff und kopiere den Sicherheitscode in die entsprechende Box.
Friendica wird dann versuchen, die abschließende OAuth-Einstellungen über die API zu beziehen.
Wenn es geklappt hat, kannst du in den Einstellungen festlegen, ob deine öffentlichen Nachrichten automatisch in deinem GNU Social-Account erscheinen soll (achte hierbei auf das kleine Schloss-Symbol im Status-Editor)
Um die verarbeiteten Nachrichten und die Drift (die Zeitdifferenz zwischen dem Datum der Nachricht und dem Datum, an dem das System diese Nachricht verarbeitet hat) zu verfolgen, wurden dem Statistik-Endpunkt einige Felder hinzugefügt.

View file

@ -558,15 +558,17 @@ class Worker
if ($method_call) {
try {
call_user_func_array(sprintf('Friendica\Worker\%s::execute', $funcname), $argv);
} catch (\TypeError $e) {
// No need to defer a worker queue entry if the arguments are invalid
Logger::notice('Wrong worker arguments', ['class' => $funcname, 'argv' => $argv, 'queue' => $queue, 'message' => $e->getMessage()]);
} catch (\Throwable $e) {
Logger::error('Uncaught exception in worker execution', ['class' => get_class($e), 'message' => $e->getMessage(), 'code' => $e->getCode(), 'file' => $e->getFile() . ':' . $e->getLine(), 'trace' => $e->getTraceAsString(), 'previous' => $e->getPrevious()]);
Logger::error('Uncaught exception in worker method execution', ['class' => get_class($e), 'message' => $e->getMessage(), 'code' => $e->getCode(), 'file' => $e->getFile() . ':' . $e->getLine(), 'trace' => $e->getTraceAsString(), 'previous' => $e->getPrevious()]);
Worker::defer();
}
} else {
$funcname($argv, count($argv));
try {
$funcname($argv, count($argv));
} catch (\Throwable $e) {
Logger::error('Uncaught exception in worker execution', ['message' => $e->getMessage(), 'code' => $e->getCode(), 'file' => $e->getFile() . ':' . $e->getLine(), 'trace' => $e->getTraceAsString(), 'previous' => $e->getPrevious()]);
Worker::defer();
}
}
Logger::disableWorker();

View file

@ -71,16 +71,6 @@ abstract class DI
// common instances
//
/**
* @deprecated 2024.12 use DI::appHelper() instead
*
* @return App
*/
public static function app()
{
return self::$dice->create(App::class);
}
public static function appHelper(): AppHelper
{
return self::$dice->create(AppHelper::class);

View file

@ -24,11 +24,13 @@ use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Network\HTTPClient\Client\HttpClientRequest;
use Friendica\Protocol\ActivityPub;
use Friendica\Protocol\ATProtocol;
use Friendica\Util\Images;
use Friendica\Util\Network;
use Friendica\Util\ParseUrl;
use Friendica\Util\Proxy;
use Friendica\Util\Strings;
use GuzzleHttp\Psr7\Uri;
/**
* Class Media
@ -237,7 +239,7 @@ class Media
$media = self::addAccount($media);
}
if (in_array($media['type'], [self::ACTIVITY, self::LD, self::JSON])) {
if (in_array($media['type'], [self::ACTIVITY, self::LD, self::JSON]) || self::isFederatedServer($media['url'])) {
$media = self::addActivity($media);
}
@ -248,6 +250,20 @@ class Media
return $media;
}
private static function isFederatedServer(string $url): bool
{
$baseurl = Network::getBaseUrl(new Uri($url));
if (empty($baseurl)) {
return false;
}
if (Strings::compareLink($baseurl, ATProtocol::WEB)) {
return true;
}
return DBA::exists('gserver', ['nurl' => Strings::normaliseLink($baseurl), 'network' => Protocol::FEDERATED]);
}
private static function addPreviewData(array $media): array
{
if (!empty($media['preview-width']) && !empty($media['preview-height'])) {

View file

@ -151,6 +151,13 @@ class BaseSettings extends BaseModule
'accesskey' => 's',
];
$tabs[] = [
'label' => $this->t('Import Contacts'),
'url' => 'settings/importcontacts',
'selected' => static::class == Settings\UserExport::class ? 'active' : '',
'accesskey' => '',
];
$tabs[] = [
'label' => $this->t('Export personal data'),
'url' => 'settings/userexport',

View file

@ -9,7 +9,6 @@ namespace Friendica\Module\Settings;
use Exception;
use Friendica\Core\ACL;
use Friendica\Core\Logger;
use Friendica\Core\Renderer;
use Friendica\Core\Search;
use Friendica\Core\Worker;
@ -25,7 +24,6 @@ use Friendica\Module\BaseSettings;
use Friendica\Network\HTTPException;
use Friendica\Protocol\Activity;
use Friendica\Protocol\Delivery;
use Friendica\Util\Network;
use Friendica\Util\Temporal;
class Account extends BaseSettings
@ -322,42 +320,6 @@ class Account extends BaseSettings
DI::baseUrl()->redirect($redirectUrl);
}
// Import Contacts from CSV file
if (!empty($request['importcontact-submit'])) {
if (isset($_FILES['importcontact-filename'])) {
// was there an error
if ($_FILES['importcontact-filename']['error'] > 0) {
Logger::notice('Contact CSV file upload error', ['error' => $_FILES['importcontact-filename']['error']]);
DI::sysmsg()->addNotice(DI::l10n()->t('Contact CSV file upload error'));
} else {
$csvArray = array_map('str_getcsv', file($_FILES['importcontact-filename']['tmp_name']));
Logger::notice('Import started', ['lines' => count($csvArray)]);
// 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) || Network::isValidHttpUrl($csvRow[0])) {
Worker::add(Worker::PRIORITY_MEDIUM, 'AddContact', DI::userSession()->getLocalUserId(), trim($csvRow[0], '@'));
} else {
Logger::notice('Invalid account', ['url' => $csvRow[0]]);
}
}
Logger::notice('Import done');
DI::sysmsg()->addInfo(DI::l10n()->t('Importing Contacts done'));
// delete temp file
unlink($_FILES['importcontact-filename']['tmp_name']);
}
} else {
Logger::notice('Import triggered, but no import file was found.');
}
DI::baseUrl()->redirect($redirectUrl);
}
if (!empty($request['relocate-submit'])) {
Worker::add(Worker::PRIORITY_HIGH, 'Notifier', Delivery::RELOCATION, DI::userSession()->getLocalUserId());
DI::sysmsg()->addInfo(DI::l10n()->t("Relocate message has been send to your contacts"));
@ -633,11 +595,6 @@ class Account extends BaseSettings
'$h_descadvn' => DI::l10n()->t('Change the behaviour of this account for special situations'),
'$pagetype' => $pagetype,
'$importcontact' => DI::l10n()->t('Import Contacts'),
'$importcontact_text' => DI::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' => DI::l10n()->t('Upload File'),
'$importcontact_maxsize' => DI::config()->get('system', 'max_csv_file_size', 30720),
'$relocate' => DI::l10n()->t('Relocate'),
'$relocate_text' => DI::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' => DI::l10n()->t("Resend relocate message to contacts"),

View file

@ -62,7 +62,6 @@ class Connectors extends BaseSettings
$this->pconfig->set($this->session->getLocalUserId(), 'system', 'api_spoiler_title', intval($request['api_spoiler_title']));
$this->pconfig->set($this->session->getLocalUserId(), 'system', 'api_auto_attach', intval($request['api_auto_attach']));
$this->pconfig->set($this->session->getLocalUserId(), 'system', 'article_mode', intval($request['article_mode']));
$this->pconfig->set($this->session->getLocalUserId(), 'ostatus', 'legacy_contact', $request['legacy_contact']);
} elseif (!empty($request['mail-submit']) && function_exists('imap_open') && !$this->config->get('system', 'imap_disabled')) {
$mail_server = $request['mail_server'] ?? '';
$mail_port = $request['mail_port'] ?? '';
@ -127,11 +126,6 @@ class Connectors extends BaseSettings
$api_spoiler_title = intval($this->pconfig->get($this->session->getLocalUserId(), 'system', 'api_spoiler_title', true));
$api_auto_attach = intval($this->pconfig->get($this->session->getLocalUserId(), 'system', 'api_auto_attach', false));
$article_mode = intval($this->pconfig->get($this->session->getLocalUserId(), 'system', 'article_mode'));
$legacy_contact = $this->pconfig->get($this->session->getLocalUserId(), 'ostatus', 'legacy_contact');
if (!empty($legacy_contact)) {
$this->baseUrl->redirect('ostatus/subscribe?url=' . urlencode($legacy_contact));
}
$connector_settings_forms = [];
foreach ($this->database->selectToArray('hook', ['file', 'function'], ['hook' => 'connector_settings']) as $hook) {
@ -215,7 +209,6 @@ class Connectors extends BaseSettings
'$api_spoiler_title' => ['api_spoiler_title', $this->t('API: Use spoiler field as title'), $api_spoiler_title, $this->t('When activated, the "spoiler_text" field in the API will be used for the title on standalone posts. When deactivated it will be used for spoiler text. For comments it will always be used for spoiler text.')],
'$api_auto_attach' => ['api_auto_attach', $this->t('API: Automatically links at the end of the post as attached posts'), $api_auto_attach, $this->t('When activated, added links at the end of the post react the same way as added links in the web interface.')],
'$article_mode' => ['article_mode', $this->t('Article Mode'), $article_mode, $this->t("Controls how posts with titles are transmitted. Mastodon and its forks don't display the content of these posts if the post is created in the correct (default) way."), $article_modes],
'$legacy_contact' => ['legacy_contact', $this->t('Your legacy ActivityPub/GNU Social account'), $legacy_contact, $this->t('If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.')],
'$connector_settings_forms' => $connector_settings_forms,

View file

@ -0,0 +1,118 @@
<?php
// Copyright (C) 2010-2024, the Friendica project
// SPDX-FileCopyrightText: 2010-2024 the Friendica project
//
// SPDX-License-Identifier: AGPL-3.0-or-later
namespace Friendica\Module\Settings;
use Friendica\App;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\L10n;
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
use Friendica\Core\Renderer;
use Friendica\Core\Session\Capability\IHandleUserSessions;
use Friendica\Core\Worker;
use Friendica\Module\BaseSettings;
use Friendica\Module\Response;
use Friendica\Navigation\SystemMessages;
use Friendica\Network\HTTPException;
use Friendica\Util\Network;
use Friendica\Util\Profiler;
use Psr\Log\LoggerInterface;
/**
* Module to export user data
**/
class ContactImport extends BaseSettings
{
/** @var IManageConfigValues */
private $config;
/** @var IManagePersonalConfigValues */
private $pconfig;
/** @var SystemMessages */
protected $systemMessages;
public function __construct(SystemMessages $systemMessages, IManagePersonalConfigValues $pconfig, IManageConfigValues $config, IHandleUserSessions $session, App\Page $page, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
{
parent::__construct($session, $page, $l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
$this->config = $config;
$this->pconfig = $pconfig;
$this->systemMessages = $systemMessages;
}
protected function post(array $request = [])
{
if (!$this->session->getLocalUserId()) {
throw new HTTPException\ForbiddenException($this->l10n->t('Permission denied.'));
}
self::checkFormSecurityTokenRedirectOnError($this->args->getQueryString(), 'contactimport');
parent::post();
// Import Contacts from CSV file
if (!empty($request['importcontact-submit'])) {
$this->pconfig->set($this->session->getLocalUserId(), 'ostatus', 'legacy_contact', $request['legacy_contact']);
if (isset($_FILES['importcontact-filename']) && !empty($_FILES['importcontact-filename']['tmp_name'])) {
// was there an error
if ($_FILES['importcontact-filename']['error'] > 0) {
$this->logger->notice('Contact CSV file upload error', ['error' => $_FILES['importcontact-filename']['error']]);
$this->systemMessages->addNotice($this->l10n->t('Contact CSV file upload error'));
} else {
$csvArray = array_map('str_getcsv', file($_FILES['importcontact-filename']['tmp_name']));
$this->logger->notice('Import started', ['lines' => count($csvArray)]);
// 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) || Network::isValidHttpUrl($csvRow[0])) {
Worker::add(Worker::PRIORITY_MEDIUM, 'AddContact', $this->session->getLocalUserId(), trim($csvRow[0], '@'));
} else {
$this->logger->notice('Invalid account', ['url' => $csvRow[0]]);
}
}
$this->logger->notice('Import done');
$this->systemMessages->addInfo($this->l10n->t('Importing Contacts done'));
// delete temp file
unlink($_FILES['importcontact-filename']['tmp_name']);
}
} else {
$this->logger->notice('Import triggered, but no import file was found.');
}
}
$this->baseUrl->redirect($this->args->getQueryString());
}
protected function content(array $request = []): string
{
if (!$this->session->getLocalUserId()) {
throw new HTTPException\ForbiddenException($this->l10n->t('Permission denied.'));
}
parent::content();
$legacy_contact = $this->pconfig->get($this->session->getLocalUserId(), 'ostatus', 'legacy_contact');
if (!empty($legacy_contact)) {
$this->baseUrl->redirect('ostatus/subscribe?url=' . urlencode($legacy_contact));
}
$tpl = Renderer::getMarkupTemplate('settings/contactimport.tpl');
return Renderer::replaceMacros($tpl, [
'$title' => $this->l10n->t('Import Contacts'),
'$submit' => $this->l10n->t('Save Settings'),
'$form_security_token' => self::getFormSecurityToken('contactimport'),
'$importcontact_text' => $this->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' => $this->l10n->t('Upload File'),
'$importcontact_maxsize' => $this->config->get('system', 'max_csv_file_size', 30720),
'$legacy_contact' => ['legacy_contact', $this->t('Your legacy ActivityPub/GNU Social account'), $legacy_contact, $this->t('If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.')],
]);
}
}

View file

@ -129,7 +129,26 @@ final class ATProtocol
}
$data = $this->get($pds . '/xrpc/' . $url, [HttpClientOptions::HEADERS => $headers]);
$this->pConfig->set($uid, 'bluesky', 'status', is_null($data) ? self::STATUS_API_FAIL : self::STATUS_SUCCESS);
if ($data === null) {
$this->pConfig->set($uid, 'bluesky', 'status', self::STATUS_API_FAIL);
return null;
}
if (!empty($data->code) && ($data->code < 200 || $data->code >= 400)) {
if (!empty($data->message)) {
$this->pConfig->set($uid, 'bluesky', 'status-message', $data->message);
} elseif (!empty($data->code)) {
$this->pConfig->set($uid, 'bluesky', 'status-message', 'Error Code: ' . $data->code);
}
return $data;
}
$this->pConfig->set($uid, 'bluesky', 'status', self::STATUS_SUCCESS);
$this->pConfig->set($uid, 'bluesky', 'status-message', '');
return $data;
}
@ -156,6 +175,9 @@ final class ATProtocol
return null;
}
$data->code = $curlResult->getReturnCode();
} elseif (($curlResult->getReturnCode() < 200) || ($curlResult->getReturnCode() >= 400)) {
$this->logger->notice('Unexpected return code', ['url' => $url, 'code' => $curlResult->getReturnCode(), 'error' => $data ?: $curlResult->getBodyString()]);
$data->code = $curlResult->getReturnCode();
}
Item::incrementInbound(Protocol::BLUESKY);
@ -197,14 +219,17 @@ final class ATProtocol
} catch (\Exception $e) {
$this->logger->notice('Exception on post', ['exception' => $e]);
$this->pConfig->set($uid, 'bluesky', 'status', self::STATUS_API_FAIL);
$this->pConfig->set($uid, 'bluesky', 'status-message', $e->getMessage());
return null;
}
$data = json_decode($curlResult->getBodyString());
$data = json_decode($curlResult->getBodyString(), false);
if (!$curlResult->isSuccess()) {
$this->logger->notice('API Error', ['url' => $url, 'code' => $curlResult->getReturnCode(), 'error' => $data ?: $curlResult->getBodyString()]);
if (!$data) {
$this->pConfig->set($uid, 'bluesky', 'status', self::STATUS_API_FAIL);
return null;
}
$data->code = $curlResult->getReturnCode();
@ -212,8 +237,14 @@ final class ATProtocol
if (!empty($data->code) && ($data->code >= 200) && ($data->code < 400)) {
$this->pConfig->set($uid, 'bluesky', 'status', self::STATUS_SUCCESS);
$this->pConfig->set($uid, 'bluesky', 'status-message', '');
} else {
$this->pConfig->set($uid, 'bluesky', 'status', self::STATUS_API_FAIL);
if (!empty($data->message)) {
$this->pConfig->set($uid, 'bluesky', 'status-message', $data->message);
} elseif (!empty($data->code)) {
$this->pConfig->set($uid, 'bluesky', 'status-message', 'Error Code: ' . $data->code);
}
}
return $data;
}
@ -501,10 +532,6 @@ final class ATProtocol
$data = $this->post($uid, '/xrpc/com.atproto.server.refreshSession', '', ['Authorization' => ['Bearer ' . $token]]);
if (empty($data) || empty($data->accessJwt)) {
$this->logger->debug('Refresh failed', ['return' => $data]);
$password = $this->pConfig->get($uid, 'bluesky', 'password');
if (!empty($password)) {
return $this->createUserToken($uid, $password);
}
$this->pConfig->set($uid, 'bluesky', 'status', self::STATUS_TOKEN_FAIL);
return '';
}
@ -541,6 +568,7 @@ final class ATProtocol
$this->pConfig->set($uid, 'bluesky', 'refresh_token', $data->refreshJwt);
$this->pConfig->set($uid, 'bluesky', 'token_created', time());
$this->pConfig->set($uid, 'bluesky', 'status', self::STATUS_TOKEN_OK);
$this->pConfig->set($uid, 'bluesky', 'status-message', '');
return $data->accessJwt;
}
}

View file

@ -1,5 +1,5 @@
#!/usr/bin/env php
<?php
/**
* Copyright (C) 2010-2024, the Friendica project
* SPDX-FileCopyrightText: 2010-2024 the Friendica project

View file

@ -1,5 +1,5 @@
#!/usr/bin/env php
<?php
/**
* Copyright (C) 2010-2024, the Friendica project
* SPDX-FileCopyrightText: 2010-2024 the Friendica project

View file

@ -1,5 +1,5 @@
#!/usr/bin/env php
<?php
/**
* Copyright (C) 2010-2024, the Friendica project
* SPDX-FileCopyrightText: 2010-2024 the Friendica project
@ -178,7 +178,7 @@ class Processor
if ($id) {
$this->logger->info('Post inserted', ['id' => $id, 'guid' => $item['guid']]);
} elseif (Post::exists(['uid' => $uid, 'uri-id' => $item['uri-id']])) {
$this->logger->warning('Post was found', ['guid' => $item['guid'], 'uri' => $item['uri']]);
$this->logger->notice('Post was found', ['guid' => $item['guid'], 'uri' => $item['uri']]);
} else {
$this->logger->warning('Post was not inserted', ['guid' => $item['guid'], 'uri' => $item['uri']]);
}
@ -208,7 +208,7 @@ class Processor
if ($id) {
$this->logger->info('Repost inserted', ['id' => $id]);
} elseif (Post::exists(['uid' => $uid, 'uri-id' => $item['uri-id']])) {
$this->logger->warning('Repost was found', ['uri' => $item['uri']]);
$this->logger->notice('Repost was found', ['uri' => $item['uri']]);
} else {
$this->logger->warning('Repost was not inserted', ['uri' => $item['uri']]);
}
@ -237,7 +237,7 @@ class Processor
if ($id) {
$this->logger->info('Like inserted', ['id' => $id]);
} elseif (Post::exists(['uid' => $uid, 'uri-id' => $item['uri-id']])) {
$this->logger->warning('Like was found', ['uri' => $item['uri']]);
$this->logger->notice('Like was found', ['uri' => $item['uri']]);
} else {
$this->logger->warning('Like was not inserted', ['uri' => $item['uri']]);
}
@ -317,7 +317,7 @@ class Processor
if ($id) {
$this->logger->info('Fetched post inserted', ['id' => $id, 'guid' => $item['guid']]);
} elseif (Post::exists(['uid' => $uid, 'uri-id' => $item['uri-id']])) {
$this->logger->warning('Fetched post was found', ['guid' => $item['guid'], 'uri' => $item['uri']]);
$this->logger->notice('Fetched post was found', ['guid' => $item['guid'], 'uri' => $item['uri']]);
} else {
$this->logger->warning('Fetched post was not inserted', ['guid' => $item['guid'], 'uri' => $item['uri']]);
}

View file

@ -164,7 +164,7 @@ class Delivery
}
Logger::notice('Delivery failed', ['retcode' => $response->getReturnCode(), 'serverfailure' => $serverfail, 'drop' => $drop, 'runtime' => round($runtime, 3), 'uri-id' => $uri_id, 'uid' => $uid, 'item_id' => $item_id, 'cmd' => $cmd, 'inbox' => $inbox]);
Logger::notice('Delivery failed', ['retcode' => $response->getReturnCode() ?? 0, 'serverfailure' => $serverfail, 'drop' => $drop, 'runtime' => round($runtime, 3), 'uri-id' => $uri_id, 'uid' => $uid, 'item_id' => $item_id, 'cmd' => $cmd, 'inbox' => $inbox]);
}
if ($uri_id) {
if ($success) {

View file

@ -127,7 +127,6 @@ class JsonLD
$messages[] = $currentException->getMessage();
} while ($currentException = $currentException->getPrevious());
Logger::warning('JsonLD normalize error');
Logger::notice('JsonLD normalize error', ['messages' => $messages]);
Logger::info('JsonLD normalize error', ['trace' => $e->getTraceAsString()]);
Logger::debug('JsonLD normalize error', ['jsonobj' => $jsonobj]);

View file

@ -682,4 +682,19 @@ class Network
return (string)Uri::fromParts($parts);
}
/**
* Get base url without a path, fragment or query
*
* @param UriInterface $uri
* @return string baseurl
*/
public static function getBaseUrl(UriInterface $uri): string
{
return $uri
->withUserInfo('')
->withQuery('')
->withFragment('')
->withPath('');
}
}

View file

@ -453,6 +453,10 @@ class Notifier
}
$cdata = Contact::getPublicAndUserContactID($contact['id'], $sender_uid);
if (empty($cdata)) {
Logger::info('No contact entry found', ['id' => $contact['id'], 'uid' => $sender_uid]);
continue;
}
if (in_array($cdata['public'] ?: $contact['id'], $ap_contacts)) {
Logger::info('The public contact is already delivered via AP, so skip delivery via legacy DFRN/Diaspora', ['batch' => $in_batch, 'target' => $post_uriid, 'uid' => $sender_uid, 'contact' => $contact['url']]);
continue;
@ -639,8 +643,8 @@ class Notifier
private static function activityPubDelivery($cmd, array $target_item, array $parent, array $thr_parent, int $priority, string $created, array $recipients): array
{
// Don't deliver via AP when the starting post isn't from a federated network
if (!in_array($parent['network'], Protocol::FEDERATED)) {
Logger::info('Parent network is no federated network, so no AP delivery', ['network' => $parent['network']]);
if (!in_array($parent['network'] ?? '', Protocol::FEDERATED)) {
Logger::info('Parent network is no federated network, so no AP delivery', ['network' => $parent['network'] ?? '']);
return ['count' => 0, 'contacts' => []];
}

View file

@ -622,6 +622,7 @@ return [
'/delegation[/{action}/{user_id}]' => [Module\Settings\Delegation::class, [R::GET, R::POST]],
'/display' => [Module\Settings\Display::class, [R::GET, R::POST]],
'/features' => [Module\Settings\Features::class, [R::GET, R::POST]],
'/importcontacts' => [Module\Settings\ContactImport::class, [R::GET, R::POST]],
'/oauth' => [Module\Settings\OAuth::class, [R::GET, R::POST]],
'/profile' => [
'[/]' => [Module\Settings\Profile\Index::class, [R::GET, R::POST]],

File diff suppressed because it is too large Load diff

View file

@ -178,18 +178,6 @@
</div>
</form>
<h2 class="settings-heading"><a href="javascript:;">{{$importcontact}}</a></h2>
<form class="settings-content-block" action="settings" method="post" autocomplete="off" enctype="multipart/form-data">
<input type="hidden" name="form_security_token" value="{{$form_security_token}}">
<input type="hidden" name="MAX_FILE_SIZE" value="{{$importcontact_maxsize}}"/>
<p id="settings-pagetype-desc">{{$importcontact_text}}</p>
<p><input type="file" name="importcontact-filename"/></p>
<div class="settings-submit-wrapper">
<input type="submit" name="importcontact-submit" class="importcontact-submit" value="{{$importcontact_button}}"/>
</div>
</form>
<h2 class="settings-heading"><a href="javascript:;">{{$relocate}}</a></h2>
<form class="settings-content-block" action="settings" method="post" autocomplete="off" enctype="multipart/form-data">
<input type="hidden" name="form_security_token" value="{{$form_security_token}}">

View file

@ -25,7 +25,6 @@
{{include file="field_checkbox.tpl" field=$api_spoiler_title}}
{{include file="field_checkbox.tpl" field=$api_auto_attach}}
{{include file="field_select.tpl" field=$article_mode}}
{{include file="field_input.tpl" field=$legacy_contact}}
<div class="settings-submit-wrapper">
<input type="submit" id="general-submit" name="general-submit" class="settings-submit" value="{{$submit}}"/>

View file

@ -0,0 +1,23 @@
{{*
* Copyright (C) 2010-2024, the Friendica project
* SPDX-FileCopyrightText: 2010-2024 the Friendica project
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*}}
<h1>{{$title}}</h1>
<div id="settings-form">
<form action="settings/importcontacts" method="post" autocomplete="off" enctype="multipart/form-data">
<input type="hidden" name="form_security_token" value="{{$form_security_token}}">
<input type="hidden" name="MAX_FILE_SIZE" value="{{$importcontact_maxsize}}" />
{{include file="field_input.tpl" field=$legacy_contact}}
<hr>
<p id="settings-pagetype-desc">{{$importcontact_text}}</p>
<p><input type="file" name="importcontact-filename" /></p>
<div class="settings-submit-wrapper">
<input type="submit" name="importcontact-submit" class="importcontact-submit"
value="{{$importcontact_button}}" />
</div>
</form>
</div>

View file

@ -246,28 +246,6 @@
</div>
</form>
{{* Import contacts CSV *}}
<form action="settings/account/importcontact" method="post" autocomplete="off" class="panel" enctype="multipart/form-data">
<input type="hidden" name="form_security_token" value="{{$form_security_token}}">
<div class="section-subtitle-wrapper panel-heading" role="tab" id="importcontact-settings">
<h2>
<button class="btn-link accordion-toggle{{if $open !== 'importcontact'}} collapsed{{/if}}" data-toggle="collapse" data-parent="#settings" href="#importcontact-settings-collapse" aria-expanded="false" aria-controls="importcontact-settings-collapse">
{{$importcontact}}
</button>
</h2>
</div>
<div id="importcontact-settings-collapse" class="panel-collapse collapse{{if $open == 'importcontact'}} in{{/if}}" role="tabpanel" aria-labelledby="importcontact-settings">
<div class="panel-body">
<div id="importcontact-relocate-desc">{{$importcontact_text}}</div>
<input type="hidden" name="MAX_FILE_SIZE" value="{{$importcontact_maxsize}}" />
<input type="file" name="importcontact-filename" />
</div>
<div class="panel-footer">
<button type="submit" name="importcontact-submit" class="btn btn-primary" value="{{$submit}}">{{$submit}}</button>
</div>
</div>
</form>
{{* The relocate setting section *}}
<form action="settings/account/relocate" method="post" autocomplete="off" class="panel">
<input type="hidden" name="form_security_token" value="{{$form_security_token}}">

View file

@ -39,8 +39,6 @@
{{include file="field_checkbox.tpl" field=$api_auto_attach}}
{{include file="field_select.tpl" field=$article_mode}}
{{include file="field_input.tpl" field=$legacy_contact}}
</div>
<div class="panel-footer">
<button type="submit" id="general-submit" name="general-submit" class="btn btn-primary" value="{{$submit}}">{{$submit}}</button>

View file

@ -0,0 +1,28 @@
{{*
* Copyright (C) 2010-2024, the Friendica project
* SPDX-FileCopyrightText: 2010-2024 the Friendica project
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*}}
<div class="generic-page-wrapper">
<h1>{{$title}}</h1>
<div id="settings-form">
{{* Import contacts CSV *}}
<form action="settings/importcontacts" method="post" autocomplete="off" class="panel"
enctype="multipart/form-data">
<input type="hidden" name="form_security_token" value="{{$form_security_token}}">
<div class="panel-body">
{{include file="field_input.tpl" field=$legacy_contact}}
<hr>
<div id="importcontact-relocate-desc">{{$importcontact_text}}</div>
<input type="hidden" name="MAX_FILE_SIZE" value="{{$importcontact_maxsize}}" />
<input type="file" name="importcontact-filename" />
</div>
<div class="panel-footer">
<button type="submit" name="importcontact-submit" class="btn btn-primary"
value="{{$submit}}">{{$submit}}</button>
</div>
</div>
</form>
</div>
</div>