diff --git a/.gitignore b/.gitignore
index 2d8acf0160..3250fb0761 100644
--- a/.gitignore
+++ b/.gitignore
@@ -71,8 +71,8 @@ venv/
/addons
/addon
-#ignore .htaccess
-.htaccess
+#ignore base .htaccess
+/.htaccess
#ignore filesystem storage default path
/storage
diff --git a/.htaccess-dist b/.htaccess-dist
index a671cc680a..3c90982515 100644
--- a/.htaccess-dist
+++ b/.htaccess-dist
@@ -1,3 +1,6 @@
+# This file is meant to be copied to ".htaccess" on Apache-powered web servers.
+# The created .htaccess file can be edited manually and will not be overwritten by Friendica updates.
+
Options -Indexes
AddType application/x-java-archive .jar
AddType audio/ogg .oga
diff --git a/CHANGELOG b/CHANGELOG
index 2c5662c2b7..4eeb726250 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,78 @@
-Version 2020.09 (unreleased)
+Version 2020.09 (2020-09-20)
+ Friendica Core:
+ Updates to the translations: DE, EN GB, EN US, ES, FR, IT, NL, PL, RU, ZH_CN [translation teams]
+ Updates to the themes (all) [MrPetovan, tobiasd]
+ Updates to the documentation [annando, mpanhans, realkinetix, tobiasd]
+ General code cleanup and refactoring [annando, MrPetovan, nupplaphil]
+ Enhanced the API [annando]
+ Enhanced the processing of background jobs [annando]
+ Enhanced federation of activities [annando, vpzomtrrfrt]
+ Enhanced the user notifications[annando]
+ Enhanced database usage [annando, MrPetovan]
+ Enhanced ActivityPub support for forums [annando]
+ Enhanced the utilization of the cache [annando, MrPetovan]
+ Enhanced the performance of the daemon [annando]
+ Enhanced the communication with the directory servers [annando]
+ Enhanced the re-sharing of items [annando]
+ Enhanced sample lighttpd and nginx configs [MrPetovan, tobiasd]
+ Enhanced the checks for incoming postings using ActivityPub [annando, Roger Meyer]
+ Enhanced the import of RSS feeds by removing tracking pixels [annando]
+ Enhanced the speed of the full text search [annando]
+ Replaced library used for text completion [MrPetovan]
+ Fixed a problem that prevented recipients of direct messages to be selected [MrPetovan]
+ Fixed a problem that prevented new email contacts from being added [annando]
+ Fixed a problem with the console command search [tobiasd]
+ Fixed a problem during the search for contacts [annando]
+ Fixed a problem with the JOT of private notes [MrPetovan]
+ Fixed missing HTML encoding [MrPetovan]
+ Fixed a layout problem with the frio composer for new postings [MrPetovan]
+ Fixed some composer notices [nupplaphil]
+ Fixed a problem for empty preview data when importing feed posts [annando]
+ Fixed a problem with the pager on search result pages [annando]
+ Fixed some templates to show the correct un-/follow button for contacts [annando]
+ Fixed a problem with the generation of the Message-ID of notification emails [nupplaphil]
+ Added nodeinfo2 support [annando]
+ Added CSV export and import of blocked servers to the console [tobiasd]
+ Added new admin debug module for ActivityPub [MrPetovan]
+ Added the automatic determination of frequency to pull feeds [annando]
+ Added signed fetching from system users for ActivityPub [annando]
+ Added the discovery of new peers from contacts [annando]
+ Added the directory API endpoint [annando]
+ Added support for signed outbox requests [annando]
+ Added direction functionality for clarification of posting flow [annando]
+ Added the ability to set the database version [annando]
+ Added support for ActivityPub relay server [annando]
+ By default display of re-sharer information is now flattened [annando]
+ Removed some unused POCO functionality [annando]
+ Removed the unused rating functionality [annando]
+ Removed unneeded network request for local stuff [annando]
+ Removed some useless info messages [annando]
+ Reworked some additional features according to a user voting [MrPetovan]
+
+ Friendica Addons:
+ Updates to the translations: DE, EN GB, EN US, IT, NL, RU, ZH_CN [translation teams]
+ Updates to the docs [SpencerDub]
+ General code cleanup and maintenance [annando, MrPetovan]
+ blockbot:
+ added some "good" bots [annando]
+ forumdirectory:
+ fixed some SQL queries [MrPetovan]
+ phpmailer:
+ fixed a problem leading to double message ID headers [nupplaphil]
+ qcomment:
+ restructured the addon and fixed a bug preventing the addon from working [MrPetovan]
+
+ Closed Issues:
+ 2811, 4606, 5742, 5782, 7660, 8676, 8788, 8797, 8798, 8847, 8860,
+ 8874, 8882, 8885, 8906, 8914, 8922, 8928, 8929, 8935, 8940, 8941,
+ 8956, 8958, 8961, 8967, 8989, 8993, 8994, 8995, 8997, 8999, 9000,
+ 9004, 9013, 9015, 9051, 9064, 9065, 9072, 9081, 9090, 9091, 9099,
+ 9107, 9135, 9136, 9137, 9138, 9140, 9142, 9150, 9153, 9154, 9163,
+ 9164, 9172, 9182, 9192, 9193, 9204, 9210, 9229, 9231, 9246
+
+Version 2020.07-1 (2020-09-08)
+ Friendica Core
+ Fixed a problem that leaked sensitive information [Roger Meyer, MrPetovan]
Version 2020.07 (2020-07-12)
Friendica Core:
@@ -668,7 +742,7 @@ Version 2018.09 (2018-09-23)
Version 2018.05 (2018-06-01)
Friendica Core:
Update to the translations (DE, EN-GB, EN-US, FI, IS, IT, NL, PL, RU, ZN CH) [translation teams]
- Update to the documentation [andyhee, annando, fabrixxm, M-arcus, MrPedovan, rudloff, tobiasd]
+ Update to the documentation [andyhee, annando, fabrixxm, M-arcus, MrPetovan, rudloff, tobiasd]
Enhancements to the DB handling [annando]
Enhancements to the relay system [annando]
Enhancements to the handling of URL that contain unicode characters [annando]
diff --git a/CREDITS.txt b/CREDITS.txt
index 402c356b7b..07d6ecb2f3 100644
--- a/CREDITS.txt
+++ b/CREDITS.txt
@@ -55,6 +55,7 @@ Chris Case
Christian González
Christian M. Grube
Christian Vogeley
+Christian Wiwie
Cohan Robinson
Copiis Praeesse
CrystalStiletto
@@ -114,7 +115,6 @@ Hypolite Petovan
Ilmari
ImgBotApp
irhen
-Jak
Jakob
Jens Tautenhahn
jensp
@@ -122,6 +122,7 @@ Jeroen De Meerleer
jeroenpraat
Joan Bar
JOduMonT
+joe slam
Johannes Schwab
John Brazil
Jonatan Nyberg
@@ -143,7 +144,6 @@ Leberwurscht
Leonard Lausen
Lionel Triay
loma-one
-loma1
Lorem Ipsum
Ludovic Grossard
Lynn Stephenson
@@ -173,6 +173,7 @@ Michal Šupler
Michalina
Mike Macgirvin
miqrogroove
+mpanhans
mytbk
nathilia-peirce
Nicola Spanti
@@ -212,6 +213,7 @@ repat
Ricardo Pereira
Rik 4
RJ Madsen
+Roger Meyer
Roland Häder
Rui Andrada
rwa
@@ -230,7 +232,7 @@ Simon L'nu
Simó Albert i Beltran
softmetz
soko1
-SpencerDub
+Spencer Dub
St John Karp
Stanislav N.
Steffen K9
@@ -268,7 +270,6 @@ U-SOUND\mike
ufic
Ulf Rompe
Unknown
-Valvin
Valvin A
Vasudev Kamath
Vasya Novikov
diff --git a/VERSION b/VERSION
index 454e445f6e..14ffed05b5 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2020.09-dev
+2020.09
diff --git a/bin/.htaccess b/bin/.htaccess
new file mode 100644
index 0000000000..716a932e1c
--- /dev/null
+++ b/bin/.htaccess
@@ -0,0 +1,10 @@
+# This file prevents browser access to Friendica command-line scripts on Apache-powered web servers.
+# It isn't meant to be edited manually, please check the base Friendica folder for the .htaccess-dist file instead.
+
+
+ Require all denied
+
+
+ Order Allow,Deny
+ Deny from all
+
diff --git a/bin/auth_ejabberd.php b/bin/auth_ejabberd.php
index fa71faf263..e921829163 100755
--- a/bin/auth_ejabberd.php
+++ b/bin/auth_ejabberd.php
@@ -51,6 +51,11 @@
*
*/
+if (php_sapi_name() !== 'cli') {
+ header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
+ exit();
+}
+
use Dice\Dice;
use Friendica\App\Mode;
use Friendica\Util\ExAuth;
diff --git a/bin/console.php b/bin/console.php
index 27522d8554..4d5b4c79c2 100755
--- a/bin/console.php
+++ b/bin/console.php
@@ -20,6 +20,11 @@
*
*/
+if (php_sapi_name() !== 'cli') {
+ header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
+ exit();
+}
+
use Dice\Dice;
use Psr\Log\LoggerInterface;
diff --git a/bin/daemon.php b/bin/daemon.php
index 596f4de56f..3fe803d6fc 100755
--- a/bin/daemon.php
+++ b/bin/daemon.php
@@ -23,6 +23,11 @@
* This script was taken from http://php.net/manual/en/function.pcntl-fork.php
*/
+if (php_sapi_name() !== 'cli') {
+ header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
+ exit();
+}
+
use Dice\Dice;
use Friendica\Core\Logger;
use Friendica\Core\Worker;
diff --git a/bin/dev/make_credits.py b/bin/dev/make_credits.py
index d895213909..be7a52e322 100755
--- a/bin/dev/make_credits.py
+++ b/bin/dev/make_credits.py
@@ -34,7 +34,7 @@ dontinclude = ['root', 'friendica', 'bavatar', 'tony baldwin', 'Taek', 'silke m'
path = os.path.abspath(argv[0].split('bin/dev/make_credits.py')[0])
print('> base directory is assumed to be: '+path)
# a place to store contributors
-contributors = ["Andi Stadler", "Ratten", "Vít Šesták 'v6ak'"]
+contributors = ["Andi Stadler", "Ratten", "Roger Meyer", "Vít Šesták 'v6ak'"]
# get the contributors
print('> getting contributors to the friendica core repository')
p = subprocess.Popen(['git', 'shortlog', '--no-merges', '-s'],
diff --git a/bin/testargs.php b/bin/testargs.php
index b7d7125f7a..9aed353037 100644
--- a/bin/testargs.php
+++ b/bin/testargs.php
@@ -26,6 +26,10 @@
*
*/
+if (php_sapi_name() !== 'cli') {
+ header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
+ exit();
+}
if (($_SERVER["argc"] > 1) && isset($_SERVER["argv"][1])) {
echo $_SERVER["argv"][1];
diff --git a/bin/wait-for-connection b/bin/wait-for-connection
index b6c03a6705..de860e9849 100755
--- a/bin/wait-for-connection
+++ b/bin/wait-for-connection
@@ -24,6 +24,11 @@
* Usage: php bin/wait-for-connection {HOST} {PORT} [{TIMEOUT}]
*/
+if (php_sapi_name() !== 'cli') {
+ header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
+ exit();
+}
+
$timeout = 60;
switch ($argc) {
case 4:
diff --git a/bin/worker.php b/bin/worker.php
index 46eff1071a..5698cf16dd 100755
--- a/bin/worker.php
+++ b/bin/worker.php
@@ -21,6 +21,11 @@
* Starts the background processing
*/
+if (php_sapi_name() !== 'cli') {
+ header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
+ exit();
+}
+
use Dice\Dice;
use Friendica\App;
use Friendica\Core\Process;
diff --git a/boot.php b/boot.php
index b43fd6dc2f..cba94fa483 100644
--- a/boot.php
+++ b/boot.php
@@ -38,7 +38,7 @@ use Friendica\Util\DateTimeFormat;
define('FRIENDICA_PLATFORM', 'Friendica');
define('FRIENDICA_CODENAME', 'Red Hot Poker');
-define('FRIENDICA_VERSION', '2020.09-dev');
+define('FRIENDICA_VERSION', '2020.09');
define('DFRN_PROTOCOL_VERSION', '2.23');
define('NEW_UPDATE_ROUTINE_VERSION', 1170);
@@ -382,38 +382,6 @@ function is_site_admin()
return local_user() && $admin_email && in_array($a->user['email'] ?? '', $adminlist);
}
-function explode_querystring($query)
-{
- $arg_st = strpos($query, '?');
- if ($arg_st !== false) {
- $base = substr($query, 0, $arg_st);
- $arg_st += 1;
- } else {
- $base = '';
- $arg_st = 0;
- }
-
- $args = explode('&', substr($query, $arg_st));
- foreach ($args as $k => $arg) {
- /// @TODO really compare type-safe here?
- if ($arg === '') {
- unset($args[$k]);
- }
- }
- $args = array_values($args);
-
- if (!$base) {
- $base = $args[0];
- unset($args[0]);
- $args = array_values($args);
- }
-
- return [
- 'base' => $base,
- 'args' => $args,
- ];
-}
-
/**
* Returns the complete URL of the current page, e.g.: http(s)://something.com/network
*
diff --git a/composer.json b/composer.json
index fad962e414..8fbe387ad0 100644
--- a/composer.json
+++ b/composer.json
@@ -129,7 +129,7 @@
"mikey179/vfsstream": "^1.6",
"mockery/mockery": "^1.2",
"johnkary/phpunit-speedtrap": "1.1",
- "jakub-onderka/php-parallel-lint": "^1.0"
+ "php-parallel-lint/php-parallel-lint": "^1.2"
},
"scripts": {
"test": "phpunit"
diff --git a/composer.lock b/composer.lock
index ebd434e39e..d2b5f74a21 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "ffe94190e166cebf80601fc3d6d26be0",
+ "content-hash": "ed9aa898eaf8a1f8a807f3be9eecc3d7",
"packages": [
{
"name": "asika/simple-console",
@@ -3487,55 +3487,6 @@
],
"time": "2016-01-20T08:20:44+00:00"
},
- {
- "name": "jakub-onderka/php-parallel-lint",
- "version": "v1.0.0",
- "source": {
- "type": "git",
- "url": "https://github.com/JakubOnderka/PHP-Parallel-Lint.git",
- "reference": "04fbd3f5fb1c83f08724aa58a23db90bd9086ee8"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/JakubOnderka/PHP-Parallel-Lint/zipball/04fbd3f5fb1c83f08724aa58a23db90bd9086ee8",
- "reference": "04fbd3f5fb1c83f08724aa58a23db90bd9086ee8",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.3"
- },
- "require-dev": {
- "jakub-onderka/php-console-highlighter": "~0.3",
- "nette/tester": "~1.3",
- "squizlabs/php_codesniffer": "~2.7"
- },
- "suggest": {
- "jakub-onderka/php-console-highlighter": "Highlight syntax in code snippet"
- },
- "bin": [
- "parallel-lint"
- ],
- "type": "library",
- "autoload": {
- "classmap": [
- "./"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-2-Clause"
- ],
- "authors": [
- {
- "name": "Jakub Onderka",
- "email": "ahoj@jakubonderka.cz"
- }
- ],
- "description": "This tool check syntax of PHP files about 20x faster than serial check.",
- "homepage": "https://github.com/JakubOnderka/PHP-Parallel-Lint",
- "abandoned": "php-parallel-lint/php-parallel-lint",
- "time": "2018-02-24T15:31:20+00:00"
- },
{
"name": "johnkary/phpunit-speedtrap",
"version": "v1.1.0",
@@ -3740,6 +3691,59 @@
],
"time": "2017-10-19T19:58:43+00:00"
},
+ {
+ "name": "php-parallel-lint/php-parallel-lint",
+ "version": "v1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-parallel-lint/PHP-Parallel-Lint.git",
+ "reference": "474f18bc6cc6aca61ca40bfab55139de614e51ca"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-parallel-lint/PHP-Parallel-Lint/zipball/474f18bc6cc6aca61ca40bfab55139de614e51ca",
+ "reference": "474f18bc6cc6aca61ca40bfab55139de614e51ca",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "php": ">=5.4.0"
+ },
+ "replace": {
+ "grogy/php-parallel-lint": "*",
+ "jakub-onderka/php-parallel-lint": "*"
+ },
+ "require-dev": {
+ "nette/tester": "^1.3 || ^2.0",
+ "php-parallel-lint/php-console-highlighter": "~0.3",
+ "squizlabs/php_codesniffer": "~3.0"
+ },
+ "suggest": {
+ "php-parallel-lint/php-console-highlighter": "Highlight syntax in code snippet"
+ },
+ "bin": [
+ "parallel-lint"
+ ],
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "./"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Jakub Onderka",
+ "email": "ahoj@jakubonderka.cz"
+ }
+ ],
+ "description": "This tool check syntax of PHP files about 20x faster than serial check.",
+ "homepage": "https://github.com/php-parallel-lint/PHP-Parallel-Lint",
+ "time": "2020-04-04T12:18:32+00:00"
+ },
{
"name": "phpdocumentor/reflection-common",
"version": "1.0.1",
diff --git a/database.sql b/database.sql
index addd396ffd..3b7ef341c1 100644
--- a/database.sql
+++ b/database.sql
@@ -1,6 +1,6 @@
-- ------------------------------------------
--- Friendica 2020.09-dev (Red Hot Poker)
--- DB_UPDATE_VERSION 1367
+-- Friendica 2020.09-rc (Red Hot Poker)
+-- DB_UPDATE_VERSION 1368
-- ------------------------------------------
@@ -786,6 +786,7 @@ CREATE TABLE IF NOT EXISTS `item-content` (
`verb` varchar(100) NOT NULL DEFAULT '' COMMENT 'ActivityStreams verb',
PRIMARY KEY(`id`),
UNIQUE INDEX `uri-plink-hash` (`uri-plink-hash`),
+ FULLTEXT INDEX `title-content-warning-body` (`title`,`content-warning`,`body`),
INDEX `uri` (`uri`(191)),
INDEX `plink` (`plink`(191)),
INDEX `uri-id` (`uri-id`),
diff --git a/doc/Forums.md b/doc/Forums.md
index 03657b6afe..d58a8565a5 100644
--- a/doc/Forums.md
+++ b/doc/Forums.md
@@ -4,49 +4,46 @@ Forums
* [Home](help)
-Friendica also lets you create forums and/or celebrity accounts.
+Friendica also lets you create community forums and other types of accounts that can function as discussion forums, celebrity accounts, announcement channels, news reflectors, or organization pages, depending on how you want to interact with others. Management of these pages can be delegated to other accounts, or a parent account can be designated to easily toggle multiple identities.
-Every page in Friendica has a nickname and these must all be unique.
-This applies to all forums, whether they are normal profiles or forum profiles.
+Every page in Friendica has a nickname and these must all be unique. This applies to all forums, whether they are normal profiles or forum profiles.
-Therefore the first thing you need to do to create a new forum is to register a new account for the forum.
-Please note that the site administrator can restrict and/or regulate the registration of new accounts.
-
-If you create a second account on a system and use the same email address or OpenID account as an existing account, you will no longer be able to use the email address (or OpenID) to log in to the account.
-You should log in using the account nickname instead.
-
-On the new account, visit the 'Settings' page.
-Towards the end of the page are "Advanced Account/Page Type Settings".
-Typically you would use "Normal Account" for a normal personal account.
-This is the default selection.
-Community Forum/Celebrity Accounts provide the ability for people to become friends/fans of the forum without requiring approval.
-
-The exact setting you would use depends on how you wish to interact with people who join the page.
-The "Soapbox" setting lets the page owner control all communications.
-Everything you post will go out to the forum members, but there will be no opportunity for interaction.
-This setting would typically be used for announcements or corporate communications.
-
-The most common setting is the "Community Forum".
-This creates a forum page where all members can freely interact.
-
-The "Automatic Friend Account" is typically used for personal profile forums where you wish to automatically approve any friendship/connection requests.
-
-Managing Multiple forums
+Managing Accounts
---
-We recommend that you create group forums with the same email address and password as your normal account.
-If you do this, you will find a new "Manage" tab on the menu bar which lets you toggle identities easily and manage your forums.
-You are not required to do this, but the alternative is to log out and log back into the other account to manage alternate forums.
-This could get cumbersome if you manage several different forums/identities.
+To create a new linked account that can be used as a forum, log in to your normal account and go to Settings > Manage Accounts.
+Here you can register additional accounts with new nicknames that will be linked to your primary account.
-You may also appoint a delegate to manage your forum.
-Do this by visiting the [Delegation Setup Page](settings/delegation).
-This will provide you with a list of contacts on this system under "Potential Delegates".
+You may appoint a delegate to manage your new account (e.g. forum page).
+The Delegates section of Manage Accounts page will provide you with a list of contacts on this instance under "Potential Delegates".
Selecting one or more persons will give them access to manage your forum.
They will be able to edit contacts, profiles, and all content for this account/page.
Please use this facility wisely.
-Delegated managers will not be able to alter basic account settings such as passwords or page types and/or remove the account.
+Delegated managers will not be able to alter basic account settings, such as passwords or page types, or remove the account.
+Additionally, this page is also where you can choose to designate an account as a parent user.
+If your primary account is designated as the parent user, you will be able to easily toggle identities and manage your forums or other types of accounts.
+
+Types of Accounts
+---
+
+On the new account, visit the Settings > Account page.
+Towards the end of the page is a section for "Advanced account types".
+Typically you would use "Personal Page - Standard" for a normal personal account with manual approval of “friends” and “followers.”
+This is the default selection.
+On this page you can change the type of account if desired.
+
+The other subtypes of a Personal Page are “Soapbox” and “Love-all.”
+A Soapbox account is an announcement channel that automatically approvals follower requests.
+Everything posted by the account will go out to the followers, but there will be no opportunity for interaction.
+This setting would typically be used for announcements or corporate communications.
+“Love-all” automatically approves contacts as friends.
+
+In addition to Personal Page, there are options for Organization Page, News Page, and Community Forum.
+Organization and New Pages automatically approve contact requests as followers.
+
+Community Forum provide the ability for people to become friends/fans of the forum without requiring approval.
+This creates a forum page where all members can freely interact.
Posting to Community forums
---
diff --git a/doc/Install.md b/doc/Install.md
index e0cece958c..8d66425a83 100644
--- a/doc/Install.md
+++ b/doc/Install.md
@@ -33,7 +33,7 @@ The account will expire after 7 days, but you can ask the server admin to keep y
* Apache with mod-rewrite enabled and "Options All" so you can use a local `.htaccess` file
* PHP 7+ (PHP 7.1+ is recommended for performance and official support)
* PHP *command line* access with register_argc_argv set to true in the php.ini file
- * Curl, GD, PDO, MySQLi, hash, xml, zip and OpenSSL extensions
+ * Curl, GD, PDO, mbstrings, MySQLi, hash, xml, zip and OpenSSL extensions
* The POSIX module of PHP needs to be activated (e.g. [RHEL, CentOS](http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7) have disabled it)
* some form of email server or email gateway such that PHP mail() works
* MySQL 5.6+ or an equivalent alternative for MySQL (MariaDB, Percona Server etc.)
@@ -47,7 +47,6 @@ For alternative server configurations (such as Nginx server and MariaDB database
### Optional
* PHP ImageMagick extension (php-imagick) for animated GIF support.
-* [Composer](https://getcomposer.org/) for a git install
## Installation procedure
@@ -61,6 +60,8 @@ If this is nothing for you, you might be interested in
### Get Friendica
+Download the full archive of the stable release of Friendica core and the addons from [the project homepage](https://friendi.ca/resources/download-files/).
+Make sure that the version of the Friendica archive and the addons match.
Unpack the Friendica files into the root of your web server document area.
If you copy the directory tree to your webserver, make sure that you also copy `.htaccess-dist` - as "dot" files are often hidden and aren't normally copied.
diff --git a/doc/tools.md b/doc/tools.md
index 8746e9c150..1c3b8e119c 100644
--- a/doc/tools.md
+++ b/doc/tools.md
@@ -27,6 +27,7 @@ The console provides the following commands:
* typo: Checks for parse errors in Friendica files
* postupdate: Execute pending post update scripts (can last days)
* storage: Manage storage backend
+* relay: Manage ActivityPub relay servers
Please consult *bin/console help* on the command line interface of your server for details about the commands.
diff --git a/include/api.php b/include/api.php
index 5ae4754b57..643f57b634 100644
--- a/include/api.php
+++ b/include/api.php
@@ -311,22 +311,22 @@ function api_call(App $a, App\Arguments $args = null)
}
$type = "json";
- if (strpos($args->getQueryString(), ".xml") > 0) {
+ if (strpos($args->getCommand(), ".xml") > 0) {
$type = "xml";
}
- if (strpos($args->getQueryString(), ".json") > 0) {
+ if (strpos($args->getCommand(), ".json") > 0) {
$type = "json";
}
- if (strpos($args->getQueryString(), ".rss") > 0) {
+ if (strpos($args->getCommand(), ".rss") > 0) {
$type = "rss";
}
- if (strpos($args->getQueryString(), ".atom") > 0) {
+ if (strpos($args->getCommand(), ".atom") > 0) {
$type = "atom";
}
try {
foreach ($API as $p => $info) {
- if (strpos($args->getQueryString(), $p) === 0) {
+ if (strpos($args->getCommand(), $p) === 0) {
if (!api_check_method($info['method'])) {
throw new MethodNotAllowedException();
}
diff --git a/include/conversation.php b/include/conversation.php
index 64c8228ff9..ed9086307b 100644
--- a/include/conversation.php
+++ b/include/conversation.php
@@ -520,10 +520,6 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
$threadsid++;
- $owner_url = '';
- $owner_name = '';
- $sparkle = '';
-
// prevent private email from leaking.
if ($item['network'] === Protocol::MAIL && local_user() != $item['uid']) {
continue;
@@ -540,14 +536,14 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
'network' => $item['author-network'], 'url' => $item['author-link']];
$profile_link = Contact::magicLinkByContact($author);
+ $sparkle = '';
if (strpos($profile_link, 'redir/') === 0) {
$sparkle = ' sparkle';
}
$locate = ['location' => $item['location'], 'coord' => $item['coord'], 'html' => ''];
Hook::callAll('render_location',$locate);
-
- $location = ((strlen($locate['html'])) ? $locate['html'] : render_location_dummy($locate));
+ $location_html = $locate['html'] ?: Strings::escapeHtml($locate['location'] ?: $locate['coord'] ?: '');
localize_item($item);
if ($mode === 'network-new') {
@@ -563,10 +559,6 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
'delete' => DI::l10n()->t('Delete'),
];
- $star = false;
- $isstarred = "unstarred";
-
- $lock = false;
$likebuttons = [
'like' => null,
'dislike' => null,
@@ -577,7 +569,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
unset($likebuttons['dislike']);
}
- $body = Item::prepareBody($item, true, $preview);
+ $body_html = Item::prepareBody($item, true, $preview);
list($categories, $folders) = DI::contentItem()->determineCategoriesTerms($item);
@@ -596,13 +588,13 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
'network_icon' => ContactSelector::networkToIcon($item['network'], $item['author-link']),
'linktitle' => DI::l10n()->t('View %s\'s profile @ %s', $profile_name, $item['author-link']),
'profile_url' => $profile_link,
- 'item_photo_menu' => item_photo_menu($item),
+ 'item_photo_menu_html' => item_photo_menu($item),
'name' => $profile_name,
'sparkle' => $sparkle,
- 'lock' => $lock,
+ 'lock' => false,
'thumb' => DI::baseUrl()->remove($item['author-avatar']),
'title' => $title,
- 'body' => $body,
+ 'body_html' => $body_html,
'tags' => $tags['tags'],
'hashtags' => $tags['hashtags'],
'mentions' => $tags['mentions'],
@@ -613,23 +605,23 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
'has_folders' => ((count($folders)) ? 'true' : ''),
'categories' => $categories,
'folders' => $folders,
- 'text' => strip_tags($body),
+ 'text' => strip_tags($body_html),
'localtime' => DateTimeFormat::local($item['created'], 'r'),
'ago' => (($item['app']) ? DI::l10n()->t('%s from %s', Temporal::getRelativeDate($item['created']),$item['app']) : Temporal::getRelativeDate($item['created'])),
- 'location' => $location,
+ 'location_html' => $location_html,
'indent' => '',
- 'owner_name' => $owner_name,
- 'owner_url' => $owner_url,
+ 'owner_name' => '',
+ 'owner_url' => '',
'owner_photo' => DI::baseUrl()->remove($item['owner-avatar']),
'plink' => Item::getPlink($item),
'edpost' => false,
- 'isstarred' => $isstarred,
- 'star' => $star,
+ 'isstarred' => 'unstarred',
+ 'star' => false,
'drop' => $drop,
'vote' => $likebuttons,
- 'like' => '',
- 'dislike' => '',
- 'comment' => '',
+ 'like_html' => '',
+ 'dislike_html' => '',
+ 'comment_html' => '',
'conv' => (($preview) ? '' : ['href'=> 'display/'.$item['guid'], 'title'=> DI::l10n()->t('View in context')]),
'previewing' => $previewing,
'wait' => DI::l10n()->t('Please wait'),
@@ -727,7 +719,12 @@ function conversation_fetch_comments($thread_items, $pinned) {
&& ($row['thr-parent'] == $row['parent-uri']) && ($row['received'] > $received)
&& Contact::isSharing($row['author-id'], $row['uid'])) {
$direction = ['direction' => 3, 'title' => DI::l10n()->t('%s reshared this.', $row['author-name'])];
- $actor = ['link' => $row['author-link'], 'avatar' => $row['author-avatar'], 'name' => $row['author-name']];
+
+ $author = ['uid' => 0, 'id' => $row['author-id'],
+ 'network' => $row['author-network'], 'url' => $row['author-link']];
+ $url = '' . htmlentities($row['author-name']) . '';
+
+ $actor = ['url' => $url, 'link' => $row['author-link'], 'avatar' => $row['author-avatar'], 'name' => $row['author-name']];
$received = $row['received'];
}
@@ -736,12 +733,45 @@ function conversation_fetch_comments($thread_items, $pinned) {
$direction = ['direction' => 5, 'title' => DI::l10n()->t('%s commented on this.', $row['author-name'])];
}
- if (($row['gravity'] == GRAVITY_PARENT) && !$row['origin'] && ($row['author-id'] == $row['owner-id'])
- && !Contact::isSharing($row['author-id'], $row['uid'])) {
- if ($row['post-type'] == Item::PT_TAG) {
+ switch ($row['post-type']) {
+ case Item::PT_TO:
+ $row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'to')];
+ break;
+ case Item::PT_CC:
+ $row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'cc')];
+ break;
+ case Item::PT_BTO:
+ $row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'bto')];
+ break;
+ case Item::PT_BCC:
+ $row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'bcc')];
+ break;
+ case Item::PT_FOLLOWER:
+ $row['direction'] = ['direction' => 6, 'title' => DI::l10n()->t('You are following %s.', $row['author-name'])];
+ break;
+ case Item::PT_TAG:
$row['direction'] = ['direction' => 4, 'title' => DI::l10n()->t('Tagged')];
- }
-
+ break;
+ case Item::PT_ANNOUNCEMENT:
+ $row['direction'] = ['direction' => 3, 'title' => DI::l10n()->t('Reshared')];
+ break;
+ case Item::PT_COMMENT:
+ $row['direction'] = ['direction' => 5, 'title' => DI::l10n()->t('%s is participating in this thread.', $row['author-name'])];
+ break;
+ case Item::PT_STORED:
+ $row['direction'] = ['direction' => 8, 'title' => DI::l10n()->t('Stored')];
+ break;
+ case Item::PT_GLOBAL:
+ $row['direction'] = ['direction' => 9, 'title' => DI::l10n()->t('Global')];
+ break;
+ default:
+ if ($row['uid'] == 0) {
+ $row['direction'] = ['direction' => 9, 'title' => DI::l10n()->t('Global')];
+ }
+ }
+
+ if (($row['gravity'] == GRAVITY_PARENT) && !$row['origin'] && ($row['author-id'] == $row['owner-id']) &&
+ !Contact::isSharing($row['author-id'], $row['uid'])) {
$parentlines[] = $lineno;
}
@@ -758,10 +788,13 @@ function conversation_fetch_comments($thread_items, $pinned) {
if (!empty($direction)) {
foreach ($parentlines as $line) {
$comments[$line]['direction'] = $direction;
- if (!empty($actor) && DI::pConfig()->get(local_user(), 'system', 'display_resharer') ) {
- $comments[$line]['owner-link'] = $actor['link'];
- $comments[$line]['owner-avatar'] = $actor['avatar'];
- $comments[$line]['owner-name'] = $actor['name'];
+ if (!empty($actor)) {
+ $comments[$line]['reshared'] = DI::l10n()->t('%s reshared this.', $actor['url']);
+ if (DI::pConfig()->get(local_user(), 'system', 'display_resharer') ) {
+ $comments[$line]['owner-link'] = $actor['link'];
+ $comments[$line]['owner-avatar'] = $actor['avatar'];
+ $comments[$line]['owner-name'] = $actor['name'];
+ }
}
}
}
@@ -1137,34 +1170,12 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false)
$jotplugins = '';
Hook::callAll('jot_tool', $jotplugins);
- // Private/public post links for the non-JS ACL form
- $private_post = 1;
- if (!empty($_REQUEST['public'])) {
- $private_post = 0;
- }
-
- $query_str = DI::args()->getQueryString();
- if (strpos($query_str, 'public=1') !== false) {
- $query_str = str_replace(['?public=1', '&public=1'], ['', ''], $query_str);
- }
-
- /*
- * I think $a->query_string may never have ? in it, but I could be wrong
- * It looks like it's from the index.php?q=[etc] rewrite that the web
- * server does, which converts any ? to &, e.g. suggest&ignore=61 for suggest?ignore=61
- */
- if (strpos($query_str, '?') === false) {
- $public_post_link = '?public=1';
- } else {
- $public_post_link = '&public=1';
- }
-
// $tpl = Renderer::replaceMacros($tpl,array('$jotplugins' => $jotplugins));
$tpl = Renderer::getMarkupTemplate("jot.tpl");
$o .= Renderer::replaceMacros($tpl,[
'$new_post' => DI::l10n()->t('New Post'),
- '$return_path' => $query_str,
+ '$return_path' => DI::args()->getQueryString(),
'$action' => 'item',
'$share' => ($x['button'] ?? '') ?: DI::l10n()->t('Share'),
'$loading' => DI::l10n()->t('Loading...'),
@@ -1190,7 +1201,7 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false)
'$placeholdercategory' => Feature::isEnabled(local_user(), 'categories') ? DI::l10n()->t("Categories \x28comma-separated list\x29") : '',
'$wait' => DI::l10n()->t('Please wait'),
'$permset' => DI::l10n()->t('Permission settings'),
- '$shortpermset' => DI::l10n()->t('permissions'),
+ '$shortpermset' => DI::l10n()->t('Permissions'),
'$wall' => $notes_cid ? 0 : 1,
'$posttype' => $notes_cid ? Item::PT_PERSONAL_NOTE : Item::PT_ARTICLE,
'$content' => $x['content'] ?? '',
@@ -1212,11 +1223,6 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false)
// ACL permissions box
'$acl' => $x['acl'],
- '$group_perms' => DI::l10n()->t('Post to Groups'),
- '$contact_perms' => DI::l10n()->t('Post to Contacts'),
- '$private' => DI::l10n()->t('Private post'),
- '$is_private' => $private_post,
- '$public_link' => $public_post_link,
//jot nav tab (used in some themes)
'$message' => DI::l10n()->t('Message'),
@@ -1490,13 +1496,3 @@ function sort_thr_commented(array $a, array $b)
{
return strcmp($b['commented'], $a['commented']);
}
-
-function render_location_dummy(array $item) {
- if (!empty($item['location']) && !empty($item['location'])) {
- return $item['location'];
- }
-
- if (!empty($item['coord']) && !empty($item['coord'])) {
- return $item['coord'];
- }
-}
diff --git a/include/enotify.php b/include/enotify.php
index e8e1357449..478b034e5f 100644
--- a/include/enotify.php
+++ b/include/enotify.php
@@ -87,12 +87,15 @@ function notification($params)
}
$nickname = $user["nickname"];
+ // Creates a new email builder for the notification email
+ $emailBuilder = DI::emailer()->newNotifyMail();
+
// with $params['show_in_notification_page'] == false, the notification isn't inserted into
// the database, and an email is sent if applicable.
// default, if not specified: true
$show_in_notification_page = isset($params['show_in_notification_page']) ? $params['show_in_notification_page'] : true;
- $additional_mail_header = "X-Friendica-Account: <".$nickname."@".$hostname.">\n";
+ $emailBuilder->setHeader('X-Friendica-Account', '<' . $nickname . '@' . $hostname . '>');
if (array_key_exists('item', $params)) {
$title = $params['item']['title'];
@@ -509,7 +512,8 @@ function notification($params)
Logger::log('sending notification email');
if (isset($params['parent']) && (intval($params['parent']) != 0)) {
- $id_for_parent = $params['parent'] . "@" . $hostname;
+ $parent = Item::selectFirst(['guid'], ['id' => $params['parent']]);
+ $message_id = "<" . $parent['guid'] . "@" . gethostname() . ">";
// Is this the first email notification for this parent item and user?
if (!DBA::exists('notify-threads', ['master-parent-item' => $params['parent'], 'receiver-uid' => $params['uid']])) {
@@ -520,13 +524,14 @@ function notification($params)
'receiver-uid' => $params['uid'], 'parent-item' => 0];
DBA::insert('notify-threads', $fields);
- $additional_mail_header .= "Message-ID: <${id_for_parent}>\n";
+ $emailBuilder->setHeader('Message-ID', $message_id);
$log_msg = "include/enotify: No previous notification found for this parent:\n" .
" parent: ${params['parent']}\n" . " uid : ${params['uid']}\n";
Logger::log($log_msg, Logger::DEBUG);
} else {
// If not, just "follow" the thread.
- $additional_mail_header .= "References: <${id_for_parent}>\nIn-Reply-To: <${id_for_parent}>\n";
+ $emailBuilder->setHeader('References', $message_id);
+ $emailBuilder->setHeader('In-Reply-To', $message_id);
Logger::log("There's already a notification for this parent.", Logger::DEBUG);
}
}
@@ -545,7 +550,6 @@ function notification($params)
'title' => $title,
'body' => $body,
'subject' => $subject,
- 'headers' => $additional_mail_header,
];
Hook::callAll('enotify_mail', $datarray);
@@ -564,13 +568,13 @@ function notification($params)
// If a photo is present, add it to the email
if (!empty($datarray['source_photo'])) {
- $builder->withPhoto(
+ $emailBuilder->withPhoto(
$datarray['source_photo'],
$datarray['source_link'] ?? $sitelink,
$datarray['source_name'] ?? $sitename);
}
- $email = $builder->build();
+ $email = $emailBuilder->build();
// use the Emailer class to send the message
return DI::emailer()->send($email);
diff --git a/mod/editpost.php b/mod/editpost.php
index cfca7695e0..209fbcf5ab 100644
--- a/mod/editpost.php
+++ b/mod/editpost.php
@@ -131,7 +131,7 @@ function editpost_content(App $a)
//jot nav tab (used in some themes)
'$message' => DI::l10n()->t('Message'),
'$browser' => DI::l10n()->t('Browser'),
- '$shortpermset' => DI::l10n()->t('permissions'),
+ '$shortpermset' => DI::l10n()->t('Permissions'),
'$compose_link_title' => DI::l10n()->t('Open Compose page'),
]);
diff --git a/mod/events.php b/mod/events.php
index d82fbd818e..695432e2a4 100644
--- a/mod/events.php
+++ b/mod/events.php
@@ -474,16 +474,16 @@ function events_content(App $a)
$t_orig = $orig_event['summary'] ?? '';
$d_orig = $orig_event['desc'] ?? '';
$l_orig = $orig_event['location'] ?? '';
- $eid = !empty($orig_event) ? $orig_event['id'] : 0;
- $cid = !empty($orig_event) ? $orig_event['cid'] : 0;
- $uri = !empty($orig_event) ? $orig_event['uri'] : '';
+ $eid = $orig_event['id'] ?? 0;
+ $cid = $orig_event['cid'] ?? 0;
+ $uri = $orig_event['uri'] ?? '';
if ($cid || $mode === 'edit') {
$share_disabled = 'disabled="disabled"';
}
- $sdt = !empty($orig_event) ? $orig_event['start'] : 'now';
- $fdt = !empty($orig_event) ? $orig_event['finish'] : 'now';
+ $sdt = $orig_event['start'] ?? 'now';
+ $fdt = $orig_event['finish'] ?? 'now';
$tz = date_default_timezone_get();
if (!empty($orig_event)) {
diff --git a/mod/item.php b/mod/item.php
index 3474bea90c..17f6486f89 100644
--- a/mod/item.php
+++ b/mod/item.php
@@ -260,7 +260,7 @@ function item_post(App $a) {
$objecttype = $orig_post['object-type'];
$app = $orig_post['app'];
$categories = $orig_post['file'] ?? '';
- $title = Strings::escapeTags(trim($_REQUEST['title']));
+ $title = trim($_REQUEST['title'] ?? '');
$body = trim($body);
$private = $orig_post['private'];
$pubmail_enabled = $orig_post['pubmail'];
@@ -281,13 +281,13 @@ function item_post(App $a) {
$str_group_deny = isset($_REQUEST['group_deny']) ? $aclFormatter->toString($_REQUEST['group_deny']) : $user['deny_gid'] ?? '';
}
- $title = Strings::escapeTags(trim($_REQUEST['title'] ?? ''));
- $location = Strings::escapeTags(trim($_REQUEST['location'] ?? ''));
- $coord = Strings::escapeTags(trim($_REQUEST['coord'] ?? ''));
- $verb = Strings::escapeTags(trim($_REQUEST['verb'] ?? ''));
- $emailcc = Strings::escapeTags(trim($_REQUEST['emailcc'] ?? ''));
+ $title = trim($_REQUEST['title'] ?? '');
+ $location = trim($_REQUEST['location'] ?? '');
+ $coord = trim($_REQUEST['coord'] ?? '');
+ $verb = trim($_REQUEST['verb'] ?? '');
+ $emailcc = trim($_REQUEST['emailcc'] ?? '');
$body = trim($body);
- $network = Strings::escapeTags(trim(($_REQUEST['network'] ?? '') ?: Protocol::DFRN));
+ $network = trim(($_REQUEST['network'] ?? '') ?: Protocol::DFRN);
$guid = System::createUUID();
$postopts = $_REQUEST['postopts'] ?? '';
@@ -904,40 +904,8 @@ function drop_item(int $id, string $return = '')
}
if ((local_user() == $item['uid']) || $contact_id) {
- // Check if we should do HTML-based delete confirmation
- if (!empty($_REQUEST['confirm'])) {
- //