diff --git a/.gitignore b/.gitignore
index 2d8acf016..3250fb076 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 a671cc680..3c9098251 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 2c5662c2b..4eeb72625 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 402c356b7..07d6ecb2f 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 454e445f6..14ffed05b 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 000000000..716a932e1
--- /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 fa71faf26..e92182916 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 27522d855..4d5b4c79c 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 596f4de56..3fe803d6f 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 d89521390..be7a52e32 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 b7d7125f7..9aed35303 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 b6c03a670..de860e984 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 46eff1071..5698cf16d 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 b43fd6dc2..cba94fa48 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 fad962e41..8fbe387ad 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 ebd434e39..d2b5f74a2 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 addd396ff..3b7ef341c 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 03657b6af..d58a8565a 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 e0cece958..8d66425a8 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 8746e9c15..1c3b8e119 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 5ae4754b5..643f57b63 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 64c8228ff..ed9086307 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 e8e135744..478b034e5 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 cfca7695e..209fbcf5a 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 d82fbd818..695432e2a 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 3474bea90..17f6486f8 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'])) {
- //