-
$default_timezone = "value";
|
@@ -313,7 +303,7 @@ Enabling the admin panel for an account, and thus making the account holder admi
'config' => [
'admin_email' => 'someone@example.com',
]
-
+
Where you have to match the email address used for the account with the one you enter to the `config/local.config.php` file.
If more than one account should be able to access the admin panel, separate the email addresses with a comma.
diff --git a/doc/Developers-Intro.md b/doc/Developers-Intro.md
index c500b27741..5dbf1def4a 100644
--- a/doc/Developers-Intro.md
+++ b/doc/Developers-Intro.md
@@ -62,7 +62,7 @@ If you want to have git automatically update the dependencies with composer, you
}
# `composer install` if the `composer.lock` file gets changed
# to update all the php dependencies
- check_run composer.lock "bin/composer.phar install --no-dev"
+ check_run composer.lock "bin/composer.phar install"
just place it into `.git/hooks/post-merge` and make it executable.
@@ -156,3 +156,98 @@ If you are interested in improving those clients, please contact the developers
* iOS: *currently no client*
* SailfishOS: **Friendiy** [src](https://kirgroup.com/projects/fabrixxm/harbour-friendly) - developed by [Fabio](https://kirgroup.com/profile/fabrixxm/profile)
* Windows: **Friendica Mobile** for Windows versions [before 8.1](http://windowsphone.com/s?appid=e3257730-c9cf-4935-9620-5261e3505c67) and [Windows 10](https://www.microsoft.com/store/apps/9nblggh0fhmn) - developed by [Gerhard Seeber](http://mozartweg.dyndns.org/friendica/profile/gerhard/profile)
+
+## Backward compatibility
+
+### Backward Compatibility Promise
+
+Friendica can be extended by addons.
+These addons relies on many classes and conventions from Friendica.
+As developers our work on Friendica should not break things in the addons without giving the addon maintainers a chance to fix their addons.
+Our goal is to build trust for the addon maintainers but also allow Friendica developers to move on.
+This is called the Backward Compatibility Promise.
+
+Inspired by the [Symonfy BC promise](https://symfony.com/doc/current/contributing/code/bc.html) we promise BC for every class, interface, trait, enum, function, constant, etc., but with the exception of:
+
+- Classes, interfaces, traits, enums, functions, methods, properties and constants marked as `@internal` or `@private`
+- Extending or modifying any non-abstract class or method in any way
+- Extending or modifying a `final` class or method in any way
+- Calling `private` methods (via Reflection)
+- Accessing `private` properties (via Reflection)
+- Accessing `private` methods (via Reflection)
+- Accessing `private` constants (via Reflection)
+- New properties on overridden `protected` methods
+- Possible name collisions with new methods in an extended class (addon developers should prefix their custom methods in the extending classes in an appropriate way)
+- Dropping support for every PHP version that has reached end of life
+
+### Deprecation and removing features
+
+As the development goes by Friendica needs to get rid of old code and concepts.
+This will be done in 3 steps to give addon maintainers a chance to adjust their addons.
+
+**1. Label deprecation**
+
+If we as the Friendica maintainers decide to remove some functions, classes, interface, etc. we start this by adding a `@deprecated` PHPDoc note on the code.
+For instance the class `Friendica\Core\Logger` should be removed, so we add the following note with a possible replacement:
+
+```php
+/**
+ * Logger functions
+ *
+ * @deprecated 2025.02 Use constructor injection or `DI::logger()` instead
+ */
+class Logger {/* ... */}
+```
+
+This way addon developers might be notified early by their IDE or other tools that the usage of the class is deprecated.
+In Friendica we can now start to replace all occurrences and usage of this class with the alternative.
+
+The deprecation label COULD be remain over multiple releases.
+As long as the code that is labeled with `@deprecated` is used inside Friendica or the official addon repository, it SHOULD NOT be hard deprecated.
+
+**2. Hard deprecation**
+
+If the deprecated code is no longer used inside Friendica or the official addons it MUST be hard deprecated.
+The code MUST NOT be deleted.
+Starting from the next release, it MUST be stay for at least 5 months.
+Hard deprecated code COULD remain longer than 5 months, depending on when a release appears.
+Addon developer SHOULD NOT consider that they have more than 5 months to adjust their code.
+
+Hard deprecation code means that the code triggers a muted `E_USER_DEPRECATION` error if it is called.
+For instance with the deprecated class `Friendica\Core\Logger` the call of every method should trigger an error:
+
+```php
+/**
+ * Logger functions
+ *
+ * @deprecated 2025.02 Use constructor injection or `DI::logger()` instead
+ */
+class Logger {
+ public static function info(string $message, array $context = [])
+ {
+ @trigger_error('Class `' . __CLASS__ . '` is deprecated since 2025.05 and will be removed after 5 months, use constructor injection or `DI::logger()` instead.', E_USER_DEPRECATED);
+
+ self::getInstance()->info($message, $context);
+ }
+
+ /* ... */
+}
+```
+
+This way the maintainer or users of addons will be notified in the logs that the addon will stop working in one of the next releases.
+The addon maintainer now has at least 5 months or at least one release to fix the deprecations.
+
+Please note that the deprecation message contains the release that will be released next.
+In the example the code was hard deprecated with release `2025.05`, so it COULD be removed earliest with the `2025.11` release.
+
+**3. Code Removing**
+
+We promise BC for deprecated code for at least 5 months, starting from the release the deprecation was announced.
+After this time the deprecated code COULD be remove within the next release.
+
+Breaking changes MUST be happen only in a new release but MUST be hard deprecated first.
+The BC promise refers only to releases, respective the `stable` branch.
+Deprecated code on other branches like `develop` or RC branches could be removed earlier.
+This is not a BC break as long as the release will be published 5 months after the hard deprecation.
+
+If a release breaks BC without deprecation or earlier than 5 months, this SHOULD considered as a bug and BC SHOULD be restored in a bugfix release.
diff --git a/doc/FAQ.md b/doc/FAQ.md
index 5c934f1c14..4e8d9b19eb 100644
--- a/doc/FAQ.md
+++ b/doc/FAQ.md
@@ -177,15 +177,18 @@ Example: Friendica Support
Friendica supports [Mastodon API](help/API-Mastodon) and [Twitter API | gnusocial](help/api).
This means you can use some of the Mastodon and Twitter clients for Friendica.
The available features are client specific and may differ.
+Clients dedicated to Friendica are marked in **bold**.
#### Android
* [AndStatus](http://andstatus.org) ([F-Droid](https://f-droid.org/repository/browse/?fdid=org.andstatus.app), [Google Play](https://play.google.com/store/apps/details?id=org.andstatus.app))
* [Fedilab](https://fedilab.app) ([F-Droid](https://f-droid.org/app/fr.gouv.etalab.mastodon), [Google Play](https://play.google.com/store/apps/details?id=app.fedilab.android))
-* [Friendiqa](https://git.friendi.ca/lubuwest/Friendiqa) ([F-Droid](https://git.friendi.ca/lubuwest/Friendiqa#install), [Google Play](https://play.google.com/store/apps/details?id=org.qtproject.friendiqa))
+* **[Friendiqa](https://git.friendi.ca/lubuwest/Friendiqa)** ([F-Droid](https://git.friendi.ca/lubuwest/Friendiqa#install), [Google Play](https://play.google.com/store/apps/details?id=org.qtproject.friendiqa))
* [Husky](https://codeberg.org/husky/husky) ([F-Droid](https://f-droid.org/repository/browse/?fdid=su.xash.husky), [Google Play](https://play.google.com/store/apps/details?id=su.xash.husky))
* [Mastodon](https://github.com/mastodon/mastodon-android) ([F-Droid](https://f-droid.org/en/packages/org.joinmastodon.android/), [Google Play](https://play.google.com/store/apps/details?id=org.joinmastodon.android))
* [Pachli](https://pachli.app/) ([F-Droid](https://f-droid.org/en/packages/app.pachli/), [Google Play](https://play.google.com/store/apps/details?id=app.pachli))
+* **[Raccoon for Friendica](https://github.com/LiveFastEatTrashRaccoon/RaccoonForFriendica)** ([F-Droid](https://f-droid.org/packages/com.livefast.eattrash.raccoonforfriendica), [Google Play](https://play.google.com/apps/testing/com.livefast.eattrash.raccoonforfriendica))
+* **[Relatica](https://gitlab.com/mysocialportal/relatica)**
* [Subway Tooter](https://github.com/tateisu/SubwayTooter) ([F-Droid via Izzy](https://android.izzysoft.de/repo/apk/jp.juggler.subwaytooter.noFcm))
* [Tooot](https://tooot.app/) ([Google Play](https://play.google.com/store/apps/details?id=com.xmflsct.app.tooot))
* [Tusky](https://tusky.app) ([F-Droid](https://f-droid.org/repository/browse/?fdid=com.keylesspalace.tusky), [Google Play](https://play.google.com/store/apps/details?id=com.keylesspalace.tusky))
@@ -195,6 +198,7 @@ The available features are client specific and may differ.
#### iOS
* [Mastodon](https://joinmastodon.org/apps) ([App Store](https://apps.apple.com/us/app/mastodon-for-iphone/id1571998974))
+* **[Relatica](https://gitlab.com/mysocialportal/relatica)**
* [Stella*](https://www.stella-app.net/) ([App Store](https://apps.apple.com/us/app/stella-for-mastodon-twitter/id921372048))
* [Tooot](https://github.com/tooot-app) ([App Store](https://apps.apple.com/app/id1549772269))
* [TwidereX](https://github.com/TwidereProject/TwidereX-iOS) ([App Store](https://apps.apple.com/app/twidere-x/id1530314034))
@@ -202,17 +206,20 @@ The available features are client specific and may differ.
#### Linux
* [Choqok](https://choqok.kde.org)
-* [Whalebird](https://whalebird.social/en/desktop/contents) ([GitHub](https://github.com/h3poteto/whalebird-desktop))
+* **[Relatica](https://gitlab.com/mysocialportal/relatica)**
* [TheDesk](https://thedesk.top/en/) ([GitHub](https://github.com/cutls/TheDesk))
* [Toot](https://toot.readthedocs.io/en/latest/)
+* [Whalebird](https://whalebird.social/en/desktop/contents) ([GitHub](https://github.com/h3poteto/whalebird-desktop))
#### macOS
+* **[Relatica](https://gitlab.com/mysocialportal/relatica)**
* [TheDesk](https://thedesk.top/en/) ([GitHub](https://github.com/cutls/TheDesk))
* [Whalebird](https://whalebird.social/en/desktop/contents) ([App Store](https://apps.apple.com/de/app/whalebird/id1378283354), [GitHub](https://github.com/h3poteto/whalebird-desktop))
#### Windows
+* **[Relatica](https://gitlab.com/mysocialportal/relatica)**
* [TheDesk](https://thedesk.top/en/) ([GitHub](https://github.com/cutls/TheDesk))
* [Whalebird](https://whalebird.social/en/desktop/contents) ([Microsoft Store](https://apps.microsoft.com/detail/9nbw4csdv5hc), [GitHub](https://github.com/h3poteto/whalebird-desktop))
diff --git a/doc/Install.md b/doc/Install.md
index 936d46c7a9..3c162d519b 100644
--- a/doc/Install.md
+++ b/doc/Install.md
@@ -76,14 +76,6 @@ This makes the software much easier to update.
The Linux commands to clone the repository into a directory "mywebsite" would be
git clone https://github.com/friendica/friendica.git -b stable mywebsite
- cd mywebsite
- bin/composer.phar install --no-dev
-
-Make sure the folder *view/smarty3* exists and is writable by the webserver user, in this case *www-data*
-
- mkdir -p view/smarty3
- chown www-data:www-data view/smarty3
- chmod 775 view/smarty3
Get the addons by going into your website folder.
@@ -91,12 +83,22 @@ Get the addons by going into your website folder.
Clone the addon repository (separately):
- git clone https://github.com/friendica/friendica-addons.git -b stable addon
+ git clone https://github.com/friendica/friendica-addons.git -b stable addon
+
+Install the dependencies:
+
+ bin/composer.phar run install:prod
+
+Make sure the folder *view/smarty3* exists and is writable by the webserver user, in this case *www-data*
+
+ mkdir -p view/smarty3
+ chown www-data:www-data view/smarty3
+ chmod 775 view/smarty3
If you want to use the development version of Friendica you can switch to the develop branch in the repository by running
git checkout develop
- bin/composer.phar install
+ bin/composer.phar run install:prod
cd addon
git checkout develop
@@ -216,7 +218,6 @@ All options will be saved in the `config/local.config.php` and are overruling th
- `-U|--dbuser ` The username of the mysql/mariadb database login (env `MYSQL_USER` or `MYSQL_USERNAME`)
- `-P|--dbpass ` The password of the mysql/mariadb database login (env `MYSQL_PASSWORD`)
- `-d|--dbdata ` The name of the mysql/mariadb database (env `MYSQL_DATABASE`)
-- `-u|--urlpath ` The URL path of Friendica - f.e. '/friendica' (env `FRIENDICA_URL_PATH`)
- `-b|--phppath ` The path of the PHP binary (env `FRIENDICA_PHP_PATH`)
- `-A|--admin ` The admin email address of Friendica (env `FRIENDICA_ADMIN_MAIL`)
- `-T|--tz ` The timezone of Friendica (env `FRIENDICA_TZ`)
@@ -268,7 +269,7 @@ You might wish to delete/rename `config/local.config.php` to another name and dr
Set up a cron job or scheduled task to run the worker once every 5-10 minutes in order to perform background processing.
Example:
- cd /base/directory; /path/to/php bin/worker.php
+ cd /base/directory; /path/to/php bin/console.php worker
Change "/base/directory", and "/path/to/php" as appropriate for your situation.
@@ -277,7 +278,7 @@ Change "/base/directory", and "/path/to/php" as appropriate for your situation.
If you are using a Linux server, run "crontab -e" and add a line like the
one shown, substituting for your unique paths and settings:
- */10 * * * * cd /home/myname/mywebsite; /usr/bin/php bin/worker.php
+ */10 * * * * cd /home/myname/mywebsite; /usr/bin/php bin/console.php worker
You can generally find the location of PHP by executing "which php".
If you run into trouble with this section please contact your hosting provider for assistance.
@@ -290,11 +291,11 @@ Once you have installed Friendica and created an admin account as part of the pr
#### worker alternative: daemon
Otherwise, you’ll need to use the command line on your remote server and start the Friendica daemon (background task) using the following command:
- cd /path/to/friendica; php bin/daemon.php start
+ cd /path/to/friendica; php bin/console.php daemon start
Once started, you can check the daemon status using the following command:
- cd /path/to/friendica; php bin/daemon.php status
+ cd /path/to/friendica; php bin/console.php daemon status
After a server restart or any other failure, the daemon needs to be restarted.
This could be achieved by a cronjob.
@@ -426,7 +427,7 @@ provided by one of our members.
>
> */10 * * * * cd /var/www/friendica/friendica/ && sudo -u www-data /usr/bin/php \
> -d suhosin.executor.func.blacklist=none \
-> -d suhosin.executor.eval.blacklist=none -f bin/worker.php
+> -d suhosin.executor.eval.blacklist=none -f bin/console.php
>
> This worked well for simple test cases, but the friendica-cron still failed
> with a fatal error:
@@ -435,7 +436,7 @@ provided by one of our members.
> (attacker 'REMOTE_ADDR not set', file '/var/www/friendica/friendica/boot.php',
> line 1341)
>
-> After a while I noticed, that `bin/worker.php` calls further PHP script via `proc_open`.
+> After a while I noticed, that `bin/console.php worker` calls further PHP script via `proc_open`.
> These scripts themselves also use `proc_open` and fail, because they are NOT
> called with `-d suhosin.executor.func.blacklist=none`.
>
diff --git a/doc/Installing-Connectors.md b/doc/Installing-Connectors.md
index 48c3c9fb1e..bd564eb466 100644
--- a/doc/Installing-Connectors.md
+++ b/doc/Installing-Connectors.md
@@ -28,7 +28,7 @@ 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.
-To enable Jetstream processing, run `bin/jetstream.php' from the command line.
+To enable Jetstream processing, run `bin/console.php jetstream' 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'.
-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.
\ No newline at end of file
+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.
diff --git a/doc/Migrate.md b/doc/Migrate.md
index e116d029f6..ca0e0d0d95 100644
--- a/doc/Migrate.md
+++ b/doc/Migrate.md
@@ -55,9 +55,9 @@ You should see an output like this:
Finally, you may also want to optimise your database with the following command: ``mysqloptimize -p friendica-db``
-### Going offline
+### Going offline
Stop background tasks and put your server in maintenance mode.
-1. If you had set up a worker cron job like this ``*/10 * * * * cd /var/www/friendica; /usr/bin/php bin/worker.php`` run ``crontab -e`` and comment out this line. Alternatively if you deploy a worker daemon, disable this instead.
+1. If you had set up a worker cron job like this ``*/10 * * * * cd /var/www/friendica; /usr/bin/php bin/console.php worker`` run ``crontab -e`` and comment out this line. Alternatively if you deploy a worker daemon, disable this instead.
2. Put your server into maintenance mode: ``bin/console maintenance 1 "We are currently upgrading our system and will be back soon."``
## Dumping DB
@@ -73,12 +73,12 @@ Import your database on your new server: ``mysql -p friendica_db < your-friendic
### Configuration file
Copy your old server's configuration file to ``config/local.config.php``.
-Ensure the newly created database credentials are identical to the setting in the configuration file; otherwise update them accordingly.
+Ensure the newly created database credentials are identical to the setting in the configuration file; otherwise update them accordingly.
### Cron job for worker
Set up the required daily cron job.
Run ``crontab -e`` and add the following line according to your system specification
-``*/10 * * * * cd /var/www/friendica; /usr/bin/php bin/worker.php``
+``*/10 * * * * cd /var/www/friendica; /usr/bin/php bin/console.php worker``
### DNS settings
Adjust your DNS records by pointing them to your new server.
diff --git a/doc/Quick-Start-makingnewfriends.md b/doc/Quick-Start-makingnewfriends.md
index 39edd19b41..d642905698 100644
--- a/doc/Quick-Start-makingnewfriends.md
+++ b/doc/Quick-Start-makingnewfriends.md
@@ -1,5 +1,5 @@
This is your Suggested Friends page.
-If you get lost, you can click this link to bring yourself back here.
+If you get lost, you can click this link to bring yourself back here.
This is a bit like the Friend Suggestions page of Facebook.
Everybody on this list has agreed that they may be suggested as a friend.
@@ -16,6 +16,6 @@ Click the link at the top of this page to go back to the suggested friends list
Feel uncomfortable adding people you don't know?
Don't worry - that's where Groups and Pages come in!
-
+
diff --git a/doc/Settings.md b/doc/Settings.md
index 00c8be2c2e..7ca0e22286 100644
--- a/doc/Settings.md
+++ b/doc/Settings.md
@@ -419,7 +419,7 @@ We strongly discourage you from doing so, as this will break federation to other
Say you have a subdirectory for tests and put Friendica into a further subdirectory, the config would be:
'system' => [
- 'urlpath' => 'tests/friendica',
+ 'url' => 'https://example.com/tests/friendica',
],
## Other exceptions
diff --git a/doc/StrategyHooks.md b/doc/StrategyHooks.md
index 2960ceeaad..440728783c 100644
--- a/doc/StrategyHooks.md
+++ b/doc/StrategyHooks.md
@@ -83,6 +83,8 @@ return [
## Addons
+> ⚠️ Since Friendica 2025.02 the strategy hooks for addons are deprecated, please use PHP hooks instead.
+
The hook logic is useful for decoupling the Friendica core logic, but its primary goal is to modularize Friendica in creating addons.
Therefor you can either use the interfaces directly as shown above, or you can place your own `hooks.config.php` file inside a `static` directory directly under your addon core directory.
diff --git a/doc/Update.md b/doc/Update.md
index 40de14082c..b6c5c53e19 100644
--- a/doc/Update.md
+++ b/doc/Update.md
@@ -14,7 +14,7 @@ If you installed Friendica in the ``path/to/friendica`` folder:
* ``.htaccess`` if using Apache web server
The following items only need to be copied if they are located inside your friendica path:
- * your storage folder as set in **Admin -> Site -> File Upload -> Storage base path**
+ * your storage folder as set in **Admin -> Site -> File Upload -> Storage base path**
* your item cache as set in **Admin -> Site -> Performance -> Path to item cache**
* your temp folder as set in **Admin -> Site -> Advanced -> Temp path**
3. Rename the ``path/to/friendica`` folder to ``path/to/friendica_old``.
@@ -30,7 +30,7 @@ You can get the latest changes at any time with
cd path/to/friendica
git pull
- bin/composer.phar install --no-dev
+ bin/composer.phar run install:prod
The addon tree has to be updated separately like so:
@@ -89,7 +89,7 @@ Some of the updates include the use of foreign keys now that will bump into issu
```
Error 1452 occurred during database update:
Cannot add or update a child row: a foreign key constraint fails (`friendica`.`#sql-10ea6_5a6d`, CONSTRAINT `#sql-10ea6_5a6d_ibfk_1` FOREIGN KEY (`contact-id`) REFERENCES `contact` (`id`))
-ALTER TABLE `thread` ADD FOREIGN KEY (`iid`) REFERENCES `item` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;
+ALTER TABLE `thread` ADD FOREIGN KEY (`iid`) REFERENCES `item` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;
```
All current known fixes for possible items that can go wrong are as below.
diff --git a/doc/Vagrant.md b/doc/Vagrant.md
index 16088cd37e..29a2871bd8 100644
--- a/doc/Vagrant.md
+++ b/doc/Vagrant.md
@@ -18,7 +18,7 @@ What you need to do:
Please use an up-to-date vagrant version from https://www.vagrantup.com/downloads.html.
2. Git clone your Friendica repository.
Inside, you'll find a `Vagrantfile` and some scripts in the `bin/dev` folder.
-Pull the PHP requirements with `bin/composer install`.
+Pull the PHP requirements with `bin/composer.phar install`.
3. Run `vagrant up` from inside the friendica clone.
This will start the virtual machine.
Be patient: When it runs for the first time, it downloads a Debian Server image and installs Friendica.
@@ -60,7 +60,7 @@ Trouble Shooting
If you see a version mis-match for the _VirtualBox Guest Additions_ between host and guest during the initial setup of the Vagrant VM, you will need to install an addon to Vagrant (ref. [Stack Overflow](https://stackoverflow.com/a/38010683)).
Stop the Vagrant VM and run the following command:
- $> vagrant plugin install vagrant-vbguest
+ $> vagrant plugin install vagrant-vbguest
On the next Vagrant up, the version problem should be fixed.
diff --git a/doc/autoloader.md b/doc/autoloader.md
index 954c28813c..5bc0bfe9b3 100644
--- a/doc/autoloader.md
+++ b/doc/autoloader.md
@@ -46,9 +46,9 @@ The code will be something like:
// mod/network.php
getAll();
diff --git a/doc/database/db_gserver.md b/doc/database/db_gserver.md
index 0d64ef27a1..22939ec2bc 100644
--- a/doc/database/db_gserver.md
+++ b/doc/database/db_gserver.md
@@ -41,16 +41,24 @@ Fields
| blocked | Server is blocked | boolean | YES | | NULL | |
| failed | Connection failed | boolean | YES | | NULL | |
| next_contact | Next connection request | datetime | YES | | 0001-01-01 00:00:00 | |
+| redirect-gsid | Target Gserver id in case of a redirect | int unsigned | YES | | NULL | |
Indexes
------------
-| Name | Fields |
-| ------------ | ----------------- |
-| PRIMARY | id |
-| nurl | UNIQUE, nurl(190) |
-| next_contact | next_contact |
-| network | network |
+| Name | Fields |
+| ------------- | ----------------- |
+| PRIMARY | id |
+| nurl | UNIQUE, nurl(190) |
+| next_contact | next_contact |
+| network | network |
+| redirect-gsid | redirect-gsid |
+Foreign Keys
+------------
+
+| Field | Target Table | Target Field |
+|-------|--------------|--------------|
+| redirect-gsid | [gserver](help/database/db_gserver) | id |
Return to [database documentation](help/database)
diff --git a/doc/database/db_post-media.md b/doc/database/db_post-media.md
index 02c9a7d98a..acbd1f75cb 100644
--- a/doc/database/db_post-media.md
+++ b/doc/database/db_post-media.md
@@ -30,6 +30,9 @@ Fields
| publisher-url | URL of the publisher of the media | varbinary(383) | YES | | NULL | |
| publisher-name | Name of the publisher of the media | varchar(255) | YES | | NULL | |
| publisher-image | Image of the publisher of the media | varbinary(383) | YES | | NULL | |
+| language | Language information about this media in the ISO 639 format | char(3) | YES | | NULL | |
+| published | Publification date of this media | datetime | YES | | NULL | |
+| modified | Modification date of this media | datetime | YES | | NULL | |
Indexes
------------
diff --git a/doc/database/db_post-user.md b/doc/database/db_post-user.md
index f702502bf2..502c49c3ac 100644
--- a/doc/database/db_post-user.md
+++ b/doc/database/db_post-user.md
@@ -44,31 +44,31 @@ Fields
Indexes
------------
-| Name | Fields |
-| -------------------- | ----------------------- |
-| PRIMARY | id |
-| uid_uri-id | UNIQUE, uid, uri-id |
-| uri-id | uri-id |
-| parent-uri-id | parent-uri-id |
-| thr-parent-id | thr-parent-id |
-| external-id | external-id |
-| replies-id | replies-id |
-| owner-id | owner-id |
-| author-id | author-id |
-| causer-id | causer-id |
-| vid | vid |
-| contact-id | contact-id |
-| event-id | event-id |
-| psid | psid |
-| author-id_uid | author-id, uid |
-| author-id_created | author-id, created |
-| owner-id_created | owner-id, created |
-| parent-uri-id_uid | parent-uri-id, uid |
-| uid_wall_received | uid, wall, received |
-| uid_contactid | uid, contact-id |
-| uid_unseen_contactid | uid, unseen, contact-id |
-| uid_unseen | uid, unseen |
-| uid_hidden_uri-id | uid, hidden, uri-id |
+| Name | Fields |
+| --------------------- | ----------------------- |
+| PRIMARY | id |
+| uid_uri-id | UNIQUE, uid, uri-id |
+| uri-id_origin_deleted | uri-id, origin, deleted |
+| parent-uri-id | parent-uri-id |
+| thr-parent-id | thr-parent-id |
+| external-id | external-id |
+| replies-id | replies-id |
+| owner-id | owner-id |
+| author-id | author-id |
+| causer-id | causer-id |
+| vid | vid |
+| contact-id | contact-id |
+| event-id | event-id |
+| psid | psid |
+| author-id_uid | author-id, uid |
+| author-id_created | author-id, created |
+| owner-id_created | owner-id, created |
+| parent-uri-id_uid | parent-uri-id, uid |
+| uid_wall_received | uid, wall, received |
+| uid_contactid | uid, contact-id |
+| uid_unseen_contactid | uid, unseen, contact-id |
+| uid_unseen | uid, unseen |
+| uid_hidden_uri-id | uid, hidden, uri-id |
Foreign Keys
------------
diff --git a/doc/de/Addons.md b/doc/de/Addons.md
index 607f2d50dc..bfc0107528 100644
--- a/doc/de/Addons.md
+++ b/doc/de/Addons.md
@@ -359,10 +359,10 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap
Hook::callAll('register_account', $uid);
Hook::callAll('remove_user', $user);
-
+
### src/Content/ContactBlock.php
- Hook::callAll('contact_block_end', $arr);
+ Hook::callAll('contact_block_end', $text);
### src/Content/Text/BBCode.php
diff --git a/doc/de/Install.md b/doc/de/Install.md
index 147a9aebc7..205127e34e 100644
--- a/doc/de/Install.md
+++ b/doc/de/Install.md
@@ -59,7 +59,7 @@ Der Linux-Code, mit dem man die Dateien direkt in ein Verzeichnis wie "meinewebs
git clone https://github.com/friendica/friendica.git -b stable mywebsite
cd mywebsite
- bin/composer.phar install
+ bin/composer.phar run install:prod
Stelle sicher, dass der Ordner *view/smarty3* existiert and von dem Webserver-Benutzer beschreibbar ist
@@ -85,7 +85,7 @@ Wenn du die Entwickler Version von Friendica verwenden möchtest kannst du auf d
Dies tust du mit den folgenden Befehlen
git checkout develop
- bin/composer.phar install
+ bin/composer.phar run install:prod
cd addon
git checkout develop
@@ -210,13 +210,13 @@ Gehe in den Friendica-Hauptordner und führe den Kommandozeilen Befehl aus:
Erstelle einen Cron job oder einen regelmäßigen Task, um den Poller alle 5-10 Minuten im Hintergrund ablaufen zu lassen.
Beispiel:
- cd /base/directory; /path/to/php bin/worker.php
+ cd /base/directory; /path/to/php bin/console.php worker
Ändere "/base/directory" und "/path/to/php" auf deine Systemvorgaben.
Wenn du einen Linux-Server nutzt, benutze den Befehl "crontab -e" und ergänze eine Zeile wie die Folgende; angepasst an dein System
-`*/10 * * * * cd /home/myname/mywebsite; /usr/bin/php bin/worker.php`
+`*/10 * * * * cd /home/myname/mywebsite; /usr/bin/php bin/console.php worker`
Du kannst den PHP-Pfad finden, indem du den Befehl „which php“ ausführst.
Wenn du Schwierigkeiten mit diesem Schritt hast, kannst du deinen Hosting-Anbieter kontaktieren.
diff --git a/doc/de/Installing-Connectors.md b/doc/de/Installing-Connectors.md
index 1ca152ace9..7d378e2374 100644
--- a/doc/de/Installing-Connectors.md
+++ b/doc/de/Installing-Connectors.md
@@ -27,7 +27,7 @@ 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.
-Um die Jetstream-Verarbeitung zu aktivieren, führe `bin/jetstream.php' über die Befehlszeile aus.
+Um die Jetstream-Verarbeitung zu aktivieren, führe `bin/console.php daemon' über die Befehlszeile aus.
Du musst vorher die Prozess-ID-Datei in local.config.php im Abschnitt „jetstream“ mit dem Schlüssel „pidfile“ definieren.
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.
diff --git a/doc/de/Quick-Start-makingnewfriends.md b/doc/de/Quick-Start-makingnewfriends.md
index b7381cddf9..701fba505f 100644
--- a/doc/de/Quick-Start-makingnewfriends.md
+++ b/doc/de/Quick-Start-makingnewfriends.md
@@ -3,26 +3,26 @@ Neue Freunde finden
* [Zur Startseite der Hilfe](help)
-Hier siehst Du die Kontaktvorschläge.
-Wenn Du Dich mal verirrt hast, kannst Du diesen Link klicken und wieder hierher kommen.
+Hier siehst Du die Kontaktvorschläge.
+Wenn Du Dich mal verirrt hast, kannst Du diesen Link klicken und wieder hierher kommen.
-Diese Seite funktioniert in etwa wie die Seite für Kontaktvorschläge in Facebook.
-Jeder auf dieser Liste hat zugestimmt, als Kontaktvorschlag zu erscheinen.
+Diese Seite funktioniert in etwa wie die Seite für Kontaktvorschläge in Facebook.
+Jeder auf dieser Liste hat zugestimmt, als Kontaktvorschlag zu erscheinen.
Das bedeutet, das sie Anfragen meist nicht ablehnen, da sie neue Leute kennenlernen wollen.
-Siehst Du jemanden, der Dir interessant erscheint?
-Klicke auf den "Verbinden"-Knopf beim Foto.
-Als nächstes kommst Du zur Seite "Freundschafts-/Kontaktanfrage".
-Fülle das Formular wie vorgegeben aus und trage optional eine kleine Notiz ein.
-Nun musst Du nur noch auf die Bestätigung warten.
+Siehst Du jemanden, der Dir interessant erscheint?
+Klicke auf den "Verbinden"-Knopf beim Foto.
+Als nächstes kommst Du zur Seite "Freundschafts-/Kontaktanfrage".
+Fülle das Formular wie vorgegeben aus und trage optional eine kleine Notiz ein.
+Nun musst Du nur noch auf die Bestätigung warten.
Beachte dabei, dass es sich um reale Personen handelt und es somit etwas dauern kann.
-Jetzt, nachdem Du jemanden hinzugefügt hast, weißt Du vielleicht nicht mehr, wie Du zurückkommst.
+Jetzt, nachdem Du jemanden hinzugefügt hast, weißt Du vielleicht nicht mehr, wie Du zurückkommst.
Klicke einfach auf den Link oben auf dieser Seite und Du gelangst zur Seite mit den Kontaktvorschlägen zurück, um weitere Personen hinzuzufügen.
-Du willst nicht einfach Personen hinzufügen, die du nicht kennst?
+Du willst nicht einfach Personen hinzufügen, die du nicht kennst?
Kein Problem - an dieser Stelle kommen wir zu den Gruppen und Seiten.
-
+
diff --git a/doc/de/Settings.md b/doc/de/Settings.md
index 34b349e885..cee965ccaf 100644
--- a/doc/de/Settings.md
+++ b/doc/de/Settings.md
@@ -410,7 +410,7 @@ Wir raten allerdings dringen davon ab, da es die Interoperabilität mit anderen
Mal angenommen, du hast ein Unterverzeichnis tests und willst Friendica in ein weiteres Unterverzeichnis installieren, dann lautet die Konfiguration hierfür:
'system' => [
- 'urlpath' => 'tests/friendica',
+ 'url' => 'https://example.com/tests/friendica',
],
## Weitere Ausnahmen
diff --git a/doc/stats.md b/doc/stats.md
new file mode 100644
index 0000000000..1b6a2dfd2a
--- /dev/null
+++ b/doc/stats.md
@@ -0,0 +1,35 @@
+Monitoring
+===========
+
+* [Home](help)
+
+## Endpoints
+
+Currently, there are two endpoints for statistics available
+
+- `/stats` Returns some basic statistics of the current node
+- `/stats/caching` Returns statistics of cache or lock instances, which are used for the currend node
+
+### `/stats`
+
+The statistics contain data about the worker performance, the last cron call, number of reports, inbound and outbound packets, posts and comments.
+
+### `/stats/caching`
+
+The statistics contain data about the opcache, the used caching (like memory usage, hits/misses, entries, ...) and the used lock (including the cache data)
+
+## Configuration
+
+Please define 'stats_key' in your local.config.php in the 'system' section to be able to access the statistics page at /stats?key=your-defined-stats_key
+
+## 3rd Party monitoring tools
+
+### Zabbix
+
+To monitor the health status of your Friendica installation, you can use for example a tool like Zabbix.
+
+### Prometheus
+
+To use [prometheus](https://prometheus.io) for gathering metrics, use the [Friendica exporter](https://git.friendi.ca/friendica/friendica-exporter).
+
+You can find the installation instructions here: https://git.friendi.ca/friendica/friendica-exporter#installation
diff --git a/doc/tools.md b/doc/tools.md
index 2a273e3650..fac1f4b392 100644
--- a/doc/tools.md
+++ b/doc/tools.md
@@ -78,15 +78,3 @@ The following will compress */var/log/friendica* (assuming this is the location
daily
rotate 2
}
-
-### Zabbix
-
-To monitor the health status of your Friendica installation, you can use for example a tool like Zabbix. Please define 'stats_key' in your local.config.php in the 'system' section to be able to access the statistics page at /stats?key=your-defined-stats_key
-
-The statistics contain data about the worker performance, the last cron call, number of reports, inbound and outbound packets, posts and comments.
-
-### Prometheus
-
-To use [prometheus](https://prometheus.io) for gathering metrics, use the [Friendica exporter](https://git.friendi.ca/friendica/friendica-exporter).
-
-You can find the installation instructions here: https://git.friendi.ca/friendica/friendica-exporter#installation
diff --git a/index.php b/index.php
index 09620ab4c1..c1265556f6 100644
--- a/index.php
+++ b/index.php
@@ -5,38 +5,18 @@
//
// SPDX-License-Identifier: AGPL-3.0-or-later
-use Dice\Dice;
-
$start_time = microtime(true);
if (!file_exists(__DIR__ . '/vendor/autoload.php')) {
- die('Vendor path not found. Please execute "bin/composer.phar --no-dev install" on the command line in the web root.');
+ die('Vendor path not found. Please execute "bin/composer.phar run install:prod" on the command line in the web root.');
}
require __DIR__ . '/vendor/autoload.php';
-$dice = (new Dice())->addRules(include __DIR__ . '/static/dependencies.config.php');
-/** @var \Friendica\Core\Addon\Capability\ICanLoadAddons $addonLoader */
-$addonLoader = $dice->create(\Friendica\Core\Addon\Capability\ICanLoadAddons::class);
-$dice = $dice->addRules($addonLoader->getActiveAddonConfig('dependencies'));
-$dice = $dice->addRule(Friendica\App\Mode::class, ['call' => [['determineRunMode', [false, $_SERVER], Dice::CHAIN_CALL]]]);
+$request = \GuzzleHttp\Psr7\ServerRequest::fromGlobals();
-\Friendica\DI::init($dice);
+$container = \Friendica\Core\DiceContainer::fromBasePath(__DIR__);
-\Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class));
+$app = \Friendica\App::fromContainer($container);
-$a = \Friendica\DI::app();
-
-\Friendica\DI::mode()->setExecutor(\Friendica\App\Mode::INDEX);
-
-$a->runFrontend(
- $dice->create(\Friendica\App\Router::class),
- $dice->create(\Friendica\Core\PConfig\Capability\IManagePersonalConfigValues::class),
- $dice->create(\Friendica\Security\Authentication::class),
- $dice->create(\Friendica\App\Page::class),
- $dice->create(\Friendica\Content\Nav::class),
- $dice->create(Friendica\Module\Special\HTTPException::class),
- new \Friendica\Util\HTTPInputData($_SERVER),
- $start_time,
- $_SERVER
-);
+$app->processRequest($request, $start_time);
diff --git a/mod/item.php b/mod/item.php
index 445e2ebc22..e416126a4a 100644
--- a/mod/item.php
+++ b/mod/item.php
@@ -16,16 +16,14 @@
* information.
*/
-use Friendica\App;
use Friendica\Content\Conversation;
use Friendica\Content\Text\BBCode;
-use Friendica\Core\Hook;
-use Friendica\Core\Logger;
use Friendica\Core\Protocol;
use Friendica\Core\System;
use Friendica\Core\Worker;
use Friendica\Database\DBA;
use Friendica\DI;
+use Friendica\Event\ArrayFilterEvent;
use Friendica\Model\Contact;
use Friendica\Model\Item;
use Friendica\Model\ItemURI;
@@ -45,7 +43,11 @@ function item_post()
item_drop($uid, $_REQUEST['dropitems']);
}
- Hook::callAll('post_local_start', $_REQUEST);
+ $eventDispatcher = DI::eventDispatcher();
+
+ $_REQUEST = $eventDispatcher->dispatch(
+ new ArrayFilterEvent(ArrayFilterEvent::INSERT_POST_LOCAL_START, $_REQUEST)
+ )->getArray();
$return_path = $_REQUEST['return'] ?? '';
$preview = intval($_REQUEST['preview'] ?? 0);
@@ -57,7 +59,7 @@ function item_post()
*/
if (!$preview && !empty($_REQUEST['post_id_random'])) {
if (DI::session()->get('post-random') == $_REQUEST['post_id_random']) {
- Logger::warning('duplicate post');
+ DI::logger()->warning('duplicate post');
item_post_return(DI::baseUrl(), $return_path);
} else {
DI::session()->set('post-random', $_REQUEST['post_id_random']);
@@ -133,10 +135,10 @@ function item_insert(int $uid, array $request, bool $preview, string $return_pat
$post = DI::contentItem()->initializePost($post);
$post['edit'] = null;
- $post['post-type'] = $request['post_type'] ?? '';
- $post['wall'] = $request['wall'] ?? true;
+ $post['post-type'] = $request['post_type'] ?? '';
+ $post['wall'] = $request['wall'] ?? true;
$post['pubmail'] = $request['pubmail_enable'] ?? false;
- $post['created'] = $request['created_at'] ?? DateTimeFormat::utcNow();
+ $post['created'] = $request['created_at'] ?? DateTimeFormat::utcNow();
$post['edited'] = $post['changed'] = $post['commented'] = $post['created'];
$post['app'] = '';
$post['inform'] = '';
@@ -166,18 +168,18 @@ function item_insert(int $uid, array $request, bool $preview, string $return_pat
// This enables interaction like starring and saving into folders
if ($toplevel_item['uid'] == 0) {
$stored = Item::storeForUserByUriId($toplevel_item['uri-id'], $post['uid'], ['post-reason' => Item::PR_ACTIVITY]);
- Logger::info('Public item stored for user', ['uri-id' => $toplevel_item['uri-id'], 'uid' => $post['uid'], 'stored' => $stored]);
+ DI::logger()->info('Public item stored for user', ['uri-id' => $toplevel_item['uri-id'], 'uid' => $post['uid'], 'stored' => $stored]);
}
- $post['parent'] = $toplevel_item['id'];
- $post['gravity'] = Item::GRAVITY_COMMENT;
- $post['thr-parent'] = $parent_item['uri'];
- $post['wall'] = $toplevel_item['wall'];
+ $post['parent'] = $toplevel_item['id'];
+ $post['gravity'] = Item::GRAVITY_COMMENT;
+ $post['thr-parent'] = $parent_item['uri'];
+ $post['wall'] = $toplevel_item['wall'];
} else {
- $parent_item = [];
- $post['parent'] = 0;
- $post['gravity'] = Item::GRAVITY_PARENT;
- $post['thr-parent'] = $post['uri'];
+ $parent_item = [];
+ $post['parent'] = 0;
+ $post['gravity'] = Item::GRAVITY_PARENT;
+ $post['thr-parent'] = $post['uri'];
}
$post = DI::contentItem()->getACL($post, $parent_item, $request);
@@ -198,7 +200,7 @@ function item_insert(int $uid, array $request, bool $preview, string $return_pat
$post = Post::selectFirst(Item::ITEM_FIELDLIST, ['id' => $post_id]);
if (!$post) {
- Logger::error('Item couldn\'t be fetched.', ['post_id' => $post_id]);
+ DI::logger()->error('Item couldn\'t be fetched.', ['post_id' => $post_id]);
if ($return_path) {
DI::baseUrl()->redirect($return_path);
}
@@ -214,7 +216,7 @@ function item_insert(int $uid, array $request, bool $preview, string $return_pat
DI::contentItem()->copyPermissions($post['thr-parent-id'], $post['uri-id'], $post['parent-uri-id']);
}
- Logger::debug('post_complete');
+ DI::logger()->debug('post_complete');
item_post_return(DI::baseUrl(), $return_path);
// NOTREACHED
@@ -277,14 +279,24 @@ function item_process(array $post, array $request, bool $preview, string $return
System::jsonExit(['preview' => $o]);
}
- Hook::callAll('post_local', $post);
+ $eventDispatcher = DI::eventDispatcher();
+
+ $hook_data = [
+ 'item' => $post,
+ ];
+
+ $hook_data = $eventDispatcher->dispatch(
+ new ArrayFilterEvent(ArrayFilterEvent::INSERT_POST_LOCAL, $hook_data)
+ )->getArray();
+
+ $post = $hook_data['item'] ?? $post;
unset($post['edit']);
unset($post['self']);
unset($post['api_source']);
if (!empty($request['scheduled_at'])) {
- $scheduled_at = DateTimeFormat::convert($request['scheduled_at'], 'UTC', DI::app()->getTimeZone());
+ $scheduled_at = DateTimeFormat::convert($request['scheduled_at'], 'UTC', DI::appHelper()->getTimeZone());
if ($scheduled_at > DateTimeFormat::utcNow()) {
unset($post['created']);
unset($post['edited']);
@@ -298,7 +310,7 @@ function item_process(array $post, array $request, bool $preview, string $return
}
if (!empty($post['cancel'])) {
- Logger::info('mod_item: post cancelled by addon.');
+ DI::logger()->info('mod_item: post cancelled by addon.');
if ($return_path) {
DI::baseUrl()->redirect($return_path);
}
@@ -325,12 +337,12 @@ function item_post_return($baseurl, $return_path)
$json['reload'] = $baseurl . '/' . $_REQUEST['jsreload'];
}
- Logger::debug('post_json', ['json' => $json]);
+ DI::logger()->debug('post_json', ['json' => $json]);
System::jsonExit($json);
}
-function item_content(App $a)
+function item_content()
{
if (!DI::userSession()->isAuthenticated()) {
throw new HTTPException\UnauthorizedException();
@@ -445,7 +457,7 @@ function drop_item(int $id, string $return = ''): string
item_redirect_after_action($item, $return);
//NOTREACHED
} else {
- Logger::warning('Permission denied.', ['local' => DI::userSession()->getLocalUserId(), 'uid' => $item['uid'], 'cid' => $contact_id]);
+ DI::logger()->warning('Permission denied.', ['local' => DI::userSession()->getLocalUserId(), 'uid' => $item['uid'], 'cid' => $contact_id]);
DI::sysmsg()->addNotice(DI::l10n()->t('Permission denied.'));
DI::baseUrl()->redirect('display/' . $item['guid']);
//NOTREACHED
diff --git a/mod/lostpass.php b/mod/lostpass.php
index b16a4e10ec..d62fd5f5aa 100644
--- a/mod/lostpass.php
+++ b/mod/lostpass.php
@@ -7,7 +7,6 @@
*
*/
-use Friendica\App;
use Friendica\Core\Renderer;
use Friendica\Database\DBA;
use Friendica\DI;
@@ -15,7 +14,7 @@ use Friendica\Model\User;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Strings;
-function lostpass_post(App $a)
+function lostpass_post()
{
$loginame = trim($_POST['login-name']);
if (!$loginame) {
@@ -78,7 +77,7 @@ function lostpass_post(App $a)
DI::baseUrl()->redirect();
}
-function lostpass_content(App $a)
+function lostpass_content()
{
if (DI::args()->getArgc() > 1) {
$pwdreset_token = DI::args()->getArgv()[1];
diff --git a/mod/message.php b/mod/message.php
index 6ee7a8745a..f3a231601c 100644
--- a/mod/message.php
+++ b/mod/message.php
@@ -1,4 +1,5 @@
'm',
];
- $tpl = Renderer::getMarkupTemplate('message_side.tpl');
+ $tpl = Renderer::getMarkupTemplate('message_side.tpl');
DI::page()['aside'] = Renderer::replaceMacros($tpl, [
'$tabs' => $tabs,
'$new' => $new,
@@ -61,7 +62,7 @@ function message_post()
$body = !empty($_REQUEST['body']) ? Strings::escapeHtml(trim($_REQUEST['body'])) : '';
$recipient = !empty($_REQUEST['recipient']) ? intval($_REQUEST['recipient']) : 0;
- $ret = Mail::send($sender_id, $recipient, $body, $subject, $replyto);
+ $ret = Mail::send($sender_id, $recipient, $body, $subject, $replyto);
$norecip = false;
switch ($ret) {
@@ -131,7 +132,7 @@ function message_content()
$cmd = DI::args()->getArgv()[1];
if ($cmd === 'drop') {
$message = DBA::selectFirst('mail', ['convid'], ['id' => DI::args()->getArgv()[2], 'uid' => DI::userSession()->getLocalUserId()]);
- if(!DBA::isResult($message)){
+ if (!DBA::isResult($message)) {
DI::sysmsg()->addNotice(DI::l10n()->t('Conversation not found.'));
DI::baseUrl()->redirect('message');
}
@@ -141,7 +142,7 @@ function message_content()
}
$conversation = DBA::selectFirst('mail', ['id'], ['convid' => $message['convid'], 'uid' => DI::userSession()->getLocalUserId()]);
- if(!DBA::isResult($conversation)){
+ if (!DBA::isResult($conversation)) {
DI::baseUrl()->redirect('message');
}
@@ -165,7 +166,7 @@ function message_content()
$tpl = Renderer::getMarkupTemplate('msg-header.tpl');
DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [
'$nickname' => DI::userSession()->getLocalUserNickname(),
- '$linkurl' => DI::l10n()->t('Please enter a link URL:')
+ '$linkurl' => DI::l10n()->t('Please enter a link URL:')
]);
$recipientId = DI::args()->getArgv()[2] ?? null;
@@ -178,7 +179,7 @@ function message_content()
'$to' => DI::l10n()->t('To:'),
'$subject' => DI::l10n()->t('Subject:'),
'$subjtxt' => $_REQUEST['subject'] ?? '',
- '$text' => $_REQUEST['body'] ?? '',
+ '$text' => $_REQUEST['body'] ?? '',
'$readonly' => '',
'$yourmessage' => DI::l10n()->t('Your message:'),
'$select' => $select,
@@ -207,7 +208,7 @@ function message_content()
$r = get_messages(DI::userSession()->getLocalUserId(), $pager->getStart(), $pager->getItemsPerPage());
if (!DBA::isResult($r)) {
- DI::sysmsg()->addNotice(DI::l10n()->t('No messages.'));
+ $o .= DI::l10n()->t('You have no messages.');
return $o;
}
@@ -222,7 +223,8 @@ function message_content()
$o .= $header;
- $message = DBA::fetchFirst("
+ $message = DBA::fetchFirst(
+ "
SELECT `mail`.*, `contact`.`name`, `contact`.`url`, `contact`.`thumb`
FROM `mail`
LEFT JOIN `contact` ON `mail`.`contact-id` = `contact`.`id`
@@ -241,11 +243,12 @@ function message_content()
if ($message['convid']) {
$sql_extra = "AND (`mail`.`parent-uri` = ? OR `mail`.`convid` = ?)";
- $params[] = $message['convid'];
+ $params[] = $message['convid'];
} else {
$sql_extra = "AND `mail`.`parent-uri` = ?";
}
- $messages_stmt = DBA::p("
+ $messages_stmt = DBA::p(
+ "
SELECT `mail`.*, `contact`.`name`, `contact`.`url`, `contact`.`thumb`
FROM `mail`
LEFT JOIN `contact` ON `mail`.`contact-id` = `contact`.`id`
@@ -270,11 +273,11 @@ function message_content()
$tpl = Renderer::getMarkupTemplate('msg-header.tpl');
DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [
'$nickname' => DI::userSession()->getLocalUserNickname(),
- '$linkurl' => DI::l10n()->t('Please enter a link URL:')
+ '$linkurl' => DI::l10n()->t('Please enter a link URL:')
]);
- $mails = [];
- $seen = 0;
+ $mails = [];
+ $seen = 0;
$unknown = false;
foreach ($messages as $message) {
@@ -284,18 +287,18 @@ function message_content()
if ($message['from-url'] == $myprofile) {
$from_url = $myprofile;
- $sparkle = '';
+ $sparkle = '';
} else {
$from_url = Contact::magicLink($message['from-url']);
- $sparkle = ' sparkle';
+ $sparkle = ' sparkle';
}
$from_name_e = $message['from-name'];
- $subject_e = $message['title'];
- $body_e = BBCode::convertForUriId($message['uri-id'], $message['body']);
- $to_name_e = $message['name'];
+ $subject_e = $message['title'];
+ $body_e = BBCode::convertForUriId($message['uri-id'], $message['body']);
+ $to_name_e = $message['name'];
- $contact = Contact::getByURL($message['from-url'], false, ['thumb', 'addr', 'id', 'avatar', 'url']);
+ $contact = Contact::getByURL($message['from-url'], false, ['thumb', 'addr', 'id', 'avatar', 'url']);
$from_photo = Contact::getThumb($contact);
$mails[] = [
@@ -320,7 +323,7 @@ function message_content()
$parent = '';
$tpl = Renderer::getMarkupTemplate('mail_display.tpl');
- $o = Renderer::replaceMacros($tpl, [
+ $o = Renderer::replaceMacros($tpl, [
'$thread_id' => DI::args()->getArgv()[1],
'$thread_subject' => $message['title'],
'$thread_seen' => $seen,
@@ -329,19 +332,19 @@ function message_content()
'$unknown_text' => DI::l10n()->t("No secure communications available. You may be able to respond from the sender's profile page."),
'$mails' => $mails,
// reply
- '$header' => DI::l10n()->t('Send Reply'),
- '$to' => DI::l10n()->t('To:'),
- '$subject' => DI::l10n()->t('Subject:'),
- '$subjtxt' => $message['title'],
- '$readonly' => ' readonly="readonly" style="background: #BBBBBB;" ',
- '$yourmessage' => DI::l10n()->t('Your message:'),
- '$text' => '',
- '$select' => $select,
- '$parent' => $parent,
- '$upload' => DI::l10n()->t('Upload photo'),
- '$insert' => DI::l10n()->t('Insert web link'),
- '$submit' => DI::l10n()->t('Submit'),
- '$wait' => DI::l10n()->t('Please wait')
+ '$header' => DI::l10n()->t('Send Reply'),
+ '$to' => DI::l10n()->t('To:'),
+ '$subject' => DI::l10n()->t('Subject:'),
+ '$subjtxt' => $message['title'],
+ '$readonly' => ' readonly="readonly" style="background: #BBBBBB;" ',
+ '$yourmessage' => DI::l10n()->t('Your message:'),
+ '$text' => '',
+ '$select' => $select,
+ '$parent' => $parent,
+ '$upload' => DI::l10n()->t('Upload photo'),
+ '$insert' => DI::l10n()->t('Insert web link'),
+ '$submit' => DI::l10n()->t('Submit'),
+ '$wait' => DI::l10n()->t('Please wait')
]);
return $o;
@@ -396,13 +399,12 @@ function get_messages(int $uid, int $start, int $limit): array
LEFT JOIN `contact` c ON m.`contact-id` = c.`id`
WHERE m.`uid` = ?
ORDER BY m2.`mailcreated` DESC
- LIMIT ?, ?'
- , $uid, $uid, $start, $limit));
+ LIMIT ?, ?', $uid, $uid, $start, $limit));
}
function render_messages(array $msg, string $t): string
{
- $tpl = Renderer::getMarkupTemplate($t);
+ $tpl = Renderer::getMarkupTemplate($t);
$rslt = '';
$myprofile = DI::baseUrl() . '/profile/' . DI::userSession()->getLocalUserNickname();
@@ -416,7 +418,7 @@ function render_messages(array $msg, string $t): string
$participants = DI::l10n()->t("%s and You", $rr['from-name']);
}
- $body_e = $rr['body'];
+ $body_e = $rr['body'];
$to_name_e = $rr['name'];
if (is_null($rr['url'])) {
@@ -424,7 +426,7 @@ function render_messages(array $msg, string $t): string
continue;
}
- $contact = Contact::getByURL($rr['url'], false, ['thumb', 'addr', 'id', 'avatar', 'url']);
+ $contact = Contact::getByURL($rr['url'], false, ['thumb', 'addr', 'id', 'avatar', 'url']);
$from_photo = Contact::getThumb($contact);
$rslt .= Renderer::replaceMacros($tpl, [
diff --git a/mod/notes.php b/mod/notes.php
index 27e7a9dc76..478e4c4aca 100644
--- a/mod/notes.php
+++ b/mod/notes.php
@@ -7,7 +7,6 @@
*
*/
-use Friendica\App;
use Friendica\Content\Conversation;
use Friendica\Content\Nav;
use Friendica\Content\Pager;
@@ -17,7 +16,7 @@ use Friendica\Model\Item;
use Friendica\Model\Post;
use Friendica\Module\BaseProfile;
-function notes_init(App $a)
+function notes_init()
{
if (! DI::userSession()->getLocalUserId()) {
return;
@@ -27,8 +26,10 @@ function notes_init(App $a)
}
-function notes_content(App $a, bool $update = false)
+function notes_content(bool $update = false)
{
+ $contactId = DI::appHelper()->getContactId();
+
if (!DI::userSession()->getLocalUserId()) {
DI::sysmsg()->addNotice(DI::l10n()->t('Permission denied.'));
return;
@@ -46,11 +47,11 @@ function notes_content(App $a, bool $update = false)
'acl_data' => '',
];
- $o .= DI::conversation()->statusEditor($x, $a->getContactId());
+ $o .= DI::conversation()->statusEditor($x, $contactId);
}
$condition = ['uid' => DI::userSession()->getLocalUserId(), 'post-type' => Item::PT_PERSONAL_NOTE, 'gravity' => Item::GRAVITY_PARENT,
- 'contact-id'=> $a->getContactId()];
+ 'contact-id'=> $contactId];
if (DI::mode()->isMobile()) {
$itemsPerPage = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'itemspage_mobile_network',
diff --git a/mod/photos.php b/mod/photos.php
index abd62989a2..57076fc933 100644
--- a/mod/photos.php
+++ b/mod/photos.php
@@ -11,14 +11,12 @@ use Friendica\Content\Nav;
use Friendica\Content\Pager;
use Friendica\Content\Text\BBCode;
use Friendica\Core\ACL;
-use Friendica\Core\Addon;
-use Friendica\Core\Hook;
-use Friendica\Core\Logger;
use Friendica\Core\Renderer;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\Database\DBStructure;
use Friendica\DI;
+use Friendica\Event\ArrayFilterEvent;
use Friendica\Model\Contact;
use Friendica\Model\Item;
use Friendica\Model\Photo;
@@ -28,9 +26,7 @@ use Friendica\Model\Tag;
use Friendica\Model\User;
use Friendica\Module\BaseProfile;
use Friendica\Network\HTTPException;
-use Friendica\Network\Probe;
use Friendica\Protocol\Activity;
-use Friendica\Protocol\ActivityNamespace;
use Friendica\Security\Security;
use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat;
@@ -49,7 +45,7 @@ function photos_init()
Nav::setSelected('home');
if (DI::args()->getArgc() > 1) {
- $owner = Profile::load(DI::app(), DI::args()->getArgv()[1], false);
+ $owner = Profile::load(DI::appHelper(), DI::args()->getArgv()[1], false);
if (!isset($owner['account_removed']) || $owner['account_removed']) {
throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.'));
}
@@ -117,8 +113,8 @@ function photos_post()
throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.'));
}
- $can_post = false;
- $visitor = 0;
+ $can_post = false;
+ $visitor = 0;
$page_owner_uid = intval($user['uid']);
$community_page = in_array($user['page-flags'], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_COMM_MAN]);
@@ -127,8 +123,8 @@ function photos_post()
$can_post = true;
} elseif ($community_page && !empty(DI::userSession()->getRemoteContactID($page_owner_uid))) {
$contact_id = DI::userSession()->getRemoteContactID($page_owner_uid);
- $can_post = true;
- $visitor = $contact_id;
+ $can_post = true;
+ $visitor = $contact_id;
}
if (!$can_post) {
@@ -144,7 +140,7 @@ function photos_post()
System::exit();
}
- $aclFormatter = DI::aclFormatter();
+ $aclFormatter = DI::aclFormatter();
$str_contact_allow = isset($_REQUEST['contact_allow']) ? $aclFormatter->toString($_REQUEST['contact_allow']) : $owner_record['allow_cid'] ?? '';
$str_circle_allow = isset($_REQUEST['circle_allow']) ? $aclFormatter->toString($_REQUEST['circle_allow']) : $owner_record['allow_gid'] ?? '';
$str_contact_deny = isset($_REQUEST['contact_deny']) ? $aclFormatter->toString($_REQUEST['contact_deny']) : $owner_record['deny_cid'] ?? '';
@@ -154,7 +150,7 @@ function photos_post()
if ($visibility === 'public') {
// The ACL selector introduced in version 2019.12 sends ACL input data even when the Public visibility is selected
$str_contact_allow = $str_circle_allow = $str_contact_deny = $str_circle_deny = '';
- } else if ($visibility === 'custom') {
+ } elseif ($visibility === 'custom') {
// Since we know from the visibility parameter the item should be private, we have to prevent the empty ACL
// case that would make it public. So we always append the author's contact id to the allowed contacts.
// See https://github.com/friendica/friendica/issues/9672
@@ -279,7 +275,7 @@ function photos_post()
}
if (!empty($_POST['rotate']) && (intval($_POST['rotate']) == 1 || intval($_POST['rotate']) == 2)) {
- Logger::debug('rotate');
+ DI::logger()->debug('rotate');
$photo = Photo::getPhotoForUser($page_owner_uid, $resource_id);
@@ -320,7 +316,7 @@ function photos_post()
if (DBA::isResult($photos)) {
$photo = $photos[0];
- $ext = Images::getExtensionByMimeType($photo['type']);
+ $ext = Images::getExtensionByMimeType($photo['type']);
Photo::update(
['desc' => $desc, 'album' => $albname, 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_circle_allow, 'deny_cid' => $str_contact_deny, 'deny_gid' => $str_circle_deny],
['resource-id' => $resource_id, 'uid' => $page_owner_uid]
@@ -335,9 +331,9 @@ function photos_post()
if (DBA::isResult($photos) && !$item_id) {
// Create item container
$title = '';
- $uri = Item::newURI();
+ $uri = Item::newURI();
- $arr = [];
+ $arr = [];
$arr['guid'] = System::createUUID();
$arr['uid'] = $page_owner_uid;
$arr['uri'] = $uri;
@@ -359,7 +355,7 @@ function photos_post()
$arr['visible'] = 0;
$arr['origin'] = 1;
- $arr['body'] = Images::getBBCodeByResource($photo['resource-id'], $user['nickname'], $photo['scale'], $ext);
+ $arr['body'] = Images::getBBCodeByResource($photo['resource-id'], $user['nickname'], $photo['scale'], $ext);
$item_id = Item::insert($arr);
}
@@ -373,7 +369,7 @@ function photos_post()
}
if (strlen($rawtags)) {
- $inform = '';
+ $inform = '';
// if the new tag doesn't have a namespace specifier (@foo or #foo) give it a hashtag
$x = substr($rawtags, 0, 1);
@@ -382,13 +378,13 @@ function photos_post()
}
$taginfo = [];
- $tags = BBCode::getTags($rawtags);
+ $tags = BBCode::getTags($rawtags);
if (count($tags)) {
foreach ($tags as $tag) {
if (strpos($tag, '@') === 0) {
$profile = '';
- $name = substr($tag, 1);
+ $name = substr($tag, 1);
$contact = Contact::getByURL($name);
if (empty($contact)) {
$newname = $name;
@@ -456,7 +452,7 @@ function photos_post()
}
$newinform .= $inform;
- $fields = ['inform' => $newinform, 'edited' => DateTimeFormat::utcNow(), 'changed' => DateTimeFormat::utcNow()];
+ $fields = ['inform' => $newinform, 'edited' => DateTimeFormat::utcNow(), 'changed' => DateTimeFormat::utcNow()];
$condition = ['id' => $item_id];
Item::update($fields, $condition);
@@ -555,7 +551,7 @@ function photos_content()
$datum = null;
if (DI::args()->getArgc() > 3) {
$datatype = DI::args()->getArgv()[2];
- $datum = DI::args()->getArgv()[3];
+ $datum = DI::args()->getArgv()[3];
} elseif ((DI::args()->getArgc() > 2) && (DI::args()->getArgv()[2] === 'upload')) {
$datatype = 'upload';
} else {
@@ -585,12 +581,12 @@ function photos_content()
$can_post = true;
} elseif ($community_page && !empty(DI::userSession()->getRemoteContactID($owner_uid))) {
$contact_id = DI::userSession()->getRemoteContactID($owner_uid);
- $contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => $owner_uid, 'blocked' => false, 'pending' => false]);
+ $contact = DBA::selectFirst('contact', [], ['id' => $contact_id, 'uid' => $owner_uid, 'blocked' => false, 'pending' => false]);
if (DBA::isResult($contact)) {
- $can_post = true;
+ $can_post = true;
$remote_contact = true;
- $visitor = $contact_id;
+ $visitor = $contact_id;
}
}
@@ -646,14 +642,18 @@ function photos_content()
$uploader = '';
$ret = [
- 'post_url' => 'profile/' . $user['nickname'] . '/photos',
- 'addon_text' => $uploader,
+ 'post_url' => 'profile/' . $user['nickname'] . '/photos',
+ 'addon_text' => $uploader,
'default_upload' => true
];
- Hook::callAll('photo_upload_form', $ret);
+ $eventDispatcher = DI::eventDispatcher();
- $default_upload_box = Renderer::replaceMacros(Renderer::getMarkupTemplate('photos_default_uploader_box.tpl'), []);
+ $eventDispatcher->dispatch(
+ new ArrayFilterEvent(ArrayFilterEvent::PHOTO_UPLOAD_FORM, $ret)
+ );
+
+ $default_upload_box = Renderer::replaceMacros(Renderer::getMarkupTemplate('photos_default_uploader_box.tpl'), []);
$default_upload_submit = Renderer::replaceMacros(Renderer::getMarkupTemplate('photos_default_uploader_submit.tpl'), [
'$submit' => DI::l10n()->t('Submit'),
]);
@@ -678,22 +678,22 @@ function photos_content()
$aclselect_e = ($visitor ? '' : ACL::getFullSelectorHTML(DI::page(), DI::userSession()->getLocalUserId()));
$o .= Renderer::replaceMacros($tpl, [
- '$pagename' => DI::l10n()->t('Upload Photos'),
- '$sessid' => session_id(),
- '$usage' => $usage_message,
- '$nickname' => $user['nickname'],
- '$newalbum' => DI::l10n()->t('New album name: '),
- '$existalbumtext' => DI::l10n()->t('or select existing album:'),
- '$nosharetext' => DI::l10n()->t('Do not show a status post for this upload'),
- '$albumselect' => $albumselect,
- '$selname' => $selname,
- '$permissions' => DI::l10n()->t('Permissions'),
- '$aclselect' => $aclselect_e,
- '$lockstate' => ACL::getLockstateForUserId(DI::userSession()->getLocalUserId()) ? 'lock' : 'unlock',
- '$alt_uploader' => $ret['addon_text'],
- '$default_upload_box' => ($ret['default_upload'] ? $default_upload_box : ''),
+ '$pagename' => DI::l10n()->t('Upload Photos'),
+ '$sessid' => session_id(),
+ '$usage' => $usage_message,
+ '$nickname' => $user['nickname'],
+ '$newalbum' => DI::l10n()->t('New album name: '),
+ '$existalbumtext' => DI::l10n()->t('or select existing album:'),
+ '$nosharetext' => DI::l10n()->t('Do not show a status post for this upload'),
+ '$albumselect' => $albumselect,
+ '$selname' => $selname,
+ '$permissions' => DI::l10n()->t('Permissions'),
+ '$aclselect' => $aclselect_e,
+ '$lockstate' => ACL::getLockstateForUserId(DI::userSession()->getLocalUserId()) ? 'lock' : 'unlock',
+ '$alt_uploader' => $ret['addon_text'],
+ '$default_upload_box' => ($ret['default_upload'] ? $default_upload_box : ''),
'$default_upload_submit' => ($ret['default_upload'] ? $default_upload_submit : ''),
- '$uploadurl' => $ret['post_url'],
+ '$uploadurl' => $ret['post_url'],
// ACL permissions box
'$return_path' => DI::args()->getQueryString(),
@@ -715,7 +715,7 @@ function photos_content()
}
$total = 0;
- $r = DBA::toArray(DBA::p(
+ $r = DBA::toArray(DBA::p(
"SELECT `resource-id`, MAX(`scale`) AS `scale` FROM `photo` WHERE `uid` = ? AND `album` = ?
AND `scale` <= 4 $sql_extra GROUP BY `resource-id`",
$owner_uid,
@@ -751,7 +751,7 @@ function photos_content()
$drop_url = DI::args()->getQueryString();
return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [
- '$l10n' => [
+ '$l10n' => [
'message' => DI::l10n()->t('Do you really want to delete this photo album and all its photos?'),
'confirm' => DI::l10n()->t('Delete Album'),
'cancel' => DI::l10n()->t('Cancel'),
@@ -771,11 +771,11 @@ function photos_content()
$album_e = $album;
$o .= Renderer::replaceMacros($edit_tpl, [
- '$nametext' => DI::l10n()->t('New album name: '),
- '$nickname' => $user['nickname'],
- '$album' => $album_e,
- '$hexalbum' => bin2hex($album),
- '$submit' => DI::l10n()->t('Submit'),
+ '$nametext' => DI::l10n()->t('New album name: '),
+ '$nickname' => $user['nickname'],
+ '$album' => $album_e,
+ '$hexalbum' => bin2hex($album),
+ '$submit' => DI::l10n()->t('Submit'),
'$dropsubmit' => DI::l10n()->t('Delete Album')
]);
}
@@ -785,7 +785,7 @@ function photos_content()
}
if ($order_field === 'created') {
- $order = [DI::l10n()->t('Show Newest First'), 'photos/' . $user['nickname'] . '/album/' . bin2hex($album), 'oldest'];
+ $order = [DI::l10n()->t('Show Newest First'), 'photos/' . $user['nickname'] . '/album/' . bin2hex($album), 'oldest'];
} else {
$order = [DI::l10n()->t('Show Oldest First'), 'photos/' . $user['nickname'] . '/album/' . bin2hex($album) . '?order=created', 'newest'];
}
@@ -801,7 +801,7 @@ function photos_content()
$ext = Images::getExtensionByMimeType($rr['type']);
$imgalt_e = $rr['filename'];
- $desc_e = $rr['desc'];
+ $desc_e = $rr['desc'];
$photos[] = [
'id' => $rr['id'],
@@ -820,13 +820,13 @@ function photos_content()
$tpl = Renderer::getMarkupTemplate('photo_album.tpl');
$o .= Renderer::replaceMacros($tpl, [
- '$photos' => $photos,
- '$album' => $album,
+ '$photos' => $photos,
+ '$album' => $album,
'$can_post' => $can_post,
- '$upload' => [DI::l10n()->t('Upload New Photos'), 'photos/' . $user['nickname'] . '/upload/' . bin2hex($album)],
- '$order' => $order,
- '$edit' => $edit,
- '$drop' => $drop,
+ '$upload' => [DI::l10n()->t('Upload New Photos'), 'photos/' . $user['nickname'] . '/upload/' . bin2hex($album)],
+ '$order' => $order,
+ '$edit' => $edit,
+ '$drop' => $drop,
'$paginate' => $pager->renderFull($total),
]);
@@ -851,7 +851,7 @@ function photos_content()
$drop_url = DI::args()->getQueryString();
return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [
- '$l10n' => [
+ '$l10n' => [
'message' => DI::l10n()->t('Do you really want to delete this photo?'),
'confirm' => DI::l10n()->t('Delete Photo'),
'cancel' => DI::l10n()->t('Cancel'),
@@ -953,8 +953,8 @@ function photos_content()
if ($cmd === 'edit') {
$tools['view'] = ['photos/' . $user['nickname'] . '/image/' . $datum, DI::l10n()->t('View photo')];
} else {
- $tools['edit'] = ['photos/' . $user['nickname'] . '/image/' . $datum . '/edit', DI::l10n()->t('Edit photo')];
- $tools['delete'] = ['photos/' . $user['nickname'] . '/image/' . $datum . '/drop', DI::l10n()->t('Delete photo')];
+ $tools['edit'] = ['photos/' . $user['nickname'] . '/image/' . $datum . '/edit', DI::l10n()->t('Edit photo')];
+ $tools['delete'] = ['photos/' . $user['nickname'] . '/image/' . $datum . '/drop', DI::l10n()->t('Delete photo')];
$tools['profile'] = ['settings/profile/photo/crop/' . $ph[0]['resource-id'], DI::l10n()->t('Use as profile photo')];
}
@@ -976,9 +976,9 @@ function photos_content()
'filename' => $hires['filename'],
];
- $map = null;
+ $map = null;
$link_item = [];
- $total = 0;
+ $total = 0;
// Do we have an item for this photo?
@@ -992,12 +992,12 @@ function photos_content()
if (!empty($link_item['parent']) && !empty($link_item['uid'])) {
$condition = ["`parent` = ? AND `gravity` = ?", $link_item['parent'], Item::GRAVITY_COMMENT];
- $total = Post::count($condition);
+ $total = Post::count($condition);
$pager = new Pager(DI::l10n(), DI::args()->getQueryString());
$params = ['order' => ['id'], 'limit' => [$pager->getStart(), $pager->getItemsPerPage()]];
- $items = Post::toArray(Post::selectForUser($link_item['uid'], array_merge(Item::ITEM_FIELDLIST, ['author-alias']), $condition, $params));
+ $items = Post::toArray(Post::selectForUser($link_item['uid'], array_merge(Item::ITEM_FIELDLIST, ['author-alias']), $condition, $params));
if (DI::userSession()->getLocalUserId() == $link_item['uid']) {
Item::update(['unseen' => false], ['parent' => $link_item['parent']]);
@@ -1022,54 +1022,55 @@ function photos_content()
}
}
$tags = ['title' => DI::l10n()->t('Tags: '), 'tags' => $tag_arr];
- if ($cmd === 'edit') {
+ if ($cmd === 'edit' && !empty($tag_arr)) {
$tags['removeanyurl'] = 'post/' . $link_item['id'] . '/tag/remove?return=' . urlencode(DI::args()->getCommand());
- $tags['removetitle'] = DI::l10n()->t('[Select tags to remove]');
+ $tags['removetitle'] = DI::l10n()->t('[Select tags to remove]');
}
}
- $edit = Null;
+ $edit = null;
if ($cmd === 'edit' && $can_post) {
$edit_tpl = Renderer::getMarkupTemplate('photo_edit.tpl');
- $album_e = $ph[0]['album'];
- $caption_e = $ph[0]['desc'];
+ $album_e = $ph[0]['album'];
+ $caption_e = $ph[0]['desc'];
$aclselect_e = ACL::getFullSelectorHTML(DI::page(), DI::userSession()->getLocalUserId(), false, ACL::getDefaultUserPermissions($ph[0]));
$edit = Renderer::replaceMacros($edit_tpl, [
- '$id' => $ph[0]['id'],
- '$album' => ['albname', DI::l10n()->t('New album name'), $album_e, ''],
- '$caption' => ['desc', DI::l10n()->t('Caption'), $caption_e, ''],
- '$tags' => ['newtag', DI::l10n()->t('Add a Tag'), "", DI::l10n()->t('Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping')],
+ '$id' => $ph[0]['id'],
+ '$album' => ['albname', DI::l10n()->t('New album name'), $album_e, ''],
+ '$caption' => ['desc', DI::l10n()->t('Caption'), $caption_e, ''],
+ '$tags' => ['newtag', DI::l10n()->t('Add a Tag'), "", DI::l10n()->t('Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping')],
'$rotate_none' => ['rotate', DI::l10n()->t('Do not rotate'), 0, '', true],
- '$rotate_cw' => ['rotate', DI::l10n()->t("Rotate CW \x28right\x29"), 1, ''],
- '$rotate_ccw' => ['rotate', DI::l10n()->t("Rotate CCW \x28left\x29"), 2, ''],
+ '$rotate_cw' => ['rotate', DI::l10n()->t("Rotate CW \x28right\x29"), 1, ''],
+ '$rotate_ccw' => ['rotate', DI::l10n()->t("Rotate CCW \x28left\x29"), 2, ''],
- '$nickname' => $user['nickname'],
+ '$nickname' => $user['nickname'],
'$resource_id' => $ph[0]['resource-id'],
'$permissions' => DI::l10n()->t('Permissions'),
- '$aclselect' => $aclselect_e,
+ '$aclselect' => $aclselect_e,
'$item_id' => $link_item['id'] ?? 0,
- '$submit' => DI::l10n()->t('Submit'),
- '$delete' => DI::l10n()->t('Delete Photo'),
+ '$submit' => DI::l10n()->t('Submit'),
+ '$delete' => DI::l10n()->t('Delete Photo'),
// ACL permissions box
'$return_path' => DI::args()->getQueryString(),
]);
}
- $like = '';
- $dislike = '';
+ $like = '';
+ $dislike = '';
$likebuttons = '';
- $comments = '';
- $paginate = '';
+ $comments = '';
+ $paginate = '';
if (!empty($link_item['id']) && !empty($link_item['uri'])) {
- $cmnt_tpl = Renderer::getMarkupTemplate('comment_item.tpl');
- $tpl = Renderer::getMarkupTemplate('photo_item.tpl');
+ $cmnt_tpl = Renderer::getMarkupTemplate('comment_item.tpl');
+ $tpl = Renderer::getMarkupTemplate('photo_item.tpl');
$return_path = DI::args()->getCommand();
+ $addonHelper = DI::addonHelper();
if (!DBA::isResult($items)) {
if (($can_post || Security::canWriteToUserWall($owner_uid))) {
@@ -1078,26 +1079,26 @@ function photos_content()
* This should be better if done by a hook
*/
$qcomment = null;
- if (Addon::isEnabled('qcomment')) {
- $words = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'qcomment', 'words');
+ if ($addonHelper->isAddonEnabled('qcomment')) {
+ $words = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'qcomment', 'words');
$qcomment = $words ? explode("\n", $words) : [];
}
$comments .= Renderer::replaceMacros($cmnt_tpl, [
'$return_path' => '',
- '$jsreload' => $return_path,
- '$id' => $link_item['id'],
- '$parent' => $link_item['id'],
- '$profile_uid' => $owner_uid,
- '$mylink' => $contact['url'],
- '$mytitle' => DI::l10n()->t('This is you'),
- '$myphoto' => $contact['thumb'],
- '$comment' => DI::l10n()->t('Comment'),
- '$submit' => DI::l10n()->t('Submit'),
- '$preview' => DI::l10n()->t('Preview'),
- '$loading' => DI::l10n()->t('Loading...'),
- '$qcomment' => $qcomment,
- '$rand_num' => Crypto::randomDigits(12),
+ '$jsreload' => $return_path,
+ '$id' => $link_item['id'],
+ '$parent' => $link_item['id'],
+ '$profile_uid' => $owner_uid,
+ '$mylink' => $contact['url'],
+ '$mytitle' => DI::l10n()->t('This is you'),
+ '$myphoto' => $contact['thumb'],
+ '$comment' => DI::l10n()->t('Comment'),
+ '$submit' => DI::l10n()->t('Submit'),
+ '$preview' => DI::l10n()->t('Preview'),
+ '$loading' => DI::l10n()->t('Loading...'),
+ '$qcomment' => $qcomment,
+ '$rand_num' => Crypto::randomDigits(12),
]);
}
}
@@ -1134,30 +1135,30 @@ function photos_content()
* This should be better if done by a hook
*/
$qcomment = null;
- if (Addon::isEnabled('qcomment')) {
- $words = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'qcomment', 'words');
+ if ($addonHelper->isAddonEnabled('qcomment')) {
+ $words = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'qcomment', 'words');
$qcomment = $words ? explode("\n", $words) : [];
}
$comments .= Renderer::replaceMacros($cmnt_tpl, [
'$return_path' => '',
- '$jsreload' => $return_path,
- '$id' => $link_item['id'],
- '$parent' => $link_item['id'],
- '$profile_uid' => $owner_uid,
- '$mylink' => $contact['url'],
- '$mytitle' => DI::l10n()->t('This is you'),
- '$myphoto' => $contact['thumb'],
- '$comment' => DI::l10n()->t('Comment'),
- '$submit' => DI::l10n()->t('Submit'),
- '$preview' => DI::l10n()->t('Preview'),
- '$qcomment' => $qcomment,
- '$rand_num' => Crypto::randomDigits(12),
+ '$jsreload' => $return_path,
+ '$id' => $link_item['id'],
+ '$parent' => $link_item['id'],
+ '$profile_uid' => $owner_uid,
+ '$mylink' => $contact['url'],
+ '$mytitle' => DI::l10n()->t('This is you'),
+ '$myphoto' => $contact['thumb'],
+ '$comment' => DI::l10n()->t('Comment'),
+ '$submit' => DI::l10n()->t('Submit'),
+ '$preview' => DI::l10n()->t('Preview'),
+ '$qcomment' => $qcomment,
+ '$rand_num' => Crypto::randomDigits(12),
]);
}
foreach ($items as $item) {
- $comment = '';
+ $comment = '';
$template = $tpl;
$activity = DI::activity();
@@ -1184,7 +1185,7 @@ function photos_content()
}
$dropping = (($item['contact-id'] == $contact_id) || ($item['uid'] == DI::userSession()->getLocalUserId()));
- $drop = [
+ $drop = [
'dropping' => $dropping,
'pagedrop' => false,
'select' => DI::l10n()->t('Select'),
@@ -1192,7 +1193,7 @@ function photos_content()
];
$title_e = $item['title'];
- $body_e = BBCode::convertForUriId($item['uri-id'], $item['body']);
+ $body_e = BBCode::convertForUriId($item['uri-id'], $item['body']);
$comments .= Renderer::replaceMacros($template, [
'$id' => $item['id'],
@@ -1214,25 +1215,25 @@ function photos_content()
* This should be better if done by a hook
*/
$qcomment = null;
- if (Addon::isEnabled('qcomment')) {
- $words = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'qcomment', 'words');
+ if ($addonHelper->isAddonEnabled('qcomment')) {
+ $words = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'qcomment', 'words');
$qcomment = $words ? explode("\n", $words) : [];
}
$comments .= Renderer::replaceMacros($cmnt_tpl, [
'$return_path' => '',
- '$jsreload' => $return_path,
- '$id' => $item['id'],
- '$parent' => $item['parent'],
- '$profile_uid' => $owner_uid,
- '$mylink' => $contact['url'],
- '$mytitle' => DI::l10n()->t('This is you'),
- '$myphoto' => $contact['thumb'],
- '$comment' => DI::l10n()->t('Comment'),
- '$submit' => DI::l10n()->t('Submit'),
- '$preview' => DI::l10n()->t('Preview'),
- '$qcomment' => $qcomment,
- '$rand_num' => Crypto::randomDigits(12),
+ '$jsreload' => $return_path,
+ '$id' => $item['id'],
+ '$parent' => $item['parent'],
+ '$profile_uid' => $owner_uid,
+ '$mylink' => $contact['url'],
+ '$mytitle' => DI::l10n()->t('This is you'),
+ '$myphoto' => $contact['thumb'],
+ '$comment' => DI::l10n()->t('Comment'),
+ '$submit' => DI::l10n()->t('Submit'),
+ '$preview' => DI::l10n()->t('Preview'),
+ '$qcomment' => $qcomment,
+ '$rand_num' => Crypto::randomDigits(12),
]);
}
}
@@ -1246,17 +1247,17 @@ function photos_content()
}
if ($cmd === 'view' && ($can_post || Security::canWriteToUserWall($owner_uid))) {
- $like_tpl = Renderer::getMarkupTemplate('like_noshare.tpl');
+ $like_tpl = Renderer::getMarkupTemplate('like_noshare.tpl');
$likebuttons = Renderer::replaceMacros($like_tpl, [
- '$id' => $link_item['id'],
- '$like' => DI::l10n()->t('Like'),
- '$like_title' => DI::l10n()->t('I like this (toggle)'),
- '$dislike' => DI::l10n()->t('Dislike'),
- '$wait' => DI::l10n()->t('Please wait'),
+ '$id' => $link_item['id'],
+ '$like' => DI::l10n()->t('Like'),
+ '$like_title' => DI::l10n()->t('I like this (toggle)'),
+ '$dislike' => DI::l10n()->t('Dislike'),
+ '$wait' => DI::l10n()->t('Please wait'),
'$dislike_title' => DI::l10n()->t('I don\'t like this (toggle)'),
- '$hide_dislike' => DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'hide_dislike'),
- '$responses' => $responses,
- '$return_path' => DI::args()->getQueryString(),
+ '$hide_dislike' => DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'hide_dislike'),
+ '$responses' => $responses,
+ '$return_path' => DI::args()->getQueryString(),
]);
}
@@ -1265,22 +1266,22 @@ function photos_content()
$photo_tpl = Renderer::getMarkupTemplate('photo_view.tpl');
$o .= Renderer::replaceMacros($photo_tpl, [
- '$id' => $ph[0]['id'],
- '$album' => [$album_link, $ph[0]['album']],
- '$tools' => $tools,
- '$photo' => $photo,
- '$prevlink' => $prevlink,
- '$nextlink' => $nextlink,
- '$desc' => $ph[0]['desc'],
- '$tags' => $tags,
- '$edit' => $edit,
- '$map' => $map,
- '$map_text' => DI::l10n()->t('Map'),
+ '$id' => $ph[0]['id'],
+ '$album' => [$album_link, $ph[0]['album']],
+ '$tools' => $tools,
+ '$photo' => $photo,
+ '$prevlink' => $prevlink,
+ '$nextlink' => $nextlink,
+ '$desc' => $ph[0]['desc'],
+ '$tags' => $tags,
+ '$edit' => $edit,
+ '$map' => $map,
+ '$map_text' => DI::l10n()->t('Map'),
'$likebuttons' => $likebuttons,
- '$like' => $like,
- '$dislike' => $dislike,
- '$comments' => $comments,
- '$paginate' => $paginate,
+ '$like' => $like,
+ '$dislike' => $dislike,
+ '$comments' => $comments,
+ '$paginate' => $paginate,
]);
DI::page()['htmlhead'] .= "\n" . '' . "\n";
diff --git a/mod/update_contact.php b/mod/update_contact.php
index 76993c0ce3..adfbc513d7 100644
--- a/mod/update_contact.php
+++ b/mod/update_contact.php
@@ -9,14 +9,13 @@
*
*/
-use Friendica\App;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Post;
use Friendica\Model\Contact;
-function update_contact_content(App $a)
+function update_contact_content()
{
if (!empty(DI::args()->get(1)) && !empty($_GET['force'])) {
$contact = DBA::selectFirst('account-user-view', ['pid', 'deleted'], ['id' => DI::args()->get(1)]);
diff --git a/mod/update_notes.php b/mod/update_notes.php
index de455f5f00..06bbac6930 100644
--- a/mod/update_notes.php
+++ b/mod/update_notes.php
@@ -8,13 +8,11 @@
* AJAX synchronisation of notes page
*/
-use Friendica\App;
use Friendica\Core\System;
-use Friendica\DI;
require_once 'mod/notes.php';
-function update_notes_content(App $a)
+function update_notes_content()
{
$profile_uid = intval($_GET['p']);
@@ -28,7 +26,7 @@ function update_notes_content(App $a)
*
*/
- $text = notes_content($a, $profile_uid);
+ $text = notes_content($profile_uid);
System::htmlUpdateExit($text);
}
diff --git a/mods/sample-systemd.service b/mods/sample-systemd.service
index aae2e5825c..ac37359989 100644
--- a/mods/sample-systemd.service
+++ b/mods/sample-systemd.service
@@ -6,4 +6,4 @@ Description=Friendica Worker
User=http
#Adapt the path in the following line to your system, use 'which php' to find php path,
#provide the absolute path for worker.php
-ExecStart=/usr/bin/php /www/path/bin/worker.php &
+ExecStart=/usr/bin/php /www/path/bin/console.php worker &
diff --git a/src/App.php b/src/App.php
index 8813dc52a0..ffcbfd1544 100644
--- a/src/App.php
+++ b/src/App.php
@@ -7,34 +7,50 @@
namespace Friendica;
-use Exception;
+use Dice\Dice;
use Friendica\App\Arguments;
use Friendica\App\BaseURL;
+use Friendica\App\Mode;
+use Friendica\App\Page;
use Friendica\App\Request;
+use Friendica\App\Router;
use Friendica\Capabilities\ICanCreateResponses;
+use Friendica\Capabilities\ICanHandleRequests;
use Friendica\Content\Nav;
+use Friendica\Core\Addon\AddonHelper;
+use Friendica\Core\Addon\Capability\ICanLoadAddons;
use Friendica\Core\Config\Factory\Config;
+use Friendica\Core\Container;
+use Friendica\Core\Hooks\HookEventBridge;
+use Friendica\Core\Logger\LoggerManager;
+use Friendica\Core\Renderer;
use Friendica\Core\Session\Capability\IHandleUserSessions;
+use Friendica\Core\Config\Capability\IManageConfigValues;
+use Friendica\Core\DiceContainer;
+use Friendica\Core\L10n;
+use Friendica\Core\Logger\Capability\LogChannel;
+use Friendica\Core\Logger\Handler\ErrorHandler;
+use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
+use Friendica\Core\System;
+use Friendica\Core\Update;
use Friendica\Database\Definition\DbaDefinition;
use Friendica\Database\Definition\ViewDefinition;
+use Friendica\Event\ConfigLoadedEvent;
+use Friendica\Event\Event;
use Friendica\Module\Maintenance;
-use Friendica\Security\Authentication;
-use Friendica\Core\Config\ValueObject\Cache;
-use Friendica\Core\Config\Capability\IManageConfigValues;
-use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
-use Friendica\Core\L10n;
-use Friendica\Core\System;
-use Friendica\Core\Theme;
-use Friendica\Database\Database;
use Friendica\Module\Special\HTTPException as ModuleHTTPException;
use Friendica\Network\HTTPException;
use Friendica\Protocol\ATProtocol\DID;
+use Friendica\Security\Authentication;
+use Friendica\Security\ExAuth;
use Friendica\Security\OpenWebAuth;
+use Friendica\Util\BasePath;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\HTTPInputData;
use Friendica\Util\HTTPSignature;
use Friendica\Util\Profiler;
-use Friendica\Util\Strings;
+use Psr\EventDispatcher\EventDispatcherInterface;
+use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;
/**
@@ -46,27 +62,29 @@ use Psr\Log\LoggerInterface;
* and anything else that might need to be passed around
* before we spit the page out.
*
+ * @final
*/
class App
{
const PLATFORM = 'Friendica';
const CODENAME = 'Interrupted Fern';
- const VERSION = '2024.12';
-
- // Allow themes to control internal parameters
- // by changing App values in theme.php
- private $theme_info = [
- 'videowidth' => 425,
- 'videoheight' => 350,
- ];
-
- private $timezone = '';
- private $profile_owner = 0;
- private $contact_id = 0;
- private $queue = [];
+ const VERSION = '2025.02-dev';
/**
- * @var App\Mode The Mode of the Application
+ * @internal
+ */
+ public static function fromContainer(Container $container): self
+ {
+ return new self($container);
+ }
+
+ /**
+ * @var Container
+ */
+ private $container;
+
+ /**
+ * @var Mode The Mode of the Application
*/
private $mode;
@@ -75,11 +93,6 @@ class App
*/
private $baseURL;
- /** @var string The name of the current theme */
- private $currentTheme;
- /** @var string The name of the current mobile theme */
- private $currentMobileTheme;
-
/** @var string */
private $requestId;
@@ -101,11 +114,6 @@ class App
*/
private $profiler;
- /**
- * @var Database The Friendica database connection
- */
- private $database;
-
/**
* @var L10n The translator
*/
@@ -116,190 +124,239 @@ class App
*/
private $args;
- /**
- * @var IManagePersonalConfigValues
- */
- private $pConfig;
-
/**
* @var IHandleUserSessions
*/
private $session;
/**
- * Set the profile owner ID
- *
- * @param int $owner_id
- * @return void
+ * @var AppHelper $appHelper
*/
- public function setProfileOwner(int $owner_id)
+ private $appHelper;
+
+ private function __construct(Container $container)
{
- $this->profile_owner = $owner_id;
+ $this->container = $container;
}
/**
- * Get the profile owner ID
- *
- * @return int
+ * @internal
*/
- public function getProfileOwner(): int
+ public function processRequest(ServerRequestInterface $request, float $start_time): void
{
- return $this->profile_owner;
+ $this->container->addRule(Mode::class, [
+ 'call' => [
+ ['determineRunMode', [false, $request->getServerParams()], Dice::CHAIN_CALL],
+ ],
+ ]);
+
+ $this->setupContainerForAddons();
+
+ $this->setupLogChannel(LogChannel::APP);
+
+ $this->setupLegacyServiceLocator();
+
+ $this->registerErrorHandler();
+
+ $this->registerEventDispatcher();
+
+ $this->requestId = $this->container->create(Request::class)->getRequestId();
+ $this->auth = $this->container->create(Authentication::class);
+ $this->config = $this->container->create(IManageConfigValues::class);
+ $this->mode = $this->container->create(Mode::class);
+ $this->baseURL = $this->container->create(BaseURL::class);
+ $this->logger = $this->container->create(LoggerInterface::class);
+ $this->profiler = $this->container->create(Profiler::class);
+ $this->l10n = $this->container->create(L10n::class);
+ $this->args = $this->container->create(Arguments::class);
+ $this->session = $this->container->create(IHandleUserSessions::class);
+ $this->appHelper = $this->container->create(AppHelper::class);
+
+ $addonHelper = $this->container->create(AddonHelper::class);
+
+ $this->load(
+ $request->getServerParams(),
+ $this->container->create(DbaDefinition::class),
+ $this->container->create(ViewDefinition::class),
+ $this->mode,
+ $this->config,
+ $this->profiler,
+ $this->container->create(EventDispatcherInterface::class),
+ $this->appHelper,
+ $addonHelper,
+ );
+
+ $this->registerTemplateEngine();
+
+ $this->runFrontend(
+ $this->container->create(EventDispatcherInterface::class),
+ $this->container->create(IManagePersonalConfigValues::class),
+ $this->container->create(Page::class),
+ $this->container->create(Nav::class),
+ $addonHelper,
+ $this->container->create(ModuleHTTPException::class),
+ $start_time,
+ $request
+ );
}
/**
- * Set the contact ID
- *
- * @param int $contact_id
- * @return void
+ * @internal
*/
- public function setContactId(int $contact_id)
+ public function processConsole(array $serverParams): void
{
- $this->contact_id = $contact_id;
+ $argv = $serverParams['argv'] ?? [];
+
+ $this->setupContainerForAddons();
+
+ $this->setupLogChannel($this->determineLogChannel($argv));
+
+ $this->setupLegacyServiceLocator();
+
+ $this->registerErrorHandler();
+
+ $this->registerEventDispatcher();
+
+ $this->load(
+ $serverParams,
+ $this->container->create(DbaDefinition::class),
+ $this->container->create(ViewDefinition::class),
+ $this->container->create(Mode::class),
+ $this->container->create(IManageConfigValues::class),
+ $this->container->create(Profiler::class),
+ $this->container->create(EventDispatcherInterface::class),
+ $this->container->create(AppHelper::class),
+ $this->container->create(AddonHelper::class),
+ );
+
+ $this->registerTemplateEngine();
+
+ (\Friendica\Core\Console::create($this->container, $argv))->execute();
}
/**
- * Get the contact ID
- *
- * @return int
+ * @internal
*/
- public function getContactId(): int
+ public function processEjabberd(array $serverParams): void
{
- return $this->contact_id;
+ $this->setupContainerForAddons();
+
+ $this->setupLogChannel(LogChannel::AUTH_JABBERED);
+
+ $this->setupLegacyServiceLocator();
+
+ $this->registerErrorHandler();
+
+ $this->registerEventDispatcher();
+
+ $this->load(
+ $serverParams,
+ $this->container->create(DbaDefinition::class),
+ $this->container->create(ViewDefinition::class),
+ $this->container->create(Mode::class),
+ $this->container->create(IManageConfigValues::class),
+ $this->container->create(Profiler::class),
+ $this->container->create(EventDispatcherInterface::class),
+ $this->container->create(AppHelper::class),
+ $this->container->create(AddonHelper::class),
+ );
+
+ /** @var BasePath */
+ $basePath = $this->container->create(BasePath::class);
+
+ // Check the database structure and possibly fixes it
+ Update::check($basePath->getPath(), true);
+
+ $appMode = $this->container->create(Mode::class);
+
+ if ($appMode->isNormal()) {
+ /** @var ExAuth $oAuth */
+ $oAuth = $this->container->create(ExAuth::class);
+ $oAuth->readStdin();
+ }
}
- /**
- * Set the timezone
- *
- * @param string $timezone A valid time zone identifier, see https://www.php.net/manual/en/timezones.php
- * @return void
- */
- public function setTimeZone(string $timezone)
+ private function setupContainerForAddons(): void
{
- $this->timezone = (new \DateTimeZone($timezone))->getName();
- DateTimeFormat::setLocalTimeZone($this->timezone);
+ /** @var ICanLoadAddons $addonLoader */
+ $addonLoader = $this->container->create(ICanLoadAddons::class);
+
+ foreach ($addonLoader->getActiveAddonConfig('dependencies') as $name => $rule) {
+ $this->container->addRule($name, $rule);
+ }
}
- /**
- * Get the timezone
- *
- * @return int
- */
- public function getTimeZone(): string
+ private function determineLogChannel(array $argv): string
{
- return $this->timezone;
+ $command = strtolower($argv[1] ?? '');
+
+ if ($command === 'daemon' || $command === 'jetstream') {
+ return LogChannel::DAEMON;
+ }
+
+ if ($command === 'worker') {
+ return LogChannel::WORKER;
+ }
+
+ // @TODO Add support for jetstream
+
+ return LogChannel::CONSOLE;
}
- /**
- * Set workerqueue information
- *
- * @param array $queue
- * @return void
- */
- public function setQueue(array $queue)
+ private function setupLogChannel(string $logChannel): void
{
- $this->queue = $queue;
+ /** @var LoggerManager */
+ $loggerManager = $this->container->create(LoggerManager::class);
+ $loggerManager->changeLogChannel($logChannel);
}
- /**
- * Fetch workerqueue information
- *
- * @return array Worker queue
- */
- public function getQueue(): array
+ private function setupLegacyServiceLocator(): void
{
- return $this->queue ?? [];
+ if ($this->container instanceof DiceContainer) {
+ DI::init($this->container->getDice());
+ }
}
- /**
- * Fetch a specific workerqueue field
- *
- * @param string $index Work queue record to fetch
- * @return mixed Work queue item or NULL if not found
- */
- public function getQueueValue(string $index)
+ private function registerErrorHandler(): void
{
- return $this->queue[$index] ?? null;
+ ErrorHandler::register($this->container->create(LoggerInterface::class));
}
- public function setThemeInfoValue(string $index, $value)
+ private function registerEventDispatcher(): void
{
- $this->theme_info[$index] = $value;
+ /** @var \Friendica\Event\EventDispatcher */
+ $eventDispatcher = $this->container->create(EventDispatcherInterface::class);
+
+ foreach (HookEventBridge::getStaticSubscribedEvents() as $eventName => $methodName) {
+ $eventDispatcher->addListener($eventName, [HookEventBridge::class, $methodName]);
+ }
}
- public function getThemeInfo()
+ private function registerTemplateEngine(): void
{
- return $this->theme_info;
- }
-
- public function getThemeInfoValue(string $index, $default = null)
- {
- return $this->theme_info[$index] ?? $default;
- }
-
- /**
- * Returns the current config cache of this node
- *
- * @return Cache
- */
- public function getConfigCache()
- {
- return $this->config->getCache();
- }
-
- /**
- * The basepath of this app
- *
- * @return string Base path from configuration
- */
- public function getBasePath(): string
- {
- return $this->config->get('system', 'basepath');
- }
-
- /**
- * @param Database $database The Friendica Database
- * @param IManageConfigValues $config The Configuration
- * @param App\Mode $mode The mode of this Friendica app
- * @param BaseURL $baseURL The full base URL of this Friendica app
- * @param LoggerInterface $logger The current app logger
- * @param Profiler $profiler The profiler of this application
- * @param L10n $l10n The translator instance
- * @param App\Arguments $args The Friendica Arguments of the call
- * @param IManagePersonalConfigValues $pConfig Personal configuration
- * @param IHandleUserSessions $session The (User)Session handler
- * @param DbaDefinition $dbaDefinition
- * @param ViewDefinition $viewDefinition
- */
- public function __construct(Request $request, Authentication $auth, Database $database, IManageConfigValues $config, App\Mode $mode, BaseURL $baseURL, LoggerInterface $logger, Profiler $profiler, L10n $l10n, Arguments $args, IManagePersonalConfigValues $pConfig, IHandleUserSessions $session, DbaDefinition $dbaDefinition, ViewDefinition $viewDefinition)
- {
- $this->requestId = $request->getRequestId();
- $this->auth = $auth;
- $this->database = $database;
- $this->config = $config;
- $this->mode = $mode;
- $this->baseURL = $baseURL;
- $this->profiler = $profiler;
- $this->logger = $logger;
- $this->l10n = $l10n;
- $this->args = $args;
- $this->pConfig = $pConfig;
- $this->session = $session;
-
- $this->load($dbaDefinition, $viewDefinition);
+ Renderer::registerTemplateEngine('Friendica\Render\FriendicaSmartyEngine');
}
/**
* Load the whole app instance
*/
- protected function load(DbaDefinition $dbaDefinition, ViewDefinition $viewDefinition)
- {
- if ($this->config->get('system', 'ini_max_execution_time') !== false) {
- set_time_limit((int)$this->config->get('system', 'ini_max_execution_time'));
+ private function load(
+ array $serverParams,
+ DbaDefinition $dbaDefinition,
+ ViewDefinition $viewDefinition,
+ Mode $mode,
+ IManageConfigValues $config,
+ Profiler $profiler,
+ EventDispatcherInterface $eventDispatcher,
+ AppHelper $appHelper,
+ AddonHelper $addonHelper
+ ): void {
+ if ($config->get('system', 'ini_max_execution_time') !== false) {
+ set_time_limit((int) $config->get('system', 'ini_max_execution_time'));
}
- if ($this->config->get('system', 'ini_pcre_backtrack_limit') !== false) {
- ini_set('pcre.backtrack_limit', (int)$this->config->get('system', 'ini_pcre_backtrack_limit'));
+ if ($config->get('system', 'ini_pcre_backtrack_limit') !== false) {
+ ini_set('pcre.backtrack_limit', (int) $config->get('system', 'ini_pcre_backtrack_limit'));
}
// Normally this constant is defined - but not if "pcntl" isn't installed
@@ -310,27 +367,20 @@ class App
// Ensure that all "strtotime" operations do run timezone independent
date_default_timezone_set('UTC');
- set_include_path(
- get_include_path() . PATH_SEPARATOR
- . $this->getBasePath() . DIRECTORY_SEPARATOR . 'include' . PATH_SEPARATOR
- . $this->getBasePath() . DIRECTORY_SEPARATOR . 'library' . PATH_SEPARATOR
- . $this->getBasePath());
+ $profiler->reset();
- $this->profiler->reset();
-
- if ($this->mode->has(App\Mode::DBAVAILABLE)) {
+ if ($mode->has(Mode::DBAVAILABLE)) {
Core\Hook::loadHooks();
- $loader = (new Config())->createConfigFileManager($this->getBasePath(), $_SERVER);
- Core\Hook::callAll('load_config', $loader);
+ $loader = (new Config())->createConfigFileManager($appHelper->getBasePath(), $addonHelper->getAddonPath(), $serverParams);
+
+ $eventDispatcher->dispatch(new ConfigLoadedEvent(ConfigLoadedEvent::CONFIG_LOADED, $loader));
// Hooks are now working, reload the whole definitions with hook enabled
$dbaDefinition->load(true);
$viewDefinition->load(true);
}
- $this->loadDefaultTimezone();
- // Register template engines
- Core\Renderer::registerTemplateEngine('Friendica\Render\FriendicaSmartyEngine');
+ $this->loadDefaultTimezone($config, $appHelper);
}
/**
@@ -340,166 +390,16 @@ class App
*
* @global string $default_timezone
*/
- private function loadDefaultTimezone()
+ private function loadDefaultTimezone(IManageConfigValues $config, AppHelper $appHelper)
{
- if ($this->config->get('system', 'default_timezone')) {
- $timezone = $this->config->get('system', 'default_timezone', 'UTC');
+ if ($config->get('system', 'default_timezone')) {
+ $timezone = $config->get('system', 'default_timezone', 'UTC');
} else {
global $default_timezone;
$timezone = $default_timezone ?? '' ?: 'UTC';
}
- $this->setTimeZone($timezone);
- }
-
- /**
- * Returns the current theme name. May be overridden by the mobile theme name.
- *
- * @return string Current theme name or empty string in installation phase
- * @throws Exception
- */
- public function getCurrentTheme(): string
- {
- if ($this->mode->isInstall()) {
- return '';
- }
-
- // Specific mobile theme override
- if (($this->mode->isMobile() || $this->mode->isTablet()) && $this->session->get('show-mobile', true)) {
- $user_mobile_theme = $this->getCurrentMobileTheme();
-
- // --- means same mobile theme as desktop
- if (!empty($user_mobile_theme) && $user_mobile_theme !== '---') {
- return $user_mobile_theme;
- }
- }
-
- if (!$this->currentTheme) {
- $this->computeCurrentTheme();
- }
-
- return $this->currentTheme;
- }
-
- /**
- * Returns the current mobile theme name.
- *
- * @return string Mobile theme name or empty string if installer
- * @throws Exception
- */
- public function getCurrentMobileTheme(): string
- {
- if ($this->mode->isInstall()) {
- return '';
- }
-
- if (is_null($this->currentMobileTheme)) {
- $this->computeCurrentMobileTheme();
- }
-
- return $this->currentMobileTheme;
- }
-
- /**
- * Setter for current theme name
- *
- * @param string $theme Name of current theme
- */
- public function setCurrentTheme(string $theme)
- {
- $this->currentTheme = $theme;
- }
-
- /**
- * Setter for current mobile theme name
- *
- * @param string $theme Name of current mobile theme
- */
- public function setCurrentMobileTheme(string $theme)
- {
- $this->currentMobileTheme = $theme;
- }
-
- /**
- * Computes the current theme name based on the node settings, the page owner settings and the user settings
- *
- * @throws Exception
- */
- private function computeCurrentTheme()
- {
- $system_theme = $this->config->get('system', 'theme');
- if (!$system_theme) {
- throw new Exception($this->l10n->t('No system theme config value set.'));
- }
-
- // Sane default
- $this->setCurrentTheme($system_theme);
-
- $page_theme = null;
- // Find the theme that belongs to the user whose stuff we are looking at
- if (!empty($this->profile_owner) && ($this->profile_owner != $this->session->getLocalUserId())) {
- // Allow folks to override user themes and always use their own on their own site.
- // This works only if the user is on the same server
- $user = $this->database->selectFirst('user', ['theme'], ['uid' => $this->profile_owner]);
- if ($this->database->isResult($user) && !$this->session->getLocalUserId()) {
- $page_theme = $user['theme'];
- }
- }
-
- $theme_name = $page_theme ?: $this->session->get('theme', $system_theme);
-
- $theme_name = Strings::sanitizeFilePathItem($theme_name);
- if ($theme_name
- && in_array($theme_name, Theme::getAllowedList())
- && (file_exists('view/theme/' . $theme_name . '/style.css')
- || file_exists('view/theme/' . $theme_name . '/style.php'))
- ) {
- $this->setCurrentTheme($theme_name);
- }
- }
-
- /**
- * Computes the current mobile theme name based on the node settings, the page owner settings and the user settings
- */
- private function computeCurrentMobileTheme()
- {
- $system_mobile_theme = $this->config->get('system', 'mobile-theme', '');
-
- // Sane default
- $this->setCurrentMobileTheme($system_mobile_theme);
-
- $page_mobile_theme = null;
- // Find the theme that belongs to the user whose stuff we are looking at
- if (!empty($this->profile_owner) && ($this->profile_owner != $this->session->getLocalUserId())) {
- // Allow folks to override user themes and always use their own on their own site.
- // This works only if the user is on the same server
- if (!$this->session->getLocalUserId()) {
- $page_mobile_theme = $this->pConfig->get($this->profile_owner, 'system', 'mobile-theme');
- }
- }
-
- $mobile_theme_name = $page_mobile_theme ?: $this->session->get('mobile-theme', $system_mobile_theme);
-
- $mobile_theme_name = Strings::sanitizeFilePathItem($mobile_theme_name);
- if ($mobile_theme_name == '---'
- ||
- in_array($mobile_theme_name, Theme::getAllowedList())
- && (file_exists('view/theme/' . $mobile_theme_name . '/style.css')
- || file_exists('view/theme/' . $mobile_theme_name . '/style.php'))
- ) {
- $this->setCurrentMobileTheme($mobile_theme_name);
- }
- }
-
- /**
- * Provide a sane default if nothing is chosen or the specified theme does not exist.
- *
- * @return string Current theme's stylesheet path
- * @throws Exception
- */
- public function getCurrentThemeStylesheetPath(): string
- {
- return Core\Theme::getStylesheetPath($this->getCurrentTheme());
+ $appHelper->setTimeZone($timezone);
}
/**
@@ -510,23 +410,34 @@ class App
*
* This probably should change to limit the size of this monster method.
*
- * @param App\Router $router
* @param IManagePersonalConfigValues $pconfig
- * @param Authentication $auth The Authentication backend of the node
- * @param App\Page $page The Friendica page printing container
+ * @param Page $page The Friendica page printing container
* @param ModuleHTTPException $httpException The possible HTTP Exception container
- * @param HTTPInputData $httpInput A library for processing PHP input streams
* @param float $start_time The start time of the overall script execution
- * @param array $server The $_SERVER array
*
* @throws HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
- public function runFrontend(App\Router $router, IManagePersonalConfigValues $pconfig, Authentication $auth, App\Page $page, Nav $nav, ModuleHTTPException $httpException, HTTPInputData $httpInput, float $start_time, array $server)
- {
- $requeststring = ($server['REQUEST_METHOD'] ?? '') . ' ' . ($server['REQUEST_URI'] ?? '') . ' ' . ($server['SERVER_PROTOCOL'] ?? '');
- $this->logger->debug('Request received', ['address' => $server['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $server['HTTP_REFERER'] ?? '', 'user-agent' => $server['HTTP_USER_AGENT'] ?? '']);
+ private function runFrontend(
+ EventDispatcherInterface $eventDispatcher,
+ IManagePersonalConfigValues $pconfig,
+ Page $page,
+ Nav $nav,
+ AddonHelper $addonHelper,
+ ModuleHTTPException $httpException,
+ float $start_time,
+ ServerRequestInterface $request
+ ) {
+ $this->mode->setExecutor(Mode::INDEX);
+
+ $httpInput = new HTTPInputData($request->getServerParams());
+ $serverVars = $request->getServerParams();
+ $queryVars = $request->getQueryParams();
+
+ $requeststring = ($serverVars['REQUEST_METHOD'] ?? '') . ' ' . ($serverVars['REQUEST_URI'] ?? '') . ' ' . ($serverVars['SERVER_PROTOCOL'] ?? '');
+ $this->logger->debug('Request received', ['address' => $serverVars['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $serverVars['HTTP_REFERER'] ?? '', 'user-agent' => $serverVars['HTTP_USER_AGENT'] ?? '']);
$request_start = microtime(true);
+ $request = $_REQUEST;
$this->profiler->set($start_time, 'start');
$this->profiler->set(microtime(true), 'classinit');
@@ -536,54 +447,55 @@ class App
try {
// Missing DB connection: ERROR
- if ($this->mode->has(App\Mode::LOCALCONFIGPRESENT) && !$this->mode->has(App\Mode::DBAVAILABLE)) {
+ if ($this->mode->has(Mode::LOCALCONFIGPRESENT) && !$this->mode->has(Mode::DBAVAILABLE)) {
throw new HTTPException\InternalServerErrorException($this->l10n->t('Apologies but the website is unavailable at the moment.'));
}
if (!$this->mode->isInstall()) {
// Force SSL redirection
if ($this->config->get('system', 'force_ssl') &&
- (empty($server['HTTPS']) || $server['HTTPS'] === 'off') &&
- (empty($server['HTTP_X_FORWARDED_PROTO']) || $server['HTTP_X_FORWARDED_PROTO'] === 'http') &&
- !empty($server['REQUEST_METHOD']) &&
- $server['REQUEST_METHOD'] === 'GET') {
+ (empty($serverVars['HTTPS']) || $serverVars['HTTPS'] === 'off') &&
+ (empty($serverVars['HTTP_X_FORWARDED_PROTO']) || $serverVars['HTTP_X_FORWARDED_PROTO'] === 'http') &&
+ !empty($serverVars['REQUEST_METHOD']) &&
+ $serverVars['REQUEST_METHOD'] === 'GET') {
System::externalRedirect($this->baseURL . '/' . $this->args->getQueryString());
}
- Core\Hook::callAll('init_1');
+
+ $eventDispatcher->dispatch(new Event(Event::INIT));
}
- DID::routeRequest($this->args->getCommand(), $server);
+ DID::routeRequest($this->args->getCommand(), $serverVars);
if ($this->mode->isNormal() && !$this->mode->isBackend()) {
- $requester = HTTPSignature::getSigner('', $server);
+ $requester = HTTPSignature::getSigner('', $serverVars);
if (!empty($requester)) {
OpenWebAuth::addVisitorCookieForHandle($requester);
}
}
// ZRL
- if (!empty($_GET['zrl']) && $this->mode->isNormal() && !$this->mode->isBackend() && !$this->session->getLocalUserId()) {
+ if (!empty($queryVars['zrl']) && $this->mode->isNormal() && !$this->mode->isBackend() && !$this->session->getLocalUserId()) {
// Only continue when the given profile link seems valid.
// Valid profile links contain a path with "/profile/" and no query parameters
- if ((parse_url($_GET['zrl'], PHP_URL_QUERY) == '') &&
- strpos(parse_url($_GET['zrl'], PHP_URL_PATH) ?? '', '/profile/') !== false) {
- $this->auth->setUnauthenticatedVisitor($_GET['zrl']);
+ if ((parse_url($queryVars['zrl'], PHP_URL_QUERY) == '') &&
+ strpos(parse_url($queryVars['zrl'], PHP_URL_PATH) ?? '', '/profile/') !== false) {
+ $this->auth->setUnauthenticatedVisitor($queryVars['zrl']);
OpenWebAuth::zrlInit();
} else {
// Someone came with an invalid parameter, maybe as a DDoS attempt
// We simply stop processing here
- $this->logger->debug('Invalid ZRL parameter.', ['zrl' => $_GET['zrl']]);
+ $this->logger->debug('Invalid ZRL parameter.', ['zrl' => $queryVars['zrl']]);
throw new HTTPException\ForbiddenException();
}
}
- if (!empty($_GET['owt']) && $this->mode->isNormal()) {
- $token = $_GET['owt'];
+ if (!empty($queryVars['owt']) && $this->mode->isNormal()) {
+ $token = $queryVars['owt'];
OpenWebAuth::init($token);
}
if (!$this->mode->isBackend()) {
- $auth->withSession($this);
+ $this->auth->withSession();
}
if ($this->session->isUnauthenticated()) {
@@ -600,12 +512,12 @@ class App
// but we need "view" module for stylesheet
if ($this->mode->isInstall() && $moduleName !== 'install') {
$this->baseURL->redirect('install');
- } else {
- Core\Update::check($this->getBasePath(), false);
- Core\Addon::loadAddons();
- Core\Hook::loadHooks();
}
+ Core\Update::check($this->appHelper->getBasePath(), false);
+ $addonHelper->loadAddons();
+ Core\Hook::loadHooks();
+
// Compatibility with Hubzilla
if ($moduleName == 'rpost') {
$this->baseURL->redirect('compose');
@@ -648,12 +560,12 @@ class App
$page['page_title'] = $moduleName;
// The "view" module is required to show the theme CSS
- if (!$this->mode->isInstall() && !$this->mode->has(App\Mode::MAINTENANCEDISABLED) && $moduleName !== 'view') {
- $module = $router->getModule(Maintenance::class);
+ if (!$this->mode->isInstall() && !$this->mode->has(Mode::MAINTENANCEDISABLED) && $moduleName !== 'view') {
+ $module = $this->createModuleInstance(Maintenance::class);
} else {
// determine the module class and save it to the module instance
// @todo there's an implicit dependency due SESSION::start(), so it has to be called here (yet)
- $module = $router->getModule();
+ $module = $this->createModuleInstance(null);
}
// Display can change depending on the requested language, so it shouldn't be cached whole
@@ -661,44 +573,60 @@ class App
// Processes data from GET requests
$httpinput = $httpInput->process();
- $input = array_merge($httpinput['variables'], $httpinput['files'], $request ?? $_REQUEST);
+
+ if (!is_array($httpinput['variables'])) {
+ $httpinput['variables'] = [];
+ }
+ if (!is_array($httpinput['files'])) {
+ $httpinput['files'] = [];
+ }
+
+ $input = array_merge($httpinput['variables'], $httpinput['files'], $request);
// Let the module run its internal process (init, get, post, ...)
$timestamp = microtime(true);
- $response = $module->run($httpException, $input);
+ $response = $module->run($httpException, $input);
$this->profiler->set(microtime(true) - $timestamp, 'content');
// Wrapping HTML responses in the theme template
if ($response->getHeaderLine(ICanCreateResponses::X_HEADER) === ICanCreateResponses::TYPE_HTML) {
- $response = $page->run($this, $this->session, $this->baseURL, $this->args, $this->mode, $response, $this->l10n, $this->profiler, $this->config, $pconfig, $nav, $this->session->getLocalUserId());
+ $response = $page->run($this->appHelper, $this->session, $this->baseURL, $this->args, $this->mode, $response, $this->l10n, $this->profiler, $this->config, $pconfig, $nav, $this->session->getLocalUserId());
}
- $this->logger->debug('Request processed sucessfully', ['response' => $response->getStatusCode(), 'address' => $server['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $server['HTTP_REFERER'] ?? '', 'user-agent' => $server['HTTP_USER_AGENT'] ?? '', 'duration' => number_format(microtime(true) - $request_start, 3)]);
- $this->logSlowCalls(microtime(true) - $request_start, $response->getStatusCode(), $requeststring, $server['HTTP_USER_AGENT'] ?? '');
+ $this->logger->debug('Request processed sucessfully', ['response' => $response->getStatusCode(), 'address' => $serverVars['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $serverVars['HTTP_REFERER'] ?? '', 'user-agent' => $serverVars['HTTP_USER_AGENT'] ?? '', 'duration' => number_format(microtime(true) - $request_start, 3)]);
+ $this->logSlowCalls(microtime(true) - $request_start, $response->getStatusCode(), $requeststring, $serverVars['HTTP_USER_AGENT'] ?? '');
System::echoResponse($response);
} catch (HTTPException $e) {
- $this->logger->debug('Request processed with exception', ['response' => $e->getCode(), 'address' => $server['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $server['HTTP_REFERER'] ?? '', 'user-agent' => $server['HTTP_USER_AGENT'] ?? '', 'duration' => number_format(microtime(true) - $request_start, 3)]);
- $this->logSlowCalls(microtime(true) - $request_start, $e->getCode(), $requeststring, $server['HTTP_USER_AGENT'] ?? '');
+ $this->logger->debug('Request processed with exception', ['response' => $e->getCode(), 'address' => $serverVars['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $serverVars['HTTP_REFERER'] ?? '', 'user-agent' => $serverVars['HTTP_USER_AGENT'] ?? '', 'duration' => number_format(microtime(true) - $request_start, 3)]);
+ $this->logSlowCalls(microtime(true) - $request_start, $e->getCode(), $requeststring, $serverVars['HTTP_USER_AGENT'] ?? '');
$httpException->rawContent($e);
}
$page->logRuntime($this->config, 'runFrontend');
}
- /**
- * Automatically redirects to relative or absolute URL
- * Should only be used if it isn't clear if the URL is either internal or external
- *
- * @param string $toUrl The target URL
- *
- * @throws HTTPException\InternalServerErrorException
- */
- public function redirect(string $toUrl)
+ private function createModuleInstance(?string $moduleClass = null): ICanHandleRequests
{
- if (!empty(parse_url($toUrl, PHP_URL_SCHEME))) {
- Core\System::externalRedirect($toUrl);
- } else {
- $this->baseURL->redirect($toUrl);
+ /** @var Router $router */
+ $router = $this->container->create(Router::class);
+
+ $moduleClass = $moduleClass ?? $router->getModuleClass();
+ $parameters = $router->getParameters();
+
+ $dice_profiler_threshold = $this->config->get('system', 'dice_profiler_threshold', 0);
+
+ $stamp = microtime(true);
+
+ /** @var ICanHandleRequests $module */
+ $module = $this->container->create($moduleClass, $parameters);
+
+ if ($dice_profiler_threshold > 0) {
+ $dur = floatval(microtime(true) - $stamp);
+ if ($dur >= $dice_profiler_threshold) {
+ $this->logger->notice('Dice module creation lasts too long.', ['duration' => round($dur, 3), 'module' => $moduleClass, 'parameters' => $parameters]);
+ }
}
+
+ return $module;
}
/**
diff --git a/src/App/BaseURL.php b/src/App/BaseURL.php
index 953b13c791..7aaafdc8bf 100644
--- a/src/App/BaseURL.php
+++ b/src/App/BaseURL.php
@@ -17,8 +17,7 @@ use Psr\Http\Message\UriInterface;
use Psr\Log\LoggerInterface;
/**
- * A class which checks and contains the basic
- * environment for the BaseURL (url, urlpath, ssl_policy, hostname, scheme)
+ * A class which checks and contains the basic environment for the BaseURL (url)
*/
class BaseURL extends Uri implements UriInterface
{
@@ -43,8 +42,7 @@ class BaseURL extends Uri implements UriInterface
/* Relative script path to the web server root
* Not all of those $_SERVER properties can be present, so we do by inverse priority order
*/
- $relativeScriptPath =
- ($server['REDIRECT_URL'] ?? '') ?:
+ $relativeScriptPath = ($server['REDIRECT_URL'] ?? '') ?:
($server['REDIRECT_URI'] ?? '') ?:
($server['REDIRECT_SCRIPT_URL'] ?? '') ?:
($server['SCRIPT_URL'] ?? '') ?:
@@ -103,6 +101,8 @@ class BaseURL extends Uri implements UriInterface
* @throws HTTPException\TemporaryRedirectException
*
* @throws HTTPException\InternalServerErrorException In Case the given URL is not relative to the Friendica node
+ *
+ * @return never
*/
public function redirect(string $toUrl = '', bool $ssl = false)
{
diff --git a/src/App/Mode.php b/src/App/Mode.php
index 070d67ee4f..ebf9f46163 100644
--- a/src/App/Mode.php
+++ b/src/App/Mode.php
@@ -210,7 +210,7 @@ class Mode
public function isInstall(): bool
{
return !$this->has(Mode::LOCALCONFIGPRESENT) ||
- !$this->has(MODE::DBAVAILABLE);
+ !$this->has(Mode::DBAVAILABLE);
}
/**
diff --git a/src/App/Page.php b/src/App/Page.php
index eb7772f34f..a7337485ec 100644
--- a/src/App/Page.php
+++ b/src/App/Page.php
@@ -11,23 +11,24 @@ use ArrayAccess;
use DOMDocument;
use DOMXPath;
use Friendica\App;
+use Friendica\AppHelper;
use Friendica\Content\Nav;
use Friendica\Core\Config\Capability\IManageConfigValues;
-use Friendica\Core\Hook;
use Friendica\Core\L10n;
-use Friendica\Core\Logger;
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
use Friendica\Core\Renderer;
use Friendica\Core\Session\Model\UserSession;
use Friendica\Core\System;
use Friendica\Core\Theme;
-use Friendica\Module\Response;
+use Friendica\DI;
+use Friendica\Event\HtmlFilterEvent;
use Friendica\Network\HTTPException;
use Friendica\Util\Images;
use Friendica\Util\Network;
use Friendica\Util\Profiler;
use Friendica\Util\Strings;
use GuzzleHttp\Psr7\Utils;
+use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Http\Message\ResponseInterface;
/**
@@ -70,6 +71,8 @@ class Page implements ArrayAccess
*/
private $basePath;
+ private EventDispatcherInterface $eventDispatcher;
+
private $timestamp = 0;
private $method = '';
private $module = '';
@@ -78,10 +81,11 @@ class Page implements ArrayAccess
/**
* @param string $basepath The Page basepath
*/
- public function __construct(string $basepath)
+ public function __construct(string $basepath, EventDispatcherInterface $eventDispatcher)
{
- $this->timestamp = microtime(true);
- $this->basePath = $basepath;
+ $this->timestamp = microtime(true);
+ $this->basePath = $basepath;
+ $this->eventDispatcher = $eventDispatcher;
}
public function setLogging(string $method, string $module, string $command)
@@ -102,7 +106,7 @@ class Page implements ArrayAccess
$load = number_format(System::currentLoad(), 2);
$runtime = number_format(microtime(true) - $this->timestamp, 3);
if ($runtime > $config->get('system', 'runtime_loglimit')) {
- Logger::debug('Runtime', ['method' => $this->method, 'module' => $this->module, 'runtime' => $runtime, 'load' => $load, 'origin' => $origin, 'signature' => $signature, 'request' => $_SERVER['REQUEST_URI'] ?? '']);
+ DI::logger()->debug('Runtime', ['method' => $this->method, 'module' => $this->module, 'runtime' => $runtime, 'load' => $load, 'origin' => $origin, 'signature' => $signature, 'request' => $_SERVER['REQUEST_URI'] ?? '']);
}
}
@@ -176,40 +180,34 @@ class Page implements ArrayAccess
* - Infinite scroll data
* - head.tpl template
*
- * @param App $app The Friendica App instance
- * @param Arguments $args The Friendica App Arguments
- * @param L10n $l10n The l10n language instance
- * @param IManageConfigValues $config The Friendica configuration
- * @param IManagePersonalConfigValues $pConfig The Friendica personal configuration (for user)
- * @param int $localUID The local user id
+ * @param Arguments $args The Friendica App Arguments
+ * @param L10n $l10n The l10n language instance
+ * @param IManageConfigValues $config The Friendica configuration
+ * @param IManagePersonalConfigValues $pConfig The Friendica personal configuration (for user)
+ * @param int $localUID The local user id
*
* @throws HTTPException\InternalServerErrorException
*/
- private function initHead(App $app, Arguments $args, L10n $l10n, IManageConfigValues $config, IManagePersonalConfigValues $pConfig, int $localUID)
- {
- $interval = ($localUID ? $pConfig->get($localUID, 'system', 'update_interval') : 40000);
-
- // If the update is 'deactivated' set it to the highest integer number (~24 days)
- if ($interval < 0) {
- $interval = 2147483647;
- }
-
- if ($interval < 10000) {
- $interval = 40000;
- }
-
+ private function initHead(
+ AppHelper $appHelper,
+ Arguments $args,
+ L10n $l10n,
+ IManageConfigValues $config,
+ IManagePersonalConfigValues $pConfig,
+ int $localUID
+ ) {
// Default title: current module called
if (empty($this->page['title']) && $args->getModuleName()) {
- $this->page['title'] = ucfirst($args->getModuleName());
+ $this->page['title'] = $l10n->t(ucfirst($args->getModuleName()));
}
- // Prepend the sitename to the page title
- $this->page['title'] = $config->get('config', 'sitename', '') . (!empty($this->page['title']) ? ' | ' . $this->page['title'] : '');
+ // Append the sitename to the page title
+ $this->page['title'] = (!empty($this->page['title']) ? $this->page['title'] . ' | ' : '') . $config->get('config', 'sitename', '');
if (!empty(Renderer::$theme['stylesheet'])) {
$stylesheet = Renderer::$theme['stylesheet'];
} else {
- $stylesheet = $app->getCurrentThemeStylesheetPath();
+ $stylesheet = $appHelper->getCurrentThemeStylesheetPath();
}
$this->registerStylesheet($stylesheet);
@@ -224,7 +222,9 @@ class Page implements ArrayAccess
$touch_icon = 'images/friendica-192.png';
}
- Hook::callAll('head', $this->page['htmlhead']);
+ $this->page['htmlhead'] = $this->eventDispatcher->dispatch(
+ new HtmlFilterEvent(HtmlFilterEvent::HEAD, $this->page['htmlhead'])
+ )->getHtml();
$tpl = Renderer::getMarkupTemplate('head.tpl');
/* put the head template at the beginning of page['htmlhead']
@@ -261,13 +261,13 @@ class Page implements ArrayAccess
'dictMaxFilesExceeded' => $l10n->t("You can't upload any more files."),
],
- '$local_user' => $localUID,
- '$generator' => 'Friendica' . ' ' . App::VERSION,
- '$update_interval' => $interval,
- '$shortcut_icon' => $shortcut_icon,
- '$touch_icon' => $touch_icon,
- '$block_public' => intval($config->get('system', 'block_public')),
- '$stylesheets' => $this->stylesheets,
+ '$local_user' => $localUID,
+ '$generator' => 'Friendica' . ' ' . App::VERSION,
+ '$update_content' => (int)$pConfig->get($localUID, 'system', 'update_content'),
+ '$shortcut_icon' => $shortcut_icon,
+ '$touch_icon' => $touch_icon,
+ '$block_public' => intval($config->get('system', 'block_public')),
+ '$stylesheets' => $this->stylesheets,
// Dropzone
'$max_imagesize' => round(Images::getMaxUploadBytes() / 1000000, 0),
@@ -313,7 +313,6 @@ class Page implements ArrayAccess
* - Registered footer scripts (through App->registerFooterScript())
* - footer.tpl template
*
- * @param App $app The Friendica App instance
* @param Mode $mode The Friendica runtime mode
* @param L10n $l10n The l10n instance
*
@@ -347,11 +346,14 @@ class Page implements ArrayAccess
]);
}
- Hook::callAll('footer', $this->page['footer']);
+ $this->page['footer'] = $this->eventDispatcher->dispatch(
+ new HtmlFilterEvent(HtmlFilterEvent::FOOTER, $this->page['footer'])
+ )->getHtml();
$tpl = Renderer::getMarkupTemplate('footer.tpl');
$this->page['footer'] = Renderer::replaceMacros($tpl, [
'$footerScripts' => array_unique($this->footerScripts),
+ '$close' => $l10n->t('Close'),
]) . $this->page['footer'];
}
@@ -371,7 +373,9 @@ class Page implements ArrayAccess
{
// initialise content region
if ($mode->isNormal()) {
- Hook::callAll('page_content_top', $this->page['content']);
+ $this->page['content'] = $this->eventDispatcher->dispatch(
+ new HtmlFilterEvent(HtmlFilterEvent::PAGE_CONTENT_TOP, $this->page['content'])
+ )->getHtml();
}
$this->page['content'] .= (string)$response->getBody();
@@ -399,23 +403,34 @@ class Page implements ArrayAccess
/**
* Executes the creation of the current page and prints it to the screen
*
- * @param App $app The Friendica App
- * @param BaseURL $baseURL The Friendica Base URL
- * @param Arguments $args The Friendica App arguments
- * @param Mode $mode The current node mode
- * @param ResponseInterface $response The Response of the module class, including type, content & headers
- * @param L10n $l10n The l10n language class
+ * @param BaseURL $baseURL The Friendica Base URL
+ * @param Arguments $args The Friendica App arguments
+ * @param Mode $mode The current node mode
+ * @param ResponseInterface $response The Response of the module class, including type, content & headers
+ * @param L10n $l10n The l10n language class
* @param Profiler $profiler
- * @param IManageConfigValues $config The Configuration of this node
- * @param IManagePersonalConfigValues $pconfig The personal/user configuration
+ * @param IManageConfigValues $config The Configuration of this node
+ * @param IManagePersonalConfigValues $pconfig The personal/user configuration
* @param Nav $nav
* @param int $localUID
* @throws HTTPException\MethodNotAllowedException
* @throws HTTPException\InternalServerErrorException
* @throws HTTPException\ServiceUnavailableException
*/
- public function run(App $app, UserSession $session, BaseURL $baseURL, Arguments $args, Mode $mode, ResponseInterface $response, L10n $l10n, Profiler $profiler, IManageConfigValues $config, IManagePersonalConfigValues $pconfig, Nav $nav, int $localUID)
- {
+ public function run(
+ AppHelper $appHelper,
+ UserSession $session,
+ BaseURL $baseURL,
+ Arguments $args,
+ Mode $mode,
+ ResponseInterface $response,
+ L10n $l10n,
+ Profiler $profiler,
+ IManageConfigValues $config,
+ IManagePersonalConfigValues $pconfig,
+ Nav $nav,
+ int $localUID
+ ) {
$moduleName = $args->getModuleName();
$this->command = $moduleName;
@@ -430,7 +445,7 @@ class Page implements ArrayAccess
$this->initContent($response, $mode);
// Load current theme info after module has been initialized as theme could have been set in module
- $currentTheme = $app->getCurrentTheme();
+ $currentTheme = $appHelper->getCurrentTheme();
$theme_info_file = 'view/theme/' . $currentTheme . '/theme.php';
if (file_exists($theme_info_file)) {
require_once $theme_info_file;
@@ -438,7 +453,7 @@ class Page implements ArrayAccess
if (function_exists(str_replace('-', '_', $currentTheme) . '_init')) {
$func = str_replace('-', '_', $currentTheme) . '_init';
- $func($app);
+ $func($appHelper);
}
/* Create the page head after setting the language
@@ -448,7 +463,7 @@ class Page implements ArrayAccess
* all the module functions have executed so that all
* theme choices made by the modules can take effect.
*/
- $this->initHead($app, $args, $l10n, $config, $pconfig, $localUID);
+ $this->initHead($appHelper, $args, $l10n, $config, $pconfig, $localUID);
/* Build the page ending -- this is stuff that goes right before
* the closing |